Как отобразить самого верхнего родителя в иерархии справочника 1С

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

При работе с иерархическими справочниками в 1С часто возникает задача отображения не просто непосредственного родителя, а самого «корня» всей цепочки — самого верхнего элемента иерархии. Это бывает необходимо для визуального контроля, построения аналитических отчетов или упрощения навигации пользователя по сложной структуре. Рассмотрим подробнее, как реализовать этот функционал, разберем возможные архитектурные подходы и проанализируем риски каждого из них.

Постановка задачи и выбор стратегии

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

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

Решение 1: Динамическое отображение на форме элемента

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

Разберем по шагам, как это реализовать:

  1. Создадим на форме справочника реквизит ВерхнийРодитель с типом ссылки на этот же справочник.
  2. Вынесем этот реквизит на форму в режиме «Только просмотр».
  3. В обработчике события ПриЧтенииНаСервере или ПриСозданииНаСервере пропишем логику поиска корня.

Для универсального получения корня лучше всего использовать рекурсивную функцию или цикл «вверх» по иерархии. Рассмотрим пример кода, который можно разместить в модуле менеджера справочника, чтобы использовать его повсеместно:


// Функция в модуле менеджера справочника
Функция ПолучитьКорневогоРодителя(ТекущаяСсылка) Экспорт
    
    Если ТекущаяСсылка.Пустая() Тогда
        Возврат Справочники.Номенклатура.ПустаяСсылка();
    КонецЕсли;
    
    ТекущийОбъект = ТекущаяСсылка;
    // Поднимаемся вверх, пока у родителя есть свой родитель
    Пока ЗначениеЗаполнено(ТекущийОбъект.Родитель) Цикл
        ТекущийОбъект = ТекущийОбъект.Родитель;
    КонецЦикла;
    
    Возврат ТекущийОбъект;
    
КонецФункции

На форме элемента в процедуре ПриЧтенииНаСервере мы просто вызываем эту функцию:


&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
    ВерхнийРодитель = Справочники.Номенклатура.ПолучитьКорневогоРодителя(Объект.Ссылка);
КонецПроцедуры

Решение 2: Получение корня через запрос (для отчетов и списков)

Если нам нужно вывести верхнего родителя в списке или в отчете, цикл по иерархии не подойдет из-за низкой производительности. В этом случае мы можем использовать язык запросов. Если глубина иерархии нам известна и она небольшая (например, до 4-х уровней), можно применить конструкцию ВЫБОР КОГДА.

Посмотрим на пример такого запроса для справочника Номенклатура:


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

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

Решение 3: Использование регистра сведений для кэширования

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

Создадим непериодический регистр сведений СвязиКорневыхРодителей с измерениями Элемент и ресурсом КорневойРодитель.

Преимущества этого подхода:

Почему хранение реквизита в самом объекте считается плохой практикой?

Рассмотрим ситуацию с точки зрения архитектуры системы. Допустим, у нас есть папка «Оптовые продажи», в которой находится 50 000 товаров. Эта папка является корневой. В каждом товаре записан реквизит ВерхнийРодитель, ссылающийся на эту папку.

Если пользователь решит переместить папку «Оптовые продажи» внутрь новой папки «Все продажи», то для сохранения логики работы системы нам придется программно перезаписать все 50 000 элементов. Это вызовет:

  1. Длительные блокировки: Другие пользователи не смогут работать с этими элементами, пока идет транзакция.
  2. Рост журнала регистрации: Каждая запись зафиксирует изменения, что увеличит объем базы.
  3. Срабатывание обработчиков: При записи каждого элемента будут запускаться все подписки на события (обмен данными, проверка заполнения, дополнительные расчеты), что может лавинообразно увеличить нагрузку на сервер.

Поэтому специалисты рекомендуют использовать динамический расчет (Решение 1) или вынос данных в отдельный регистр (Решение 3).

Оптимизация с использованием метода Уровень()

Проанализируем еще один способ оптимизации. В платформе 1С у элементов справочника есть встроенный метод Уровень(). Он позволяет быстро понять, насколько глубоко находится элемент. Если Уровень() возвращает 0, значит, элемент уже является корневым. Это можно использовать для сокращения количества обращений к базе данных в итерационных циклах.


Если Объект.Ссылка.Уровень() = 0 Тогда
    ВерхнийРодитель = Объект.Ссылка;
Иначе
    // Запускаем поиск корня только если уровень выше нулевого
    ВерхнийРодитель = Справочники.Номенклатура.ПолучитьКорневогоРодителя(Объект.Ссылка);
КонецЕсли;

Выводы и рекомендации

Подводя итог, выделим основные рекомендации по реализации отображения верхнего родителя:

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

← На главную