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