Как правильно создать иерархию групп и элементов в справочнике 1С при загрузке из дерева значений

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

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

Анализ исходной задачи и кода

Типичная ситуация: данные об объектах строительства хранятся в ДеревоЗначений, где структура уже визуально определена. Задача состоит в том, чтобы перенести эту структуру в справочник ОбъектыСтроительства. Проанализируем фрагмент кода, который часто становится источником проблем:


Если Строка.ПолучитьЭлементы().Количество() > 0 Тогда 
    ОбъектыСтроительстваЭлемент = Справочники.ОбъектыСтроительства.СоздатьГруппу(); 
Иначе 
    ОбъектыСтроительстваЭлемент = Справочники.ОбъектыСтроительства.СоздатьЭлемент(); 
КонецЕсли;

ОбъектыСтроительстваЭлемент.Наименование = Строка.Наименование;

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

ОбъектыСтроительстваЭлемент.Записать();

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

Проблема 1: Типизация родителя и пустые ссылки

Первое, на что стоит обратить внимание — это строка ОбъектыСтроительстваЭлемент.Родитель = "". В системе 1С свойство Родитель имеет тип СправочникСсылка. Присвоение пустой строки вместо ссылки является грубой ошибкой. Хотя платформа может попытаться неявно преобразовать типы, это часто приводит к тому, что родитель просто не устанавливается, и элемент оказывается в корне справочника.

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

ОбъектыСтроительстваЭлемент.Родитель = Справочники.ОбъектыСтроительства.ПустаяСсылка();

Проблема 2: Поиск родителя по наименованию

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

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

Решение: При обходе дерева значений рекомендуется записывать ссылку на созданную группу непосредственно в колонку дерева. Это позволит избежать лишних поисков и обеспечит 100% точность.

Оптимальный алгоритм: Рекурсивный обход дерева

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

Во-первых, создадим процедуру, которая будет принимать на вход коллекцию строк дерева значений и ссылку на родительский узел:


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

Такой подход гарантирует, что родитель всегда создается раньше своих детей, и мы передаем в подчиненные элементы уже готовую ссылку НовыйОбъект.Ссылка, минуя медленные и ненадежные поиски по наименованию.

Проблема 3: Настройки отображения в интерфейсе

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

Проанализируем основные причины:

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

Дополнительные рекомендации для повышения надежности

Для того чтобы загрузка данных была профессиональной и безопасной, рассмотрим еще несколько важных аспектов:

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


НачатьТранзакцию();
Попытка
    ЗагрузитьУровеньДерева(ДеревоДанных.Строки, Справочники.ОбъектыСтроительства.ПустаяСсылка());
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    Сообщить("Ошибка при загрузке: " + ОписаниеОшибки());
КонецПопытки;

2. Проверка уникальности. Перед созданием нового объекта желательно проверить, не был ли он создан ранее. Для этого можно использовать дополнительные реквизиты (например, внешний идентификатор или уникальный код из системы-источника).

3. Метод УстановитьСсылкуНового(). В очень сложных структурах, где связи могут быть перекрестными, можно использовать метод УстановитьСсылкуНового(). Это позволяет получить ссылку на объект еще до его фактической записи в базу данных, что бывает полезно при заполнении сложных взаимосвязей в иерархии.

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

← На главную