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