В процессе автоматизации учета крупных компаний перед разработчиками часто возникает задача ограничения видимости документов в списках. Типичный пример: в документе «Перемещение товаров» пользователи конкретного филиала или подразделения должны видеть только «свои» документы. То есть те операции, в которых их подразделение выступает либо в роли отправителя, либо в роли получателя. При этом сотрудники управляющей компании (головного офиса) должны иметь беспрепятственный доступ ко всем документам базы.
В этой статье мы подробно разберем, как реализовать такую логику программно, избегая распространенных ошибок, связанных с синтаксисом платформы 1С:Предприятие 8.3, обходом коллекций и архитектурой динамических списков.
Для реализации этой задачи нам потребуется доработать форму списка документа. Нам необходимо при создании формы на сервере определить подразделение текущего пользователя, проверить, относится ли он к руководящему составу (которому можно видеть всё), и, если нет, наложить жесткий отбор. Отбор должен работать по условию ИЛИ:
ПодразделениеОтправитель равен подразделению пользователя;ПодразделениеПолучатель равен подразделению пользователя.Рассмотрим по шагам, как это правильно запрограммировать в модуле управляемой формы списка.
---
Для объединения нескольких условий отбора по правилу «ИЛИ» в компоновке данных используется специальный объект — группа элементов отбора. Посмотрим на пример кода, который решает эту задачу:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Получаем подразделение текущего пользователя из параметров сеанса
ТекущееПодразделение = ПараметрыСеанса.ТекущийПользователь.Подразделение;
// Проверяем, заполнено ли подразделение у пользователя
Если ЗначениеЗаполнено(ТекущееПодразделение) Тогда
// Проверяем, нужно ли накладывать ограничение (исключаем головное подразделение)
Если НЕ ПользовательВГлавеУправления(ТекущееПодразделение) Тогда
// 1. Добавляем группу отборов в компоновщик настроек динамического списка
ГруппаОтбора = Список.КомпоновщикНастроек.Настройки.Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
ГруппаОтбора.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИли;
ГруппаОтбора.Использование = Истина;
ГруппаОтбора.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный;
// 2. Добавляем первое условие: Отправитель равен нашему подразделению
ЭлементОтбора1 = ГруппаОтбора.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора1.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ПодразделениеОтправитель");
ЭлементОтбора1.ВидСравнения = ВидСравненияКомпоновкиДанных.ВИерархии; // Позволяет учитывать подчиненные подразделения
ЭлементОтбора1.ПравоеЗначение = ТекущееПодразделение;
ЭлементОтбора1.Использование = Истина;
// 3. Добавляем второе условие: Получатель равен нашему подразделению
ЭлементОтбора2 = ГруппаОтбора.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора2.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ПодразделениеПолучатель");
ЭлементОтбора2.ВидСравнения = ВидСравненияКомпоновкиДанных.ВИерархии;
ЭлементОтбора2.ПравоеЗначение = ТекущееПодразделение;
ЭлементОтбора2.Использование = Истина;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
---
В обсуждении на форуме предлагалось искать головное подразделение по коду "00-000001". Настоятельно рекомендуем избегать «хардкода» строковых кодов в программном коде. Если код элемента в справочнике изменится или при переносе данных создадутся новые дубли, логика программы сломается. Лучше использовать предопределенные элементы, константы или проверять свойства объектов.
Напишем вспомогательную функцию для проверки принадлежности к руководящему подразделению:
&НаСервере
Функция ПользовательВГлавеУправления(ПодразделениеПользователя)
// Безопасный способ определения головного подразделения.
// Вместо жесткого поиска по коду, используем предопределенный элемент (если он есть)
// или параметр учета. Здесь для примера покажем поиск по коду, но с проверкой на пустое значение.
ГоловноеПодразделение = Справочники.ПодразделенияОрганизаций.НайтиПоКоду("00-000001");
Если ГоловноеПодразделение.Пустая() Тогда
Возврат Ложь; // Если не нашли головное по коду, по умолчанию ограничиваем права
КонецЕсли;
// Проверяем, совпадает ли подразделение с головным или является ли оно его подчиненным (в иерархии)
Возврат ПодразделениеПользователя.ПринадлежитЭлементу(ГоловноеПодразделение);
КонецФункции
---
Проанализируем детально ошибки, с которыми столкнулись участники форума, и выясним причины их возникновения:
Для каждогоПопытка очистить существующие отборы следующим образом гарантированно приведет к неполному удалению элементов или аварийному завершению работы платформы:
// ТАК ДЕЛАТЬ НЕЛЬЗЯ!
Для каждого ЭлементОтбора Из Список.Отбор.Элементы Цикл
Список.Отбор.Элементы.Удалить(ЭлементОтбора);
КонецЦикла;
Причина ошибки: Во время обхода коллекции методом Для каждого удаление текущего элемента смещает индексы всех последующих элементов назад. В результате цикл пропускает каждый второй элемент. Правильным решением для полной очистки коллекции является использование штатного метода Очистить():
Список.КомпоновщикНастроек.Настройки.Отбор.Элементы.Очистить();
Эта ошибка возникла в сообщении 23 и 26. Разработчик скопировал кусок кода, в котором пропустил инициализацию переменной ЭлементОтбора в ветке Иначе:
Иначе
// Переменная ЭлементОтбора не была создана через .Добавить()!
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ПодразделениеОрганизации");
КонецЕсли;
Решение проблемы: Всегда следите за тем, чтобы перед обращением к свойствам объекта (таким как ЛевоеЗначение, ВидСравнения, ПравоеЗначение) этот объект был физически добавлен в коллекцию и присвоен переменной:
ЭлементОтбора = Список.КомпоновщикНастроек.Настройки.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
В сообщении 10 предлагалось использовать тип ГруппаВыбранныхПолейКомпоновкиДанных. Это грубая ошибка. Данный тип предназначен исключительно для группировки выводимых колонок в отчете или списке, но никак не для фильтрации данных.
Правильный тип: Для создания логических условий И/ИЛИ/НЕ всегда используйте ГруппаЭлементовОтбораКомпоновкиДанных.
---
Поскольку разработка ведется в конфигурации «Бухгалтерия КОРП 3.0», в ней по умолчанию присутствует Библиотека стандартных подсистем (БСП). БСП предоставляет готовые программные интерфейсы для работы с отборами динамических списков, что позволяет сократить количество строк кода и повысить его надежность.
Рассмотрим, как установить простой отбор (например, в журнале документов или списке платежных поручений) с помощью функций БСП:
&НаСервере
Процедура УстановитьОтборПоБСП()
ТекущееПодразделение = ПараметрыСеанса.ТекущийПользователь.Подразделение;
// Если пользователь не из управления, ставим отбор в одну строчку
Если НЕ ПользовательВГлавеУправления(ТекущееПодразделение) Тогда
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(
Список, // Наш динамический список
"ПодразделениеОрганизации", // Имя поля в списке
ТекущееПодразделение, // Значение отбора
ВидСравненияКомпоновкиДанных.ВИерархии, // Вид сравнения
"Отбор по подразделению", // Представление отбора
Истина, // Использование
РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный // Скрываем от пользователя
);
КонецЕсли;
КонецПроцедуры
---
В сообщениях 40-55 автор столкнулся с проблемой: отбор отлично работает в документе «Перемещение товаров», но в «Журнале документов (Деньги)» пользователи перестают видеть вообще какие-либо данные.
При работе с журналами документов крайне важно учитывать следующие архитектурные особенности:
ПодразделениеОрганизации), обязательно должно быть выведено в качестве Графы журнала в конфигураторе. На вкладке «Графы» проверяемого журнала должно быть настроено соответствие этого поля реквизитам входящих в него документов. Если у какого-то документа нет соответствия этой графе, этот документ полностью исчезнет из видимости при установке отбора.Подразделение = [Наше подразделение] этот документ автоматически скрывается. Для таких случаев в отбор необходимо добавлять дополнительное условие «ИЛИ Подразделение НЕ Заполнено».---
Программная установка отборов в форме списка — это отличное решение для обеспечения удобства работы пользователей (чтобы они не путались в чужих документах). Однако это не является надежным средством защиты информации.
Опытный пользователь сможет обойти форму списка и просмотреть чужие перемещения через:
Если перед вами стоит задача жесткого разграничения коммерческих или конфиденциальных данных, лучшим решением будет использование типового механизма RLS (Ограничение доступа на уровне записей), который уже встроен в 1С:Бухгалтерию КОРП. Настройка RLS по подразделениям выполняется в режиме «1С:Предприятие» в разделе Администрирование — Настройки пользователей и прав — Группы доступа без изменения программного кода конфигурации.