В процессе настройки ограничений прав доступа (RLS) в 1С можно столкнуться с нетиповой, но распространенной задачей. Представим ситуацию: есть документ, например, "Заявка", в котором присутствуют два реквизита — Организация и ЦФО (Центр финансовой ответственности). Архитектурно оба этих реквизита ссылаются на один и тот же справочник Организации. Необходимо настроить RLS таким образом, чтобы пользователь, имеющий доступ ко всем организациям, видел заявки только по конкретному, разрешенному ему ЦФО.
Стандартный механизм ограничения по организациям здесь не поможет. Если разрешить пользователю доступ к организации, он увидит все документы по ней, независимо от того, какой ЦФО указан в реквизите. Попытка же создать второй, отдельный вид доступа для ЦФО, наталкивается на ограничение платформы. Давайте разберемся, в чем именно заключается проблема и какие существуют способы ее решения.
При попытке создать новый вид доступа для разграничения по ЦФО, разработчик сталкивается с фундаментальным ограничением подсистемы RLS. Нельзя создать два разных вида доступа, которые ссылаются на один и тот же тип данных.
Например, при попытке программно добавить новый вид доступа в модуле УправлениеДоступомПереопределяемый, мы получим ошибку. Код может выглядеть так:
// Стандартный вид доступа по Организациям уже существует и использует
// Тип("СправочникСсылка.Организации")
// Попытка создать второй вид доступа для ЦФО на тот же тип
ВидДоступа = ВидыДоступа.Добавить();
ВидДоступа.Имя = "ДоступПоЦФО";
ВидДоступа.Представление = НСтр("ru = 'Доступ по ЦФО'");
ВидДоступа.ТипЗначений = Тип("СправочникСсылка.Организации"); // <-- Эта строка вызовет ошибку, так как тип уже используется
Из-за этого ограничения простой путь через создание второго вида доступа и использование стандартного шаблона #ПоЗначениям оказывается нерабочим. Применение шаблона с двумя одинаковыми видами доступа для разных реквизитов, как в примере ниже, приведет к некорректной работе: система либо проигнорирует второе условие, либо, что чаще, пользователь увидит в поле ЦФО значение <Объект не найден>, так как RLS "вырежет" доступ к элементу справочника.
#ПоЗначениям("Документ.Заявка", "", "",
"Организации", "Организация", // Условие по первому реквизиту
"Организации","ЦФО" // Попытка применить то же ограничение ко второму реквизиту
)
Чтобы обойти это ограничение и реализовать нужную нам логику, необходимо использовать более гибкие и мощные подходы.
Наиболее правильным с точки зрения архитектуры и гибкости является вынесение логики прав доступа к ЦФО в отдельный объект — регистр сведений. Этот подход позволяет администраторам системы легко управлять правами, не прибегая к изменению конфигурации, а также обеспечивает высокую производительность.
Шаг 1: Создание регистра сведений
Создадим новый регистр сведений, например, ПраваДоступаПоЦФО, со следующей структурой:
Пользователь (тип СправочникСсылка.Пользователи или СправочникСсылка.ГруппыДоступа, в зависимости от вашей модели прав).ЦФО (тип СправочникСсылка.Организации) — здесь мы будем хранить ссылку на конкретный ЦФО, к которому дается доступ.ДоступРазрешен, хотя для простых сценариев достаточно самого факта наличия записи в регистре.Шаг 2: Заполнение прав доступа
Теперь администратор или ответственное лицо может через стандартный интерфейс заполнять этот регистр, указывая, какому пользователю или какой группе доступа какой ЦФО разрешен. Это делает систему управления правами прозрачной и удобной.
Шаг 3: Модификация шаблона RLS для документа "Заявка"
Самая важная часть — это написание текста ограничения для роли, которая имеет доступ к заявкам. Вместо стандартных шаблонов мы напишем свой, используя вложенный запрос к нашему новому регистру.
Откроем нужную роль и в шаблоне ограничения доступа на чтение для документа Заявка напишем следующий код:
#Таблица_Документа(T)
#Если РольДоступна("ИмяВашейРоли") Тогда
// Часть 1: Стандартное ограничение по Организации
(T.Организация В (
ВЫБРАТЬ РазрешенныеЗначения.ЗначениеДоступа
ИЗ Справочник.КлючиДоступа_Организации КАК РазрешенныеЗначения
ГДЕ РазрешенныеЗначения.КлючДоступа В (&КлючиДоступа_Организации)
))
И // Условия объединяются через "И", т.к. должны выполняться оба
// Часть 2: Наше кастомное ограничение по ЦФО
(T.ЦФО В (
ВЫБРАТЬ
ПраваДоступаПоЦФО.ЦФО
ИЗ
РегистрСведений.ПраваДоступаПоЦФО КАК ПраваДоступаПоЦФО
ГДЕ
ПраваДоступаПоЦФО.Пользователь = &ТекущийПользователь
))
#КонецЕсли
Проанализируем этот код:
#Таблица_Документа(T) — это директива, которая позволяет нам обращаться к полям основной таблицы (в нашем случае, документа "Заявка") через псевдоним T.T.Организация в список разрешенных для пользователя организаций.&ТекущийПользователь — стандартный параметр сеанса RLS) из нашего регистра ПраваДоступаПоЦФО.И, что означает: пользователь увидит документ только в том случае, если у него есть права и на организацию, И на ЦФО, указанные в документе.Этот подход является наиболее предпочтительным, так как он не требует сложных доработок конфигурации (кроме добавления одного регистра), гибок в настройке и легко читаем.
Еще один элегантный способ решения — использование параметров сеанса. Суть в том, чтобы при старте сеанса пользователя вычислить все доступные ему ЦФО и сохранить их в специальном параметре сеанса. Затем этот параметр можно использовать в шаблоне RLS.
Шаг 1: Создание параметра сеанса
В дереве конфигурации создадим новый параметр сеанса, например, СписокДоступныхЦФО. Тип значения установим ХранилищеЗначения, чтобы хранить в нем массив или список значений.
Шаг 2: Заполнение параметра при старте системы
В модуле сеанса, в процедуре УстановкаПараметровСеанса, необходимо добавить код, который будет заполнять наш новый параметр. Данные для заполнения можно брать из того же регистра сведений, что и в первом решении.
// В модуле сеанса, процедура УстановкаПараметровСеанса()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПраваДоступаПоЦФО.ЦФО
|ИЗ
| РегистрСведений.ПраваДоступаПоЦФО КАК ПраваДоступаПоЦФО
|ГДЕ
| ПраваДоступаПоЦФО.Пользователь = &Пользователь";
Запрос.УстановитьПараметр("Пользователь", ПараметрыСеанса.ТекущийПользователь);
Результат = Запрос.Выполнить().Выгрузить();
СписокЦФО = Результат.ВыгрузитьКолонку("ЦФО");
// Сохраняем массив ссылок в параметр сеанса
ПараметрыСеанса.СписокДоступныхЦФО = Новый ХранилищеЗначения(СписокЦФО);
Шаг 3: Настройка шаблона RLS
Теперь текст ограничения в роли можно сделать еще проще и производительнее, так как не будет вложенного запроса на каждую строку.
#Таблица_Документа(T)
// Ограничение по Организации (стандартное)
T.Организация В (&СписокДоступныхОрганизаций) // Условное название параметра
И // Объединяем условия
// Ограничение по ЦФО через наш параметр сеанса
T.ЦФО В (&СписокДоступныхЦФО)
Важно: Прямое использование параметра сеанса в условии В (&ИмяПараметра) возможно, если он имеет тип ФиксированныйМассив. Если тип ХранилищеЗначения, может потребоваться небольшая доработка в модуле УправлениеДоступомПереопределяемый для корректной подстановки значения. Однако общая концепция остается той же.
Этот метод хорош тем, что права рассчитываются один раз при входе в систему, что может дать выигрыш в производительности при работе с большими списками документов.
Проблема настройки RLS для двух реквизитов, ссылающихся на один справочник, является классическим примером, где стандартных механизмов недостаточно. Ключ к решению — отказ от попытки создать второй "вид доступа" и использование более гибких инструментов.
Рекомендуемый подход — создание отдельного регистра сведений для хранения прав доступа к ЦФО и использование вложенного запроса в шаблоне RLS. Этот метод обеспечивает:
Использование параметров сеанса является отличной альтернативой, особенно в высоконагруженных системах, так как позволяет оптимизировать выполнение запросов RLS. Оба способа позволяют эффективно решить поставленную задачу и обойти ограничения платформы 1С.