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