При разработке правил конвертации данных в программе «Конвертация данных 2.1» (КД 2.1) специалисты часто сталкиваются с ситуацией, когда правила отлично работают при ручном тестировании через обработку УниверсальныйОбменДаннымиXML в файловом режиме, но выдают критические ошибки при запуске в реальной базе через Планы обмена (в режиме синхронизации данных). Сегодня мы детально проанализируем одну из таких сложных архитектурных задач: перенос информации из нестандартного независимого регистра сведений источника в регистр сведений дополнительных свойств приемника (ЗначенияСвойствОбъектов), разберем причины возникающих ошибок и построим надежное решение.
Представим сценарий: в базе-источнике существует независимый непериодический регистр сведений (например, КомплектыИКоллекции) со следующей структурой:
Номенклатура (Ссылка на справочник Номенклатура);Комплект (Ссылка на справочник Комплекты);Коллекция (Ссылка на справочник Коллекции).В базе-приемнике аналогичные сущности (Комплекты и Коллекции) хранятся не в самом регистре или реквизитах номенклатуры, а в виде дополнительных свойств (характеристик) в типовом регистре сведений ЗначенияСвойствОбъектов. Этот регистр имеет другую структуру:
Объект (Ссылка на Номенклатуру);Свойство (Ссылка на План видов характеристик ДополнительныеРеквизитыИСведения);Значение (Характеристика, принимающая значения соответствующих справочников).Наша главная задача — при изменении одной записи в источнике сформировать и перенести две разные записи в приемнике: одну для свойства «Коллекция», другую — для свойства «Комплект». При этом обмен должен выполняться автоматически через План обмена по мере регистрации изменений.
---
Рассмотрим, почему при использовании планов обмена возникает ошибка обращения к реквизитам. При интерактивной работе через обработку выгрузки КД 2.1 обходит регистр сведений построчно. В обработчик ПередВыгрузкой Правила выгрузки данных (ПВД) передается конкретная строка выборки (объект типа РегистрСведенийКлючЗаписи или строка таблицы значений), где напрямую доступны свойства Коллекция, Комплект и Номенклатура.
Однако при обмене по Планам обмена регистрация изменений для независимых регистров сведений происходит не посимвольно или построчно, а наборами записей. В этот момент платформа регистрирует объект типа РегистрСведенийНаборЗаписей. Когда механизм обмена пытается выполнить правила выгрузки для этого зарегистрированного изменения, в обработчик в качестве источника (параметр Объект или Источник) передается именно РегистрСведенийНаборЗаписей.
Поскольку у самого набора записей нет реквизитов Коллекция или Комплект (они принадлежат конкретным строкам внутри этого набора), обращение вида Объект.Коллекция приводит к аварийному завершению выгрузки с ошибкой «Поле объекта не обнаружено».
Решение проблемы: Всегда обходить переданный объект в цикле, так как в условиях планов обмена мы гарантированно работаем с коллекцией (набором) строк:
Для Каждого Запись Из Источник Цикл
// Здесь мы уже работаем с конкретной строкой набора
ИмяКоллекции = Запись.Коллекция;
КонецЦикла;
---
Эта ошибка происходит на стороне базы-приемника во время загрузки данных XML. Причиной становится использование функции ВыгрузитьПоПравилу(), когда в качестве приемника указывается одиночная запись регистра сведений. В процессе разбора XML-пакета универсальный механизм БСП (Библиотеки стандартных подсистем) выполняет чтение и пытается записать объект. Чтобы временно отключить платформенные триггеры регистрации изменений при обмене, БСП выполняет стандартную операцию:
Объект.ДополнительныеСвойства.Вставить("ОтключитьМеханизмРегистрацииОбъектов", Истина);
Если данные были выгружены через ПКО для одиночной записи регистра, то при чтении XML инициализируется объект типа РегистрСведенийМенеджерЗаписи. У этого объекта в платформе 1С:Предприятие отсутствует свойство ДополнительныеСвойства. В результате загрузка прерывается с критической системной ошибкой.
Решение проблемы: Для корректной работы с регистрами сведений в рамках обмена БСП необходимо использовать исключительно наборы записей. Вместо вызова ВыгрузитьПоПравилу() мы должны использовать специализированную функцию КД 2.1 — ВыгрузитьРегистр(). Эта функция правильно упаковывает выгружаемые данные в XML-контейнер набора записей (РегистрСведенийНаборЗаписей), у которого свойство ДополнительныеСвойства присутствует по умолчанию.
---
Проанализируем ситуацию и настроим правила конвертации таким образом, чтобы они без сбоев работали при обмене по планам обмена.
При обмене через планы обмена правила выгрузки данных (ПВД) со стандартной обработкой не играют ключевой роли, так как выборка данных идет по зарегистрированным изменениям. Более того, произвольные алгоритмы в ПВД могут некорректно инициализироваться. Мы создадим одно ПКО для исходного регистра, которое будет обрабатывать наборы записей, а ПВД создавать для него не обязательно — механизм обмена сам определит нужное ПКО по типу метаданных зарегистрированного изменения.
Создадим новое Правило конвертации объектов (ПКО) со следующими настройками:
КомплектыИКоллекции_Набор;РегистрСведенийНаборЗаписей.КомплектыИКоллекции;В созданном ПКО перейдем в обработчик события ПередВыгрузкой. Здесь мы напишем код, который обойдет все строки набора, сформирует для каждой строки структуры отбора и записей для двух разных свойств, и принудительно выгрузит их как полноценные наборы записей для регистра ЗначенияСвойствОбъектов.
Рассмотрим подробнее пример этого кода:
// Проверим, что Источник не пустой и содержит записи
Если Источник <> Неопределено Тогда
Для Каждого Запись Из Источник Цикл
// ==========================================
// 1. ВЫГРУЗКА ДОП. СВОЙСТВА "КОЛЛЕКЦИЯ"
// ==========================================
Если ЗначениеЗаполнено(Запись.Коллекция) Тогда
// Создаем таблицы для передачи структуры Набора Записей в ВыгрузитьРегистр()
ТаблицаОтбора = Новый ТаблицаЗначений;
ТаблицаОтбора.Колонки.Добавить("Имя");
ТаблицаОтбора.Колонки.Добавить("Значение");
ТаблицаОтбора.Колонки.Добавить("Использование");
ТаблицаСтрок = Новый ТаблицаЗначений;
ТаблицаСтрок.Колонки.Добавить("Объект");
ТаблицаСтрок.Колонки.Добавить("Свойство");
ТаблицаСтрок.Колонки.Добавить("Значение");
// Настраиваем отбор набора записей в приемнике (по Объекту и Свойству)
СтрокаОтбора = ТаблицаОтбора.Добавить();
СтрокаОтбора.Имя = "Объект";
СтрокаОтбора.Значение = Запись.Номенклатура;
СтрокаОтбора.Использование = Истина;
СтрокаОтбора = ТаблицаОтбора.Добавить();
СтрокаОтбора.Имя = "Свойство";
СтрокаОтбора.Значение = "00125"; // Код свойства "Коллекция" в базе-приемнике
СтрокаОтбора.Использование = Истина;
// Добавляем саму строку записи в набор
НоваяСтрока = ТаблицаСтрок.Добавить();
НоваяСтрока.Объект = Запись.Номенклатура;
НоваяСтрока.Свойство = "00125";
НоваяСтрока.Значение = Запись.Коллекция;
// Упаковываем все в структуру параметров для выгрузки регистра
СтруктураРегистра = Новый Структура("Отбор, Строки", ТаблицаОтбора, ТаблицаСтрок);
// Выгружаем данные через встроенную функцию ВыгрузитьРегистр()
// "ЗначенияСвойствОбъектов" - это имя ПКО регистра-приемника
ВыгрузитьРегистр(СтруктураРегистра, , , Истина, "ЗначенияСвойствОбъектов");
КонецЕсли;
// ==========================================
// 2. ВЫГРУЗКА ДОП. СВОЙСТВА "КОМПЛЕКТ"
// ==========================================
Если ЗначениеЗаполнено(Запись.Комплект) Тогда
ТаблицаОтбора = Новый ТаблицаЗначений;
ТаблицаОтбора.Колонки.Добавить("Имя");
ТаблицаОтбора.Колонки.Добавить("Значение");
ТаблицаОтбора.Колонки.Добавить("Использование");
ТаблицаСтрок = Новый ТаблицаЗначений;
ТаблицаСтрок.Колонки.Добавить("Объект");
ТаблицаСтрок.Колонки.Добавить("Свойство");
ТаблицаСтрок.Колонки.Добавить("Значение");
СтрокаОтбора = ТаблицаОтбора.Добавить();
СтрокаОтбора.Имя = "Объект";
СтрокаОтбора.Значение = Запись.Номенклатура;
СтрокаОтбора.Использование = Истина;
СтрокаОтбора = ТаблицаОтбора.Добавить();
СтрокаОтбора.Имя = "Свойство";
СтрокаОтбора.Значение = "00166"; // Код свойства "Комплект" в базе-приемнике
СтрокаОтбора.Использование = Истина;
НоваяСтрока = ТаблицаСтрок.Добавить();
НоваяСтрока.Объект = Запись.Номенклатура;
НоваяСтрока.Свойство = "00166";
НоваяСтрока.Значение = Запись.Комплект;
СтруктураРегистра = Новый Структура("Отбор, Строки", ТаблицаОтбора, ТаблицаСтрок);
ВыгрузитьРегистр(СтруктураРегистра, , , Истина, "ЗначенияСвойствОбъектов");
КонецЕсли;
КонецЦикла;
КонецЕсли;
// Критически важная строка!
// Отменяем стандартную автоматическую выгрузку самого ПКО,
// так как мы переопределили выгрузку вручную в цикле.
Отказ = Истина;
Теперь создадим ПКО для самого регистра дополнительных свойств в приемнике:
ЗначенияСвойствОбъектов;ВыгрузитьРегистр());РегистрСведенийНаборЗаписей.ЗначенияСвойствОбъектов.В табличной части Правил конвертации свойств (ПКС) для этого правила добавьте три строки:
Объект (укажите правило конвертации для справочника Номенклатура);Свойство (укажите правило конвертации для Плана видов характеристик);Значение (поскольку значения могут быть разных типов, КД автоматически определит тип на основе ПКО, указанных в настройках).Для каждого из этих трех ПКС обязательно установите флаг «Получать из входящих данных». Это укажет системе, что значения реквизитов нужно брать из структуры ТаблицаСтрок, которую мы сформировали на Шаге 3.
Обратите внимание: на Шаге 3 в качестве значения измерения Свойство мы передали обычную текстовую строку с кодом элемента ПВХ (например, "00125"). Чтобы база-приемник правильно сопоставила эту строку с реальным элементом базы данных, в ПКО для Плана видов характеристик (который используется для конвертации свойства) в настройках поиска укажите способ поиска: «По коду».
---
Проанализируем результаты проделанной работы. Использование предложенной архитектуры дает несколько весомых преимуществ:
РегистрСведенийНаборЗаписей.ВыгрузитьРегистр() гарантирует, что на стороне загрузки будет сформирован набор записей, у которого есть свойство ДополнительныеСвойства, что предотвращает появление системных сбоев при чтении XML.Мы успешно решили сложную задачу трансформации данных КД 2.1 в условиях работы с планами обмена БСП. Описанный шаблон проектирования правил выгрузки можно использовать для любых аналогичных задач конвертации «один ко многим» для независимых регистров сведений.