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