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