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