Как заполнить документ на основании в обработчиках Конвертации данных, если у объекта еще нет ссылки?

Программист 1С v8.3 (Обычные формы) 1C:Бухгалтерия Бухгалтерский учет IT и автоматизация бизнеса
← На главную

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

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

Выясним причину: почему ПолучитьСсылкуНового() оставляет подчиненный документ пустым

Проанализируем механику работы платформы «1С:Предприятие 8». Метод ПолучитьСсылкуНового() лишь резервирует в оперативной памяти уникальный идентификатор (GUID/UUID) для документа, который еще физически не сохранен в базе данных.

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

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

Рассмотрим рабочие способы обхода этого ограничения платформы.

Способ 1. Использование обработчика «После записи» в ПКО (рекомендуемый вариант)

Наиболее чистый с точки зрения архитектуры способ — перенести логику создания подчиненного документа из обработчика «После загрузки» в обработчик «После записи» правила конвертации объекта (ПКО).

К моменту выполнения обработчика «После записи» основной документ реализации уже сохранен в СУБД. Его ссылка является полноценной, а строки табличных частей физически записаны в таблицы базы и доступны для выполнения запросов обработкой заполнения.

Разберем по шагам алгоритм реализации в обработчике «После записи» ПКО РеализацияТоваровУслуг:

  1. Проверяем, что документ новый (или флаг отсутствия ранее созданного счета) и соответствует условиям отбора.
  2. Создаем документ счета на оплату через менеджер документов.
  3. Устанавливаем режим загрузки данных, чтобы исключить лишние интерактивные проверки и блокировки.
  4. Вызываем штатный метод Заполнить(Объект.Ссылка).
  5. Записываем счет на оплату.
  6. Связываем счет с реализацией и перезаписываем реализацию в режиме обмена данными.

Посмотрим на пример программного кода:


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

Способ 2. Прямое копирование данных из памяти в обработчике «После загрузки»

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

Посмотрим, как реализовать этот подход:


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

Этот способ работает быстрее всего, так как не требует двойной записи документа реализации в СУБД, однако требует аккуратного переноса всех необходимых реквизитов, если структура табличных частей реализации и счета имеет различия.

Способ 3. Принудительная запись объекта реализации в обработчике «После загрузки»

Еще один вариант, который часто обсуждается разработчиками, — принудительный вызов метода Объект.Записать() перед формированием счета непосредственно внутри обработчика «После загрузки».

Проанализируем особенности и риски этого решения:


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

Важное ограничение: механизм обмена КД 2.1 всегда автоматически вызывает метод Объект.Записать() после завершения кода обработчика «После загрузки». Если вызвать запись вручную в теле обработчика, документ запишется в базу данных дважды. Это приводит к двойному срабатыванию подписок на события, удвоению времени обработки каждого пакета и возможному возникновению взаимоблокировок при параллельной загрузке данных. Поэтому при возможности выбора предпочтение всегда следует отдавать Способу 1 или Способу 2.

← На главную