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

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

Довольно часто в практике программиста 1С встречается интересная задача: имеется справочник с глубокой иерархией (например, Номенклатура со структурой "Продукция — Категория — Вид" и уровнем вложенности вплоть до 10). Требуется выстроить все группы в одну таблицу так, чтобы каждый уровень иерархии располагался в отдельной колонке (Продукция | Категория | Вид). Дополнительно может потребоваться получить иерархию только до определенного уровня (например, остановиться только на категориях).

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

Вариант 1. Программный обход через Дерево Значений и Таблицу Значений (Рекурсия)

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


Процедура ОбойтиУровеньДереваБезСС(Строки)
    Для каждого Строка из Строки Цикл
        НомерКолонки = Строка.Уровень(); //Номер колонок групп
        ИмяКолонкиНом = "ПродукцияНом";//Имя колонки продукции
        
        СтрокаТаблицыИтогов = ТаблицаПоГруппировкам.Добавить();
        Если Строка.Строки.Количество() > 0 тогда 
            ИмяКолонки = "группа" + НомерКолонки;
            КвалификаторыЧисла = Новый КвалификаторыСтроки(100);
            Массив = Новый Массив;
            Массив.Добавить(Тип("Строка"));
            ЧисловойТип = Новый ОписаниеТипов(Массив, , ,КвалификаторыЧисла);
            
            Если ТаблицаПоГруппировкам.Колонки.Найти("группа"+ИмяКолонки) <> Неопределено Тогда
                ЭлементыФормы.ТаблицаПоГруппировкам.СоздатьКолонки();
                СтрокаТаблицыИтогов["группа"+ИмяКолонки] = Строка.Ссылка.Наименование;
            Иначе
                ТаблицаПоГруппировкам.Колонки.Добавить("группа"+ИмяКолонки,ЧисловойТип,ИмяКолонки);
                ЭлементыФормы.ТаблицаПоГруппировкам.СоздатьКолонки();
                СтрокаТаблицыИтогов["группа"+ИмяКолонки] = Строка.Ссылка.Наименование;
            КонецЕсли;
        Иначе
            // Аналогичный код для элементов
            // ...
        КонецЕсли;
        
        ОбойтиУровеньДереваБезСС(Строка.Строки);
    КонецЦикла; 
КонецПроцедуры

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

Критическая ошибка текущего кода: использование метода ЭлементыФормы.ТаблицаПоГруппировкам.СоздатьКолонки() внутри цикла. Это крайне ресурсоемкая операция. Перестроение элементов управления на форме при каждой итерации рекурсии приведет к катастрофическому падению производительности, особенно если в справочнике тысячи элементов.

Как сделать правильно:

  1. Сначала необходимо вычислить максимальную глубину (уровень) дерева.
  2. Один раз программно создать все нужные колонки в таблице значений (на сервере или до начала вывода на форму).
  3. Сформировать таблицу в памяти, заполнив ее с помощью рекурсии.
  4. И только после этого однократно вывести готовую таблицу на форму с помощью метода СоздатьКолонки().

Вариант 2. Использование Системы Компоновки Данных (СКД)

Самым правильным, современным и быстрым инструментом для вывода иерархии в отдельные колонки в 1С является СКД.

Поскольку максимальный уровень вложенности нам примерно известен (например, не больше 10), мы можем решить задачу еще на этапе написания запроса СКД. Рассмотрим подробнее этот механизм:

  1. В запросе можно заранее прописать поля через обращения к родителю. Например: Номенклатура.Родитель КАК Уровень2, Номенклатура.Родитель.Родитель КАК Уровень1. Это жестко зафиксирует колонки, если структура справочника строго регламентирована.
  2. При настройке вывода иерархии в СКД часто возникает проблема «дублей», когда родительская группа выводится отдельной строкой, а под ней снова идут данные. Чтобы избавиться от этого и выстроить четкую таблицу, используют пользовательские поля.
  3. Создаем пользовательское поле с выражением: Выбор Когда Уровень() = 0 Тогда Неопределено Иначе Номенклатура Конец. Это позволит скрыть ненужные промежуточные группировки.

Решение второго вопроса: как получить данные только по определенному уровню?

Выясним способы ограничения выборки конкретным уровнем (например, вывести справочник, остановившись только на уровне "Вид").

Способ 1: Через СКД

В системе компоновки данных существуют встроенные системные поля Уровень() и УровеньВГруппировке. Используя их, мы можем легко наложить отбор прямо в пользовательских настройках отчета (например, установить отбор Уровень() = 2).

Способ 2: Через язык запросов

В языке запросов 1С нет встроенной функции получения уровня вложенности элемента напрямую. Однако получить иерархическую структуру можно с помощью конструкции ИТОГИ ПО ... ТОЛЬКО ИЕРАРХИЯ.


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Элемент
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ИТОГИ ПО
    Элемент ТОЛЬКО ИЕРАРХИЯ

Если требуется получить дерево с четким пониманием уровня прямо из запроса в виде плоской таблицы, разработчики часто применяют соединения справочника с самим собой по полю Родитель. Для каждого уровня вложенности добавляется свое левое соединение, и вычисляется поле Уровень = 1 + ....

Способ 3: Ограничение на форме (Визуальное)

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

Итог: Программный обход справочников и деревьев с ручным формированием колонок оправдан только в очень нестандартных задачах. В 90% случаев для вывода иерархии в отдельные колонки и отбора по уровню следует использовать возможности Системы Компоновки Данных (СКД) — это сэкономит ресурсы системы и время разработчика.

← На главную