При программной загрузке иерархических справочников (например, справочника Номенклатура) разработчики часто сталкиваются с ошибкой, указывающей на то, что поле Родитель недоступно для записи. Проанализируем причину возникновения этой проблемы и рассмотрим правильные варианты реализации программной загрузки элементов с несколькими уровнями вложенности.
Выясним причину, почему возникает ошибка вида Поле "Родитель" не доступно для записи. В платформе 1С:Предприятие существует четкое разграничение между ссылкой на элемент (СправочникСсылка) и непосредственно объектом данных (СправочникОбъект).
Объект типа СправочникСсылка предназначен исключительно для чтения данных из базы. Любые попытки изменить его свойства или свойства вышестоящих элементов через обращение по цепочке вида Элм.Родитель.Родитель = НовоеЗначение приведут к ошибке, так как Элм.Родитель возвращает ссылку, а не редактируемый объект.
Для модификации реквизитов любого элемента или группы необходимо сначала получить объект с помощью метода ПолучитьОбъект(), внести изменения и вызвать метод Записать().
Рассмотрим пример кода, в котором была допущена ошибка:
// Попытка напрямую изменить родителя у родителя (через ссылку)
Если ИтогГрупп2.Количество() > 0 Тогда
Элм.Родитель.Родитель = ИтогГрупп2[0].Ссылка; // Ошибка: Элм.Родитель является ссылкой!
Иначе
ГруппаСПР2 = Справочники.Номенклатура.СоздатьГруппу();
ГруппаСПР2.Наименование = Группа2;
ГруппаСПР2.Записать();
Элм.Родитель.Родитель = ГруппаСПР2.Ссылка; // Ошибка!
КонецЕсли;
В приведенном фрагменте присвоение выполняется для свойства Родитель объекта Элм.Родитель. Так как Элм.Родитель — это СправочникСсылка, платформа блокирует запись.
Разберем по шагам, как корректно изменить вышестоящую группу. Чтобы установить родителя для родительской группы, выполним следующую последовательность действий:
ПолучитьОбъект().Родитель этого объекта ссылку на вышестоящую группу.Записать().Рассмотрим исправленный вариант работы с объектом родителя:
// Получаем объект родительской группы из имеющейся ссылки
ОбъектРодителя = Элм.Родитель.ПолучитьОбъект();
Если ОбъектРодителя <> Неопределено Тогда
// Устанавливаем вышестоящего родителя для найденной группы
ОбъектРодителя.Родитель = СсылкаНаВышестоящуюГруппу;
ОбъектРодителя.Записать();
КонецЕсли;
При построении алгоритмов загрузки данных с произвольной или глубокой вложенностью рекомендуется использовать более универсальные подходы, чтобы избежать жесткой привязки к вызовам вида Родитель.Родитель.
1. Поиск группы с учетом родителя
Если в справочнике могут существовать папки с одинаковыми названиями в разных ветках, поиск по одному лишь наименованию вернет неверную группу. В запросе или при использовании метода НайтиПоНаименованию() всегда следует указывать родительскую группу предыдущего уровня:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.ЭтоГруппа = ИСТИНА
| И Номенклатура.Наименование = &Наименование
| И Номенклатура.Родитель = &Родитель";
Запрос.УстановитьПараметр("Наименование", ИмяТекущейГруппы);
Запрос.УстановитьПараметр("Родитель", РодительВерхнегоУровня);
Результат = Запрос.Выполнить();
2. Последовательное создание структуры сверху вниз
Наиболее надежный способ — последовательное создание или поиск групп по цепочке, начиная от самого верхнего корневого уровня к нижнему:
СправочникСсылка.Номенклатура.ПустаяСсылка()). При отсутствии — создаем ее и сохраняем ссылку.Родитель для самого элемента номенклатуры.Такой подход полностью исключает необходимость редактирования уже привязанных родителей «снизу вверх» и защищает код от ошибок доступа на запись.