Как открыть форму выбора иерархического справочника с отбором по конкретной группе?

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

При разработке в среде «1С:Предприятие 8.3» программисты часто сталкиваются со специфической проблемой: требуется открыть форму выбора иерархического справочника (например, «Склады» или «Номенклатура»), но ограничить видимость так, чтобы пользователь мог выбирать элементы и группы только внутри одной конкретной ветки дерева. При этом все остальные корневые папки и соседние ветви должны быть полностью скрыты.

Обычная попытка наложить фиксированные настройки или стандартный отбор вида ВИерархии по выбранной группе на динамический список приводит к неприятному результату. На экране пользователя отображается вся структура дерева, где «лишние» ветви оказываются пустыми, а нужная ветка находится где-то в глубине. Давайте подробно разберем, почему так происходит, проанализируем ограничения платформы и рассмотрим пять практических способов решения этой задачи.

Почему возникает проблема «пустых веток» (ограничение платформы)

Для начала выясним причину такого поведения системы. В платформе «1С:Предприятие 8» действует фундаментальное правило построения интерфейса: если динамический список отображается в режиме дерева или иерархического списка, подчиненный элемент не может быть выведен на экран, если скрыт хотя бы один из его родительских элементов.

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

Рассмотрим детальные варианты решения этой проблемы — от простых интерфейсных настроек до создания кастомных форм.

Способ 1. Использование свойств «ТекущийРодитель» и «ИзменятьТекущегоРодителя» (без создания новой формы)

Если наша цель — заставить пользователя работать строго внутри определенной папки (например, «Социальное питание»), нам не обязательно физически скрывать остальное дерево. Мы можем применить стандартный механизм «блокировки» пользователя в пределах конкретной ветки. Это самый изящный способ, так как он не требует создавать новые формы в конфигураторе.

Реализуется это следующим образом в модуле формы документа, из которого вызывается выбор:


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

Как это работает: При открытии формы выбора динамический список сразу переходит внутрь папки «Социальное питание». Свойство ИзменятьТекущегоРодителя = Ложь блокирует кнопку «Перейти на уровень вверх» (стрелку вверх) и командную панель навигации по пути. Пользователь видит только вложенные подгруппы и элементы, а выйти в корень справочника и увидеть другие склады физически не может.

Способ 2. Программное условное оформление стандартной формы (без изменения конфигуратора)

Если все-таки необходимо скрыть «лишние» группы визуально, мы можем воспользоваться механизмом условного оформления динамического списка. Но делать это в кастомной форме не обязательно — мы можем настроить стандартную форму программно «на лету» прямо при ее открытии.

Посмотрим на пример реализации в модуле формы документа:


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

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

При таком подходе все элементы справочника, которые не принадлежат иерархии группы «Социальное питание», будут программно скрыты. Однако учитывайте, что корень дерева и пустые ветки все равно могут схематично отображаться в некоторых режимах просмотра списка.

Способ 3. Использование Дерева Значений вместо Динамического списка (в кастомной форме)

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

Разберем по шагам, как это реализовать:

  1. Создайте новую произвольную форму в справочнике или обработке.
  2. Добавьте реквизит формы с именем ДеревоСкладов и типом ДеревоЗначений. Создайте в нем колонки: Ссылка (тип СправочникСсылка.Склады) и Наименование (тип Строка).
  3. Перетащите реквизит ДеревоСкладов на форму в виде таблицы. Установите для таблицы режим отображения «Дерево».
  4. В модуле этой формы напишите следующий обработчик заполнения:

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    // Находим нашу опорную ветку
    ГруппаСоцПитание = Справочники.Склады.НайтиПоНаименованию("Социальное питание", Истина);
    
    Если Не ЗначениеЗаполнено(ГруппаСоцПитание) Тогда
        Возврат;
    КонецЕсли;
    
    // Строим запрос для получения иерархии
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	Склады.Ссылка КАК Ссылка,
        |	Склады.Наименование КАК Наименование
        |ИЗ
        |	Справочник.Склады КАК Склады
        |ГДЕ
        |	Склады.Ссылка В ИЕРАРХИИ(&КорневаяГруппа)
        |	И Склады.ЭтоГруппа = ИСТИНА
        |ИТОГИ ПО
        |	Ссылка ИЕРАРХИЯ";
        
    Запрос.УстановитьПараметр("КорневаяГруппа", ГруппаСоцПитание);
    
    // Выгружаем результат запроса в дерево значений по группировкам с иерархией
    РезультатЗапроса = Запрос.Выполнить();
    ДеревоРезультат = РезультатЗапроса.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
    
    // Копируем полученную структуру в реквизит формы
    ЗначениеВРеквизитФормы(ДеревоРезультат, "ДеревоСкладов");
    
КонецПроцедуры

Для возврата выбранного значения пользователю достаточно обработать событие выбора строки в таблице дерева значений на клиенте:


&НаКлиенте
Процедура ДеревоСкладовВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    ТекущиеДанные = Элементы.ДеревоСкладов.ТекущиеДанные;
    
    Если ТекущиеДанные <> Неопределено Тогда
        // Возвращаем выбранную ссылку в вызывающую форму
        Закрыть(ТекущиеДанные.Ссылка);
    КонецЕсли;
    
КонецПроцедуры

Преимущества этого способа: Вы получаете абсолютно чистое дерево. В нем не будет никаких «призраков» корневых папок, пустых веток или лишних узлов верхнего уровня. Отображается строго ветка «Социальное питание» со всей своей внутренней структурой. Скорость работы формы на малых и средних справочниках будет мгновенной.

Способ 4. Создание собственной формы с условным оформлением Динамического списка

Если справочник огромный и дерево значений использовать нежелательно из-за возможных задержек при загрузке данных, мы можем использовать подход автора темы с созданием кастомной формы выбора и настройкой динамического списка в режиме дерева.

Выполним следующие шаги:

  1. Создаем новую форму выбора. Располагаем на ней стандартный динамический список Список.
  2. В свойствах таблицы формы для списка устанавливаем «Отображение» в значение Дерево.
  3. Прописываем условное оформление в обработчике события ПриСозданииНаСервере:

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

И на клиенте обрабатываем команду выбора элемента:


&НаКлиенте
Процедура СписокВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    ТекущиеДанные = Элементы.Список.ТекущиеДанные;
    
    Если ТекущиеДанные <> Неопределено Тогда
        Закрыть(ТекущиеДанные.Ссылка);
    КонецЕсли;
    
КонецПроцедуры

Способ 5. Ограничение видимости элементов на уровне прав доступа (RLS)

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

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

В роли пользователя настраивается шаблон ограничения доступа к справочнику «Склады» на уровне базы данных (СУБД):

ГДЕ Ссылка В ИЕРАРХИИ (&ДоступныеСклады)

При использовании RLS платформа автоматически на уровне SQL-запросов отсекает любые упоминания других складов. В результате, какую бы форму выбора (включая стандартную форму в режиме дерева) пользователь ни открыл, он физически увидит только разрешенную ему ветку без необходимости писать какой-либо программный код в формах.

Резюме и рекомендации

Подведем итог совместной работы и выберем оптимальный инструмент под конкретную ситуацию:

← На главную