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

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

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

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

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

Для понимания проблемы проанализируем внутренний механизм работы динамического списка. Когда для таблицы формы установлен режим отображения Дерево или Иерархический список, считывание данных платформой выполняется порционно сверху вниз:

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

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

Решение 1. Выборка цепочки родителей через условия соединения

Разберем вариант, когда структура каталога имеет небольшую и фиксированную глубину вложенности. Чтобы платформа смогла построить дерево сверху донизу, в результат запроса динамического списка должны попасть не только непосредственные папки с товарами, но и все их вышестоящие родительские группы.

Посмотрим на пример произвольного запроса для динамического списка (с установленной основной таблицей Справочник.Номенклатура):


ВЫБРАТЬ РАЗЛИЧНЫЕ
    СправочникНоменклатура.Ссылка КАК Ссылка,
    СправочникНоменклатура.Родитель КАК Родитель,
    СправочникНоменклатура.ЭтоГруппа КАК ЭтоГруппа,
    СправочникНоменклатура.Код КАК Код,
    СправочникНоменклатура.Наименование КАК Наименование
ИЗ
    Справочник.Номенклатура КАК СправочникНоменклатура
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(&ТекущаяДата, Склад = &ТекущийСклад) КАК ТоварыНаСкладахОстатки
        ПО (СправочникНоменклатура.Ссылка = ТоварыНаСкладахОстатки.Номенклатура.Родитель
            ИЛИ СправочникНоменклатура.Ссылка = ТоварыНаСкладахОстатки.Номенклатура.Родитель.Родитель
            ИЛИ СправочникНоменклатура.Ссылка = ТоварыНаСкладахОстатки.Номенклатура.Родитель.Родитель.Родитель)

Разберем особенности этого подхода:

  1. Обязательно указываем свойство Основная таблица в настройках динамического списка (указываем Справочник.Номенклатура), иначе режим дерева будет недоступен.
  2. Условие ИЛИ по цепочке Родитель.Родитель... обеспечивает попадание в результат запроса всех вышестоящих папок вплоть до корня каталога.
  3. Ограничение подхода: если глубина вложенности папок в справочнике превысит число разыменований через точку, глубоко вложенные группы вновь перестанут выводиться. Кроме того, множественные соединения через ИЛИ могут снижать производительность на больших базах.

Решение 2. Использование реквизита формы «ДеревоЗначений»

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

Рассмотрим по шагам реализацию этого решения:

  1. На форме создаем реквизит ДеревоГрупп с типом ДеревоЗначений и добавляем в него колонки Ссылка, Наименование, Код. Перетаскиваем реквизит в элементы формы как таблицу.
  2. На сервере выполняем запрос с итогами по иерархии, выбирая остатки товаров:

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

Такой подход полностью исключает проблемы с пропущенными родительскими папками, так как конструкция ИТОГИ ПО ... ИЕРАРХИЯ автоматически восстанавливает все промежуточные родительские узлы до самого верхнего уровня.

Решение 3. Установка отбора по массиву ссылок в динамическом списке

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

  1. Отдельным запросом на сервере получаем все группы, в которых есть остатки товаров.
  2. Рекурсивно или в цикле поднимаемся по родителям всех найденных папок и собираем полный массив ссылок (включая все промежуточные узлы и корень).
  3. Передаем массив в отбор динамического списка:

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

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

← На главную