При переносе структурированных данных из внешних источников или внутренних объектов, таких как ДеревоЗначений, программисты часто сталкиваются с ситуацией, когда элементы создаются «плоским списком». Несмотря на то, что в коде прописано заполнение родителя, иерархия в интерфейсе справочника не отображается. Рассмотрим детально, как правильно организовать этот процесс, разберем типичные ошибки и внедрим лучшие практики разработки на платформе 1С:Предприятие.
Типичная ситуация: данные об объектах строительства хранятся в ДеревоЗначений, где структура уже визуально определена. Задача состоит в том, чтобы перенести эту структуру в справочник ОбъектыСтроительства. Проанализируем фрагмент кода, который часто становится источником проблем:
Если Строка.ПолучитьЭлементы().Количество() > 0 Тогда
ОбъектыСтроительстваЭлемент = Справочники.ОбъектыСтроительства.СоздатьГруппу();
Иначе
ОбъектыСтроительстваЭлемент = Справочники.ОбъектыСтроительства.СоздатьЭлемент();
КонецЕсли;
ОбъектыСтроительстваЭлемент.Наименование = Строка.Наименование;
Если Строка.ПолучитьРодителя() = Неопределено Тогда
ОбъектыСтроительстваЭлемент.Родитель = ""; // Ошибка здесь
Иначе
ОбъектыСтроительстваЭлемент.Родитель = Справочники.ОбъектыСтроительства.НайтиПоНаименованию(Строка.ПолучитьРодителя().Наименование);
КонецЕсли;
ОбъектыСтроительстваЭлемент.Записать();
В этом коде мы видим несколько критических моментов, которые мешают корректному созданию иерархии. Разберем их по порядку и найдем правильные решения.
Первое, на что стоит обратить внимание — это строка ОбъектыСтроительстваЭлемент.Родитель = "". В системе 1С свойство Родитель имеет тип СправочникСсылка. Присвоение пустой строки вместо ссылки является грубой ошибкой. Хотя платформа может попытаться неявно преобразовать типы, это часто приводит к тому, что родитель просто не устанавливается, и элемент оказывается в корне справочника.
Решение: Для очистки родителя или указания на корень справочника необходимо использовать пустую ссылку соответствующего типа. Рассмотрим правильный вариант записи:
ОбъектыСтроительстваЭлемент.Родитель = Справочники.ОбъектыСтроительства.ПустаяСсылка();
Использование метода НайтиПоНаименованию — крайне ненадежный подход. Проанализируем ситуацию: если в дереве значений или в справочнике уже существуют две группы с одинаковым именем (например, «Цех №1» в разных филиалах), метод вернет первую попавшуюся ссылку. В итоге элементы могут «улететь» в совершенно другую ветку иерархии.
Кроме того, если родитель еще не был создан и записан в базу данных, поиск по наименованию ничего не вернет, и иерархия нарушится.
Решение: При обходе дерева значений рекомендуется записывать ссылку на созданную группу непосредственно в колонку дерева. Это позволит избежать лишних поисков и обеспечит 100% точность.
Чтобы иерархия создавалась гарантированно правильно (сначала родитель, потом его подчиненные), лучше всего использовать рекурсию. Рассмотрим по шагам, как реализовать такую процедуру.
Во-первых, создадим процедуру, которая будет принимать на вход коллекцию строк дерева значений и ссылку на родительский узел:
Процедура ЗагрузитьУровеньДерева(СтрокиДерева, РодительСсылка)
Для Каждого СтрокаДерева Из СтрокиДерева Цикл
// Определяем, группа это или элемент
Если СтрокаДерева.Строки.Количество() > 0 Тогда
НовыйОбъект = Справочники.ОбъектыСтроительства.СоздатьГруппу();
Иначе
НовыйОбъект = Справочники.ОбъектыСтроительства.СоздатьЭлемент();
КонецЕсли;
// Заполняем реквизиты
НовыйОбъект.Наименование = СтрокаДерева.Наименование;
НовыйОбъект.Родитель = РодительСсылка;
// Записываем объект, чтобы получить ссылку
НовыйОбъект.Записать();
// Рекурсивно вызываем эту же процедуру для подчиненных строк
Если СтрокаДерева.Строки.Количество() > 0 Тогда
ЗагрузитьУровеньДерева(СтрокаДерева.Строки, НовыйОбъект.Ссылка);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Такой подход гарантирует, что родитель всегда создается раньше своих детей, и мы передаем в подчиненные элементы уже готовую ссылку НовыйОбъект.Ссылка, минуя медленные и ненадежные поиски по наименованию.
Иногда программный код работает идеально, родитель в базе данных заполнен, но в форме списка справочника группы все равно отображаются подряд. Разберем ситуацию: почему так происходит?
Проанализируем основные причины:
СоздатьГруппу().Для того чтобы загрузка данных была профессиональной и безопасной, рассмотрим еще несколько важных аспектов:
1. Использование транзакций. При массовом создании элементов (сотни и тысячи строк) обязательно используйте механизм транзакций. Это значительно ускорит процесс и предотвратит появление «частично загруженных» данных, если в середине процесса произойдет ошибка.
НачатьТранзакцию();
Попытка
ЗагрузитьУровеньДерева(ДеревоДанных.Строки, Справочники.ОбъектыСтроительства.ПустаяСсылка());
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
Сообщить("Ошибка при загрузке: " + ОписаниеОшибки());
КонецПопытки;
2. Проверка уникальности. Перед созданием нового объекта желательно проверить, не был ли он создан ранее. Для этого можно использовать дополнительные реквизиты (например, внешний идентификатор или уникальный код из системы-источника).
3. Метод УстановитьСсылкуНового().
В очень сложных структурах, где связи могут быть перекрестными, можно использовать метод УстановитьСсылкуНового(). Это позволяет получить ссылку на объект еще до его фактической записи в базу данных, что бывает полезно при заполнении сложных взаимосвязей в иерархии.
Подводя итог, можно сказать: секрет правильной иерархии в 1С заключается в строгом соблюдении типов данных (использование ссылок вместо строк) и правильной последовательности создания объектов (от родителя к подчиненным). Использование рекурсии при работе с ДеревоЗначений является «золотым стандартом», позволяющим избежать большинства логических ошибок.