Как вывести в подборе только одну ветку дерева папок номенклатуры в 1С?

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

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

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

Почему стандартный отбор ломает дерево в динамическом списке?

Для начала проанализируем ситуацию и выясним, почему обычный отбор не работает для иерархических списков. Динамический список с включенным отображением «Дерево» работает по особому алгоритму оптимизации запросов к СУБД:

  1. Платформа изначально считывает из базы данных только элементы верхнего уровня — те, у которых родитель пуст (Родитель = ЗначениеЗаполнено(Ложь)).
  2. Когда пользователь нажимает на «плюсик» (разворачивает узел), платформа отправляет к СУБД новый точечный запрос на получение элементов, у которых родителем является выбранная папка.

Если мы устанавливаем стандартный отбор динамического списка, например: Ссылка В Иерархии (&НашаПапка), то СУБД отсекает все элементы, не входящие в эту папку. Но папки самого верхнего уровня (корневые) также отсекаются! Платформа пытается построить дерево с корня, не находит корневых элементов в выборке и в результате либо очищает список, либо (если отключена основная таблица) выводит элементы сплошным «плоским» списком, полностью теряя иерархическую структуру.

К счастью, в платформе «1С:Предприятие» предусмотрены штатные инструменты для обхода этого ограничения.

Решение 1. Использование свойства «РодительВерхнегоУровня» (Рекомендуемый способ)

Самым простым, надежным и штатным способом является использование свойства РодительВерхнегоУровня (в англоязычном варианте — TopLevelParent). Важно понимать: это свойство принадлежит не самому реквизиту формы «Динамический список», а элементу управления «Таблица» на форме, который этот список визуализирует.

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

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


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

Передача родителя через параметры открытия формы

Еще более изящный вариант — передавать это свойство динамически в момент вызова формы выбора из другого модуля. Платформа 1С умеет автоматически обрабатывать параметр с именем РодительВерхнегоУровня при открытии формы. Нам даже не придется писать код внутри самой формы выбора!

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


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

При таком подходе форма откроется сразу настроенной на нужную ветку, а пользователь физически не сможет выйти за рамки указанной папки «Материалы».

Решение 2. Использование свойств «ТекущийРодитель» и «ИзменятьТекущегоРодителя»

Иногда бизнес-логика требует, чтобы пользователь при открытии формы сразу позиционировался внутри определенной папки, но при этом сама структура вышестоящих папок оставалась видимой (или, наоборот, нужно жестко запереть пользователя в папке без возможности подняться наверх). Для этого сценария мы можем использовать связку свойств элемента формы «Таблица»:

Разберем по шагам, как зафиксировать пользователя в определенной ветке с помощью этих свойств:


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

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

Альтернативный вариант для сложных случаев: Дерево значений

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

Для таких задач динамический список не подходит, так как он не умеет на лету анализировать итоги по остаткам в разрезе иерархии без потери производительности. В этом случае мы рекомендуем отказаться от динамического списка в пользу классического Дерева Значений.

Проанализируем этот алгоритм:

  1. На форме создается реквизит с типом ДеревоЗначений.
  2. На сервере пишется запрос, который собирает данные по номенклатуре и остаткам. В запросе обязательно используется конструкция ИТОГИ ПО Ссылка ИЕРАРХИЯ.
  3. Результат запроса выгружается в дерево значений, которое затем отображается на форме.

Посмотрим на пример реализации построения такого дерева на сервере:


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

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

Резюме

Для решения задачи отображения одной ветки дерева папок номенклатуры в 1С используйте следующие правила:

  1. В 95% случаев используйте свойство элемента формы РодительВерхнегоУровня. Оно работает быстро, безотказно и поддерживается платформой на уровне стандартных параметров открытия форм.
  2. Если необходимо ограничить пользователя конкретной папкой без визуального отсечения всей остальной структуры, используйте связку ТекущийРодитель и ИзменятьТекущегоРодителя = Ложь.
  3. Никогда не пытайтесь решить эту задачу простым наложением отбора В иерархии в динамическом списке, так как это нарушает внутренние алгоритмы выборки СУБД для построения дерева.
← На главную