Как правильно создать зависимый объект и записать ссылку на него в реквизит при записи в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Ограничения событийной модели и частые ошибки

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

  1. Попытка изменения реквизитов в событии ПриЗаписи или ОбработкаПроведения: На этапе обработчиков ПриЗаписи и ОбработкаПроведения данные текущего объекта уже сериализованы и записаны в таблицы базы данных. Если изменить реквизит через ЭтотОбъект.ИмяРеквизита = СсылкаНаНовыйОбъект внутри этих процедур, новое значение не зафиксируется в базе автоматически. Потребуется вызов метода ЭтотОбъект.Записать(), что приводит к рекурсивному входу в обработчики, блокировкам таблиц или зацикливанию.
  2. Создание ссылочных объектов при проведении: Процедура ОбработкаПроведения предназначена исключительно для формирования движений по регистрам. Создание справочников или новых документов внутри проведения нарушает стандарты разработки, затягивает время удержания исключительных блокировок и провоцирует взаимные блокировки (deadlocks).
  3. Опасения по поводу целостности данных: Платформа 1С выполняет события записи объекта внутри неявной системной транзакции. Если при выполнении обработчика ПередЗаписи создается сторонний объект, а затем в ПриЗаписи происходит ошибка или устанавливается параметр Отказ = Истина, транзакция целиком откатывается. Созданный объект не останется «висеть» в базе данных.

Основное решение: создание зависимого объекта в обработчике «ПередЗаписью»

Рассмотрим стандартный и наиболее безопасный вариант реализации. Создание, заполнение и запись зависимого объекта выполняются непосредственно в модуле объекта внутри процедуры ПередЗаписью до того, как платформа зафиксирует изменения текущего объекта.

Разберем пример реализации в модуле объекта:


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

Посмотрим на порядок выполнения операций: создание вспомогательного элемента и присвоение ссылки происходят до фиксации текущего объекта. Платформа штатно сохраняет реквизит СвязанныйОбъект вместе со всеми остальными полями в рамках одной транзакции.

Решение проблемы взаимных циклических ссылок

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

Разберем пример с перекрестной привязкой объектов:


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

Альтернативные архитектурные решения

Если специфика бизнес-логики не требует обязательного хранения реквизита в самом объекте, проанализируем альтернативные подходы:

  1. Инверсия направления связи: Ссылка хранится не в основном объекте, а в подчиненном. В таком случае зависимый объект создается в обработчике ПриЗаписи, когда у основного объекта уже гарантированно сформирована ссылка (ЭтотОбъект.Ссылка).
  2. Использование непериодического регистра сведений: Создается связующий регистр сведений с измерениями ОсновнойОбъект и ЗависимыйОбъект. Запись набора записей регистра выполняется в событиях ПриЗаписи или ОбработкаПроведения, что исключает необходимость изменения реквизитов основного объекта.
  3. Вынесение тяжелых операций после записи: Если создание зависимого объекта требует обращения к внешним веб-сервисам или генерации файлов, операцию выносят в обработчик формы ПослеЗаписиНаСервере или в фоновое задание, чтобы сократить длительность транзакции базы данных.
← На главную