Как правильно использовать УстановитьСсылкуНового при копировании объектов и назначении владельца

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

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

Суть проблемы и механизм работы со ссылками

Когда мы создаем новый объект с помощью метода СоздатьЭлемент() или Скопировать(), он существует только в оперативной памяти. У него еще нет постоянной ссылки в базе данных. Если нам нужно создать подчиненный элемент (например, единицу измерения для новой номенклатуры) до того, как основной объект будет записан, нам необходимо "зарезервировать" ссылку для основного объекта. Для этого используется метод УстановитьСсылкуНового().

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

Особенности метода Скопировать() и установка ссылки

При использовании метода Скопировать() объект получает признак ЭтоНовый() = Истина, однако его внутренняя структура уже частично заполнена данными образца. Рассмотрим правильную последовательность действий для назначения ссылки. Проанализируем пример кода:


// Получаем объект-копию
НовыйЭлемент = ТекДанные.Ссылка.Скопировать();

// Генерируем новый уникальный идентификатор и создаем пустую ссылку
СсылкаНового = Справочники.Номенклатура.ПолучитьСсылку(Новый УникальныйИдентификатор());

// Устанавливаем эту ссылку для нашего нового (еще не записанного) объекта
НовыйЭлемент.УстановитьСсылкуНового(СсылкаНового);

// Теперь мы можем использовать СсылкаНового как владельца для других объектов

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

Проблема валидации владельца при записи подчиненных объектов

Даже если мы успешно установили ссылку нового объекта, при попытке записать подчиненный элемент (например, справочник "ЕдиницыИзмерения"), в поле Владелец которого подставлена эта ссылка, мы можем столкнуться с ошибкой. Система 1С выполняет проверку ссылочной целостности: она видит, что объект, указанный в поле Владелец, физически отсутствует в таблицах базы данных. Рассмотрим, как выглядит проблемный код:


НоваяЕдиница = Справочники.ЕдиницыИзмерения.СоздатьЭлемент();
НоваяЕдиница.Владелец = СсылкаНового; // Ссылка установлена, но объекта в БД еще нет
НоваяЕдиница.УстановитьНовыйКод();
ЗаполнитьЗначенияСвойств(НоваяЕдиница, СтруктураДанныхЕдиницы);

// Здесь может возникнуть ошибка "Объект не найден" или нарушение прав
НоваяЕдиница.Записать(); 

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

Решение через ОбменДанными.Загрузка

Для того чтобы "убедить" платформу пропустить проверку существования владельца, мы можем использовать свойство ОбменДанными.Загрузка. Установка этого свойства в значение Истина отключает большинство прикладных проверок в модулях объекта и, что более важно, некоторые системные проверки ссылочной целостности на уровне записи в БД.

Рассмотрим доработанный и корректный алгоритм:


// 1. Создаем ссылку для будущего объекта
НоваяСсылкаНоменклатуры = Справочники.Номенклатура.ПолучитьСсылку();

// 2. Подготавливаем объект номенклатуры
НовыйОбъектНоменклатура = СсылкаОбразец.Скопировать();
НовыйОбъектНоменклатура.УстановитьСсылкуНового(НоваяСсылкаНоменклатуры);

// 3. Создаем подчиненный объект
НоваяЕдиница = Справочники.ЕдиницыИзмерения.СоздатьЭлемент();
НоваяЕдиница.Владелец = НоваяСсылкаНоменклатуры;
НоваяЕдиница.ОбменДанными.Загрузка = Истина; // Ключевой момент: отключаем проверки
НоваяЕдиница.Записать();

// 4. Записываем основной объект
НовыйОбъектНоменклатура.Записать();

Посмотрим на результат: использование ОбменДанными.Загрузка = Истина позволяет записать подчиненный элемент, который временно ссылается на "пустое место". Как только мы вызываем НовыйОбъектНоменклатура.Записать(), зарезервированная ссылка материализуется в базе данных, и целостность восстанавливается.

Рекомендации по стабильной работе

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

  1. Используйте переменную ссылки напрямую: Не полагайтесь на метод ПолучитьСсылкуНового(). Если вы сами создали GUID и передали его в УстановитьСсылкуНового(), сохраните это значение в переменную и используйте её для заполнения реквизитов других объектов.
  2. Соблюдайте транзакционность: Всегда оборачивайте создание владельца и подчиненных объектов в одну транзакцию (НачатьТранзакцию() ... ЗафиксироватьТранзакцию()). Это гарантирует, что если запись основного объекта сорвется, в базе не останется "сиротских" записей подчиненных справочников с битыми ссылками.
  3. Ограничения ОбменДанными.Загрузка: Помните, что при включении этого режима не будут выполнены ваши проверки в процедурах ПередЗаписью. Если там содержится важная бизнес-логика (например, формирование наименования по шаблону), её придется вызвать принудительно или продублировать.
  4. Кэширование в обычных формах: В обычном приложении (ОФ) иногда наблюдаются странности с обновлением состояния объекта. Если код ведет себя нестабильно (как упоминалось в теме — "заработало на 6-й раз"), это часто связано с контекстом выполнения. Убедитесь, что работа с объектом происходит в четкой последовательности без лишних серверных вызовов между установкой ссылки и записью.

Альтернативный подход: ДополнительныеСвойства

Если использование ОбменДанными.Загрузка кажется вам слишком рискованным (например, из-за сложной логики в подписках на события), существует альтернативный метод. Вы можете не записывать подчиненные объекты сразу, а поместить их (или данные для их создания) в структуру ДополнительныеСвойства объекта-владельца.

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

Подведем итог: использование УстановитьСсылкуНового в сочетании с ОбменДанными.Загрузка является рабочим инструментом платформы 1С, позволяющим гибко управлять процессом создания связанных данных, если соблюдать осторожность при работе с транзакциями и проверками целостности.

← На главную