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