Как программно установить отбор в списке документов по нескольким подразделениям в ЗУП 3.1

Программист 1С v8.3 (Управляемые формы) 1С:Зарплата и Управление Персоналом
← На главную

В крупных информационных базах 1С:Зарплата и Управление Персоналом (ЗУП 3.1) стандартный механизм ограничения доступа на уровне записей (RLS) может приводить к существенному снижению производительности. Особенно это заметно, когда количество пользователей превышает сотню, а правила распределения доступа становятся нетипичными — например, когда доступ к документам подразделения должен иметь сотрудник, который в нем официально не числится. В таких случаях мы сталкиваемся с необходимостью реализовать программный фильтр через расширение конфигурации.

Рассмотрим ситуацию, когда перечень разрешенных подразделений для каждого пользователя загружается из внешней системы (например, Oracle) и должен динамически применяться к формам списков документов. Проанализируем наиболее эффективные способы решения этой задачи, обеспечивающие баланс между скоростью работы и удобством пользователя.

Метод 1. Использование фиксированных настроек динамического списка

Самый распространенный и архитектурно правильный способ ограничить видимость данных в форме списка — это программное изменение ФиксированныеНастройки динамического списка. Преимущество этого метода в том, что пользователь не видит этот отбор в стандартном окне настроек и не может его самостоятельно отключить или изменить.

Для реализации этого метода в расширении необходимо перехватить процедуру ПриСозданииНаСервере формы списка. Разберем пример кода, который накладывает фильтр по списку ссылок на подразделения:


&После("ПриСозданииНаСервере")
Процедура Расш1_ПриСозданииНаСервереПосле(Отказ, СтандартнаяОбработка)
    
    // Получаем массив доступных подразделений (например, из параметров сеанса)
    МассивПодразделений = ПараметрыСеанса.Расш1_ДоступныеПодразделения;
    
    // Если список пуст, возможно, стоит запретить просмотр вообще или вывести предупреждение
    Если МассивПодразделений.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;

    // Работаем с фиксированными настройками динамического списка "Список"
    ГруппаОтбора = Список.КомпоновщикНастроек.ФиксированныеНастройки.Отбор.Элементы;
    
    // Проверяем, нет ли уже такого отбора, чтобы не дублировать
    ПолеПодразделение = Новый ПолеКомпоновкиДанных("Подразделение");
    
    ЭлементОтбора = ГруппаОтбора.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
    ЭлементОтбора.ЛевоеЗначение  = ПолеПодразделение;
    ЭлементОтбора.ВидСравнения   = ВидСравненияКомпоновкиДанных.ВСписке;
    ЭлементОтбора.ПравоеЗначение = МассивПодразделений;
    ЭлементОтбора.Использование  = Истина;
    ЭлементОтбора.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный;

КонецПроцедуры

Важно отметить, что использование ВидСравненияКомпоновкиДанных.ВСписке позволяет эффективно обрабатывать сразу несколько значений. Если подразделение в документах может называться по-разному (например, ПодразделениеОрганизации), убедитесь, что вы используете правильное имя поля компоновки.

Метод 2. Динамическая подмена текста запроса

В некоторых случаях, особенно в сложных списках, где данные собираются из нескольких таблиц, обычного отбора может быть недостаточно для оптимальной работы СУБД. Тогда мы можем принудительно изменить сам SQL-запрос, который генерирует платформа. Проанализируем, как это сделать:

Мы можем вставить условие ГДЕ непосредственно в текст запроса динамического списка. Это позволяет базе данных подготовить более оптимальный план выполнения запроса.


&После("ПриСозданииНаСервере")
Процедура Расш1_ИзменитьЗапросСписка(Отказ, СтандартнаяОбработка)
    
    МассивПодразделений = ПараметрыСеанса.Расш1_ДоступныеПодразделения;
    
    // Модифицируем текст запроса
    ТекстЗапроса = Список.ТекстЗапроса;
    ДополнительноеУсловие = "ГДЕ ОсновнаяТаблица.Подразделение В (&ДоступныеПодразделения)";
    
    // Простая замена (в реальной задаче стоит использовать СтрЗаменить или анализ структуры запроса)
    Список.ТекстЗапроса = ТекстЗапроса + " " + ДополнительноеУсловие;
    
    // Устанавливаем параметр запроса
    Список.Параметры.УстановитьЗначениеПараметра("ДоступныеПодразделения", МассивПодразделений);
    
КонецПроцедуры

Важный нюанс: при использовании этого метода нужно быть осторожным с иерархией. Если доступ дается на головное подразделение, то оператор В не подтянет документы дочерних отделов. В этом случае лучше заранее программно вычислить все нижестоящие подразделения и передать в параметр "плоский" массив всех разрешенных ссылок.

Метод 3. Кэширование данных через параметры сеанса

Так как информация о доступных подразделениях поступает из внешней базы данных Oracle, выполнение запроса к внешней базе при каждом открытии формы документа недопустимо — это вызовет огромные задержки. Выясним, как оптимизировать этот процесс.

Рекомендуется создать в расширении ПараметрСеанса с типом ФиксированныйМассив. Заполнение этого параметра следует выполнять один раз при старте сеанса (в модуле управляемого приложения или через обработчик УстановкаПараметровСеанса в модуле сеанса).

  1. В модуле сеанса расширения перехватываем установку параметров.
  2. Выполняем запрос к Oracle (используя COM-соединение или HTTP-сервис).
  3. Помещаем полученные ссылки на подразделения 1С в массив.
  4. Записываем массив в параметр сеанса.

Таким образом, при открытии любой формы списка документов программист просто считывает готовые данные из оперативной памяти сервера 1С, что происходит мгновенно.

Метод 4. Ограничение данных в отчетах

Программный отбор в формах списков решает задачу удобства интерфейса, но не защищает данные. Пользователь всё еще может увидеть начисления по "чужим" подразделениям через стандартные отчеты, такие как "Полный свод начислений" или "Расчетная ведомость". Чтобы сделать ограничение комплексным, необходимо использовать возможности системы компоновки данных (СКД).

В ЗУП 3.1 большинство отчетов строится на базе общих механизмов. Для внедрения фильтрации во все отчеты сразу, мы можем использовать расширение общего модуля ОтчетыСервер, метод ПриКомпоновкеРезультата. Там мы можем программно добавить элемент отбора в настройки компоновщика данных перед формированием отчета.


// В модуле объекта или общем модуле
Процедура Расш1_ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
    ПолеПодразделение = Новый ПолеКомпоновкиДанных("Подразделение");
    
    // Добавляем отбор в пользовательские настройки или настройки по умолчанию
    Отбор = КомпоновщикНастроек.Настройки.Отбор;
    Элемент = Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
    Элемент.ЛевоеЗначение = ПолеПодразделение;
    Элемент.ВидСравнения = ВидСравненияКомпоновкиДанных.ВСписке;
    Элемент.ПравоеЗначение = ПараметрыСеанса.Расш1_ДоступныеПодразделения;
    Элемент.Использование = Истина;
    
КонецПроцедуры

Безопасность и риски

Необходимо помнить, что программные отборы в формах и отчетах — это не полноценная замена безопасности. Если пользователю оставлены права на чтение объектов в метаданных (роли), он теоретически может получить к ним доступ через консоль запросов или внешние обработки. Однако для решения бизнес-задачи распределения ответственности между заведующими отделений и исключения "визуального шума" в списках, описанные методы являются наиболее производительными и гибкими.

Подводя итог, можно сказать, что для реализации качественного ограничения по подразделениям в ЗУП 3.1 без RLS, мы должны использовать связку из параметров сеанса для хранения прав, фиксированных настроек динамического списка для форм и модификации СКД для отчетов. Это обеспечит высокую скорость работы системы даже при очень большом количестве данных и пользователей.

← На главную