При разработке механизмов выгрузки данных или построении сложных отчетов часто возникает необходимость получить не просто непосредственного родителя товара, а всю цепочку категорий (групп), в которые он входит — от самого верхнего уровня до папки, в которой непосредственно лежит элемент. В этой статье мы разберем, как решить эту задачу двумя основными способами: с помощью программного обхода и через механизм запросов 1С.
Этот метод наиболее удобен, если вы уже находитесь внутри цикла обработки товаров и вам нужно быстро получить список родителей для текущей ссылки. Основная идея заключается в последовательном обращении к свойству Родитель до тех пор, пока мы не достигнем корня справочника (пустой ссылки).
Рассмотрим по шагам реализацию этого алгоритма с сохранением данных в таблицу значений для последующей сортировки:
ДеревоГрупп с колонками для хранения ссылки на группу и её уровня вложенности.Пока, который будет двигаться вверх по дереву.Уровень().Проанализируем готовый код:
// Предположим, переменная ТоварСсылка содержит ссылку на номенклатуру
ТекущийРодитель = ТоварСсылка.Родитель;
// Подготовим таблицу для хранения результата
ОписаниеУровня = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10, 0));
ТаблицаИерархии = Новый ТаблицаЗначений;
ТаблицаИерархии.Колонки.Добавить("Группа", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТаблицаИерархии.Колонки.Добавить("Уровень", ОписаниеУровня);
// Обходим дерево вверх
Пока ЗначениеЗаполнено(ТекущийРодитель) Цикл
НоваяСтрока = ТаблицаИерархии.Добавить();
НоваяСтрока.Группа = ТекущийРодитель;
НоваяСтрока.Уровень = ТекущийРодитель.Уровень();
// Переходим к следующему предку
ТекущийРодитель = ТекущийРодитель.Родитель;
КонецЦикла;
// Чтобы дерево шло от корня к товару, отсортируем по возрастанию уровня
ТаблицаИерархии.Сортировать("Уровень Возр");
// Теперь можно выгрузить результат, например, в массив или строку
Для Каждого Стр Из ТаблицаИерархии Цикл
Сообщить("Уровень " + Стр.Уровень + ": " + Стр.Группа);
КонецЦикла;
Важный нюанс: Обратите внимание на типизацию колонок таблицы значений. В 1С метод Сортировать() может вести себя некорректно или вызывать ошибку "Несоответствие типов", если колонки не были типизированы при создании (особенно это касается числовых полей уровня).
Если вам нужно получить дерево категорий максимально производительно (особенно для большого списка товаров) или вы хотите избежать множественных обращений к базе данных в цикле, следует использовать язык запросов 1С. Система имеет встроенный механизм для работы с иерархией через предложение ИТОГИ.
Выясним, как составить такой запрос. Нам понадобится конструкция ТОЛЬКО ИЕРАРХИЯ, которая позволяет выбрать всех родителей элемента, не выбирая при этом сам элемент в итоги (или выбирая и то, и другое, если убрать это ключевое слово).
ВЫБРАТЬ
Номенклатура.Ссылка КАК Ссылка
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.Ссылка = &ТоварСсылка
ИТОГИ ПО
Ссылка ТОЛЬКО ИЕРАРХИЯ
Разберем особенности работы с результатом такого запроса. При обходе выборки с типом ОбходРезультатаЗапроса.ПоГруппировкамСИерархией платформа 1С вернет вам дерево, начиная с самого верхнего уровня. Это избавляет от необходимости вручную сортировать таблицу значений, как мы делали в первом способе.
Иногда (хотя это считается плохим тоном в программировании) разработчики используют фиксированное количество уровней в запросе, если точно знают, что вложенность справочника ограничена (например, не более 5 уровней). Это позволяет получить всех родителей в одну строку таблицы.
Посмотрим на пример такого подхода:
ВЫБРАТЬ
Номенклатура.Родитель КАК Уровень1,
Номенклатура.Родитель.Родитель КАК Уровень2,
Номенклатура.Родитель.Родитель.Родитель КАК Уровень3,
Номенклатура.Родитель.Родитель.Родитель.Родитель КАК Уровень4
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.Ссылка = &ТоварСсылка
Важно: Мы крайне не рекомендуем использовать этот метод в тиражируемых решениях. Если структура справочника изменится и уровней станет больше, код перестанет работать корректно. Кроме того, большое количество точек в запросе (разыменование полей через родителя) негативно сказывается на производительности SQL-запроса.
Проанализируем ситуацию и выберем оптимальный путь:
Не забывайте проверять результат на пустые ссылки (ЗначениеЗаполнено()) и учитывать, что корень справочника в 1С всегда представлен пустой ссылкой соответствующего типа, а не значением NULL (за исключением специфических левых соединений в запросах).