Как получить дерево категорий для конкретного товара в 1С?

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

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

Способ 1: Программный обход иерархии в цикле

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

Рассмотрим по шагам реализацию этого алгоритма с сохранением данных в таблицу значений для последующей сортировки:

  1. Создадим таблицу значений ДеревоГрупп с колонками для хранения ссылки на группу и её уровня вложенности.
  2. Используем цикл Пока, который будет двигаться вверх по дереву.
  3. Для определения порядка (кто "папа", а кто "дедушка") воспользуемся методом Уровень().

Проанализируем готовый код:


// Предположим, переменная ТоварСсылка содержит ссылку на номенклатуру
ТекущийРодитель = ТоварСсылка.Родитель;

// Подготовим таблицу для хранения результата
ОписаниеУровня = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10, 0));
ТаблицаИерархии = Новый ТаблицаЗначений;
ТаблицаИерархии.Колонки.Добавить("Группа", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТаблицаИерархии.Колонки.Добавить("Уровень", ОписаниеУровня);

// Обходим дерево вверх
Пока ЗначениеЗаполнено(ТекущийРодитель) Цикл
    НоваяСтрока = ТаблицаИерархии.Добавить();
    НоваяСтрока.Группа  = ТекущийРодитель;
    НоваяСтрока.Уровень = ТекущийРодитель.Уровень();
    
    // Переходим к следующему предку
    ТекущийРодитель = ТекущийРодитель.Родитель;
КонецЦикла;

// Чтобы дерево шло от корня к товару, отсортируем по возрастанию уровня
ТаблицаИерархии.Сортировать("Уровень Возр");

// Теперь можно выгрузить результат, например, в массив или строку
Для Каждого Стр Из ТаблицаИерархии Цикл
    Сообщить("Уровень " + Стр.Уровень + ": " + Стр.Группа);
КонецЦикла;

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

Способ 2: Получение иерархии через запрос (ИТОГИ)

Если вам нужно получить дерево категорий максимально производительно (особенно для большого списка товаров) или вы хотите избежать множественных обращений к базе данных в цикле, следует использовать язык запросов 1С. Система имеет встроенный механизм для работы с иерархией через предложение ИТОГИ.

Выясним, как составить такой запрос. Нам понадобится конструкция ТОЛЬКО ИЕРАРХИЯ, которая позволяет выбрать всех родителей элемента, не выбирая при этом сам элемент в итоги (или выбирая и то, и другое, если убрать это ключевое слово).


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

Разберем особенности работы с результатом такого запроса. При обходе выборки с типом ОбходРезультатаЗапроса.ПоГруппировкамСИерархией платформа 1С вернет вам дерево, начиная с самого верхнего уровня. Это избавляет от необходимости вручную сортировать таблицу значений, как мы делали в первом способе.

Способ 3: Статический запрос (через "выбор когда")

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

Посмотрим на пример такого подхода:


ВЫБРАТЬ
    Номенклатура.Родитель КАК Уровень1,
    Номенклатура.Родитель.Родитель КАК Уровень2,
    Номенклатура.Родитель.Родитель.Родитель КАК Уровень3,
    Номенклатура.Родитель.Родитель.Родитель.Родитель КАК Уровень4
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.Ссылка = &ТоварСсылка

Важно: Мы крайне не рекомендуем использовать этот метод в тиражируемых решениях. Если структура справочника изменится и уровней станет больше, код перестанет работать корректно. Кроме того, большое количество точек в запросе (разыменование полей через родителя) негативно сказывается на производительности SQL-запроса.

Какой метод выбрать?

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

  1. Если нужно получить путь для одного товара в контексте формы или простой обработки — используйте Способ 1 (Цикл). Он нагляден и прост в отладке.
  2. Если вы формируете данные для выгрузки (например, в XML или JSON для сайта/мобильного приложения) — используйте Способ 2 (Запрос с Итогами). Это наиболее "православный" метод с точки зрения платформы 1С, так как он минимизирует количество серверных вызовов.
  3. Если вы работаете с очень большими справочниками (сотни тысяч элементов) и вам нужно получить иерархию сразу для всего списка товаров, изучите тему "Транзитивное замыкание". Это более сложная техника построения запросов с использованием временных таблиц, которая позволяет избежать рекурсии и итогов.

Не забывайте проверять результат на пустые ссылки (ЗначениеЗаполнено()) и учитывать, что корень справочника в 1С всегда представлен пустой ссылкой соответствующего типа, а не значением NULL (за исключением специфических левых соединений в запросах).

← На главную