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

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

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

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

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

Выясняем причину: почему пропадают ссылки?

Для того чтобы понять природу ошибки, проанализируем, как механизм Универсального обмена данными в формате XML обрабатывает выгрузку объектов. Когда мы передаем таблицу значений в параметр ВыборкаДанных в обработчике "Перед обработкой" ПВД, платформа начинает построчно обходить эту таблицу.

Каждая строка нашей таблицы значений выступает в качестве объекта-источника для Правила Конвертации Объектов (ПКО). В ПКО для свойств (ПКС) указаны правила конвертации их типов (например, для склада — ПКО "Склады", для номенклатуры — ПКО "Номенклатура").

Проблема кроется в следующих ключевых моментах:

  1. Отсутствие идентификатора источника. Для успешной выгрузки любого ссылочного объекта системе конвертации необходима уникальная ссылка на сам объект-источник. Если в нашей таблице значений нет колонки Ссылка, КД не может получить уникальный идентификатор (GUID) выгружаемой сущности. Без этого идентификатора КД 2.1 не в состоянии корректно сформировать узел ссылки для главного документа в XML, что лавинообразно ломает выгрузку подчиненных ссылочных реквизитов.
  2. Проблема дублирования строк при выгрузке ТЗ. Для платформы все строки таблицы значений без уникального ключа выглядят одинаково с точки зрения внутренней сериализации (функция ЗначениеВСтрокуВнутр(Источник) возвратит одинаковые данные). Из-за этого механизм обмена может выгрузить только самую первую строку таблицы, посчитав все последующие строки дублирующимися объектами.
  3. Конфликт в обработчиках. Попытка прописать в обработчике "Перед выгрузкой" ПКО код типа КлючВыгружаемыхДанных = Источник.Ссылка приводит к ошибке "Значение не является значением объектного типа (Ссылка)", так как у строки нашей таблицы значений физически отсутствует свойство с таким именем.

Давайте подробно разберем два метода решения этой проблемы: от простой доработки текущей ТЗ до перехода на профессиональный стандарт выгрузки без источника.

Способ 1. Модернизация таблицы значений и добавление колонки "Ссылка"

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

Во-первых, в ПВД в обработчике "Перед обработкой" при инициализации таблицы значений мы должны обязательно добавить колонку Ссылка с соответствующим типом документа-источника. Во-вторых, так как объект создается виртуально и в реальной базе его нет, мы сгенерируем для него уникальную ссылку с помощью нового GUID.

Проанализируем пример скорректированного кода в обработчике "Перед обработкой" ПВД:


// Инициализируем таблицу значений для выгрузки данных
ТЗ = Новый ТаблицаЗначений;
// ОБЯЗАТЕЛЬНО: Добавляем колонку "Ссылка" с типом выгружаемого документа
ТЗ.Колонки.Добавить("Ссылка", Новый ОписаниеТипов("ДокументСсылка.ОприходованиеТоваров"));
ТЗ.Колонки.Добавить("Дата", Новый ОписаниеТипов("Дата"));
ТЗ.Колонки.Добавить("ВидОперации", Новый ОписаниеТипов("ПеречислениеСсылка.ВидыОперацийОприходованиеТоваров"));
ТЗ.Колонки.Добавить("Организация", Новый ОписаниеТипов("СправочникСсылка.Организации"));
ТЗ.Колонки.Добавить("Ответственный", Новый ОписаниеТипов("СправочникСсылка.Пользователи"));
ТЗ.Колонки.Добавить("ОтражатьВБухгалтерскомУчете");
ТЗ.Колонки.Добавить("ОтражатьВУправленческомУчете");
ТЗ.Колонки.Добавить("Склад", Новый ОписаниеТипов("СправочникСсылка.Склады"));
ТЗ.Колонки.Добавить("Товары");

// Добавляем новую строку и заполняем ее
НоваяСтрока = ТЗ.Добавить();

// Генерируем новую виртуальную ссылку для нашего документа
НоваяСтрока.Ссылка = Документы.ОприходованиеТоваров.ПолучитьСсылку(Новый УникальныйИдентификатор());

НоваяСтрока.Дата = КонецДня(Параметры.Дата);
НоваяСтрока.ВидОперации = Перечисления.ВидыОперацийОприходованиеТоваров.ТоварыПродукция;
НоваяСтрока.Организация = Справочники.Организации.НайтиПоКоду("000000001");
НоваяСтрока.Ответственный = Справочники.Пользователи.НайтиПоНаименованию("Автообмен");
НоваяСтрока.ОтражатьВБухгалтерскомУчете = Ложь;
НоваяСтрока.ОтражатьВУправленческомУчете = Истина;
НоваяСтрока.Склад = Параметры.Склад;

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

// Запрос для получения остатков товаров на складах
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ТоварыНаСкладахОстатки.Номенклатура.ЕдиницаХраненияОстатков КАК ЕдиницаИзмерения,
    |	ТоварыНаСкладахОстатки.Качество КАК Качество,
    |	1 КАК Коэффициент,
    |	ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
    |	ТоварыНаСкладахОстатки.КоличествоОстаток КАК Количество,
    |	1 КАК Цена,
    |	ЗНАЧЕНИЕ(Перечисление.СтавкиНДС.НДС20) КАК СтавкаНДСВРознице
    |ИЗ
    |	РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаКон, Склад = &Склад) КАК ТоварыНаСкладахОстатки";

Запрос.УстановитьПараметр("ДатаКон", КонецДня(Параметры.Дата));
Запрос.УстановитьПараметр("Склад", Параметры.Склад);

Результат = Запрос.Выполнить();    
Выборка = Результат.Выбрать();

Пока Выборка.Следующий() Цикл
    НоваяСтрокаТЧ = НоваяСтрока.Товары.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяСтрокаТЧ, Выборка);
КонецЦикла;

// Передаем заполненную таблицу значений в выборку данных
ВыборкаДанных = ТЗ;

Теперь перейдем в настройки правил конвертации объектов (ПКО) для нашего документа. В обработчике события "Перед выгрузкой" ПКО напишем следующий код:


// Указываем КД использовать сгенерированную нами виртуальную ссылку в качестве ключа
КлючВыгружаемыхДанных = Источник.Ссылка;

Благодаря этому КД 2.1 поймет, какую ссылку использовать для идентификации документа. При формировании XML-файла узел документа получит корректный уникальный идентификатор, а все связанные ПКС (правила конвертации свойств) смогут штатно выгрузить ссылочные типы данных (Организацию, Номенклатуру и Склад).

Также важно проверить настройки самого ПКО: для ПКО должен быть установлен признак "Не запоминать выгруженные объекты", если мы используем произвольный алгоритм выборки, во избежание накопления кэша ссылок.

Способ 2. Использование функции "ВыгрузитьПоПравилу" и входящих данных (Рекомендуемый подход)

Хотя первый метод полностью работоспособен, использование ТЗ в качестве ВыборкаДанных при создании виртуальных документов часто признается разработчиками избыточным и подверженным скрытым ошибкам платформы. Более элегантным, чистым и профессиональным решением является ручной вызов выгрузки с помощью встроенного метода КД ВыгрузитьПоПравилу.

При таком подходе нам вообще не требуется создавать искусственную колонку Ссылка в источнике. Мы просто передаем все собранные данные через специальную структуру ВходящиеДанные, а у ПКО отключаем привязку к физическому объекту-источнику.

Проанализируем этот алгоритм подробно по шагам.

Шаг 1. Настройка ПКО в информационной базе Конвертации данных

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

Шаг 2. Написание алгоритма в ПВД "Перед обработкой"

В правиле выгрузки данных мы полностью отказываемся от заполнения ВыборкаДанных. Вместо этого мы пишем код прямого обхода выборки и вызова функции генерации XML-данных:


// Выполняем запрос для получения данных табличной части
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ТоварыНаСкладахОстатки.Номенклатура.ЕдиницаХраненияОстатков КАК ЕдиницаИзмерения,
    |	ТоварыНаСкладахОстатки.Качество КАК Качество,
    |	1 КАК Коэффициент,
    |	ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
    |	ТоварыНаСкладахОстатки.КоличествоОстаток КАК Количество,
    |	1 КАК Цена,
    |	ЗНАЧЕНИЕ(Перечисление.СтавкиНДС.НДС20) КАК СтавкаНДСВРознице
    |ИЗ
    |	РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаКон, Склад = &Склад) КАК ТоварыНаСкладахОстатки";

Запрос.УстановитьПараметр("ДатаКон", КонецДня(Параметры.Дата));
Запрос.УстановитьПараметр("Склад", Параметры.Склад);

Результат = Запрос.Выполнить();    
Выборка = Результат.Выбрать();

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

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

// Заполняем таблицу значений строками из выборки запроса
Пока Выборка.Следующий() Цикл
    НоваяСтрока = ТаблицаТовары.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяСтрока, Выборка);
КонецЦикла;

// Добавляем таблицу товаров в общую структуру входящих данных
ВходящиеДанные.Вставить("Товары", ТаблицаТовары);

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

// Отменяем стандартную обработку ПВД, чтобы система не пыталась выгружать пустую ВыборкаДанных
Отказ = Истина;

Рассмотрим преимущества этого метода:

Резюме

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

← На главную