Можно ли записать дополнительные сведения для нового документа через ссылку нового в 1С

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

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

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

Проанализируем сначала частую ошибку при вызове методов платформы для получения ссылки. Метод ПолучитьСсылкуНового() не принимает никаких входных параметров. Если попытаться передать в него значение, например ДокументОбъект.ПолучитьСсылкуНового(НоваяСсылка), платформа сразу вернет синтаксическую ошибку.

Для явного назначения ссылки незаписанному объекту в платформе предусмотрен парный метод УстановитьСсылкуНового(<Ссылка>). Важно помнить о следующих особенностях платформы:

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

Почему функция БСП УправлениеСвойствами.ЗаписатьСвойстваУОбъекта не работает с новой ссылкой

Выясним причину аварийного завершения штатной подсистемы «Свойства» из Библиотеки стандартных подсистем (БСП). Разработчики часто пытаются выполнить вызов вида УправлениеСвойствами.ЗаписатьСвойстваУОбъекта(ДокументСсылка, ТаблицаСвойств), передав туда ссылку, полученную через менеджер или метод ссылки нового. Однако данный метод гарантированно завершится ошибкой.

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

Кроме того, подсистема БСП накладывает управляемые транзакционные блокировки на владельца свойств и наборы свойств. Если объект отсутствует в таблицах базы данных, корректная работа механизма блокировок и целостность проверок нарушаются.

Проблема ссылочной целостности

Посмотрим на ситуацию со стороны архитектуры базы данных. Регистр ДополнительныеСведения хранит в измерении Объект ссылку на документ. Если мы запишем набор записей регистра со сгенерированной ссылкой до записи самого документа, а при последующей записи документа произойдет ошибка (например, сработает проверка заполнения, отказ в проведении или блокировка), транзакция документа прервется. В результате в регистре сведений останется запись, ссылающаяся на несуществующий объект — так называемая «битая ссылка». Подобных ситуаций допускать нельзя.

Опасение некоторых разработчиков о «двойной записи документа» при стандартном подходе необоснованно. Документ записывается ровно один раз, а следом за ним обновляется отдельная сущность — регистр сведений.

Способ 1. Запись документа и свойств в единой транзакции (рекомендуемый для БСП)

Разберем классический и наиболее надежный способ работы со стандартными подсистемами. Если необходимо использовать штатные процедуры БСП, сначала записываем сам объект, а затем передаем полученную ссылку в подсистему свойств. Чтобы гарантировать атомарность всей операции, оборачиваем действия в явную транзакцию:


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

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

Способ 2. Передача сведений через ДополнительныеСвойства объекта

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


// В вызывающем коде (в обработке или внешнем алгоритме):
ДокументОбъект.ДополнительныеСвойства.Вставить("ТаблицаДопСведений", ТаблицаСведений);
ДокументОбъект.Записать();

В модуле самого документа в обработчике события ПриЗаписи ссылка объекта уже зафиксирована платформой в текущей транзакции записи:


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

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

Способ 3. Прямая запись набора с установкой ссылки нового (для высоконагруженных обменов)

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


НачатьТранзакцию();
Попытка
    // 1. Генерируем идентификатор и назначаем ссылку объекту
    НоваяСсылка = Документы.ЗаказКлиента.ПолучитьСсылку(Новый УникальныйИдентификатор());
    ДокументОбъект.УстановитьСсылкуНового(НоваяСсылка);
    
    // 2. Формируем набор записей регистра напрямую в обход БСП
    Набор = РегистрыСведений.ДополнительныеСведения.СоздатьНаборЗаписей();
    Набор.Отбор.Объект.Установить(НоваяСсылка);
    
    Запись = Набор.Добавить();
    Запись.Объект = НоваяСсылка;
    Запись.Свойство = НужноеСвойство;
    Запись.Значение = ЗначениеСвойства;
    
    Набор.ОбменДанными.Загрузка = Истина;
    Набор.Записать();
    
    // 3. Записываем сам документ
    ДокументОбъект.ОбменДанными.Загрузка = Истина;
    ДокументОбъект.Записать();
    
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    ВызватьИсключение;
КонецПопытки;

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

← На главную