Перенос данных между регистрами с разной структурой — одна из классических задач при работе с конфигурацией «1С:Конвертация данных 2.1» (КД 2.1). Особенно часто возникают сложности при попытке перегрузить данные из подчиненного регистра (где записи жестко привязаны к регистратору) в независимый регистр. Проблема заключается в том, что по умолчанию система пытается выгружать записи Источника как отдельные объекты РегистрСведенийЗапись, в то время как Получатель ожидает формат РегистрСведенийНаборЗаписей для корректной записи в базу.
В этой статье мы подробно разберем, как с помощью программного кода в правилах выгрузки данных (ПВД) сформировать нужную структуру и успешно выполнить перенос, используя встроенные механизмы конвертации.
Проанализируем ситуацию: в Источнике записи регистра существуют только в контексте документа-регистратора. Когда мы настраиваем обычное правило выгрузки, КД пытается сопоставить поля. Однако для независимого регистра в Получателе критически важен отбор по измерениям. Если просто передать набор полей, загрузка может пройти некорректно: данные могут не записаться, либо затереть существующие записи, которые не должны были пострадать. Нам необходимо заставить конвертацию сформировать XML-пакет, который выглядит как полноценный набор записей с установленным отбором.
Для решения этой задачи воспользуемся произвольным алгоритмом в ПВД. Рассмотрим пошагово, как реализовать этот механизм. Нам потребуется имитировать структуру набора записей, которую ожидает платформа 1С при загрузке.
Разберем пример кода, который необходимо разместить в обработчике «Перед выгрузкой» в Правилах выгрузки данных (ПВД) для конкретного регистра. Идея состоит в том, чтобы создать «виртуальную» структуру, заполнить её данными из Источника и передать в функцию ВыгрузитьРегистр.
// 1. Формируем искусственную структуру-шаблон, аналогичную РегистрСведенийНаборЗаписей
Строки = Новый ТаблицаЗначений;
Строки.Колонки.Добавить("Период");
Строки.Колонки.Добавить("Номенклатура");
Строки.Колонки.Добавить("ФорматМагазина");
Строки.Колонки.Добавить("РазрешеныЗакупки");
Строки.Колонки.Добавить("РазрешеныПродажи");
Строки.Колонки.Добавить("Активность");
Строки.Колонки.Добавить("Документ");
// Добавляем пустую строку, которую будем заполнять в цикле
Строки.Добавить();
// 2. Создаем таблицу отбора. В Получателе это определит, какие записи будут замещены.
Отбор = Новый ТаблицаЗначений;
Отбор.Колонки.Добавить("Имя");
Отбор.Колонки.Добавить("Значение");
Отбор.Колонки.Добавить("Использование");
СтрокаОтбора = Отбор.Добавить();
СтрокаОтбора.Имя = "Номенклатура";
СтрокаОтбора.Использование = Истина;
СтрокаОтбора = Отбор.Добавить();
СтрокаОтбора.Имя = "ФорматМагазина";
СтрокаОтбора.Использование = Истина;
// 3. Собираем итоговый шаблон
ШаблонНЗ = Новый Сруктура("Отбор, Строки");
ШаблонНЗ.Отбор = Отбор;
ШаблонНЗ.Строки = Строки;
// 4. Основной цикл по выгружаемым данным
// Объект в данном контексте — это выборка записей из Источника
Для каждого Запись из Объект Цикл
// Копируем шаблон (используем ЗначениеВСтрокуВнутр для глубокого копирования структуры)
НЗ = ЗначениеИзСтрокиВнутр(ЗначениеВСтрокуВнутр(ШаблонНЗ));
// Заполняем значения отбора для текущей записи
СтрокаОтбораНом = НЗ.Отбор.Найти("Номенклатура", "Имя");
СтрокаОтбораНом.Значение = Запись.Номенклатура;
СтрокаОтбораФормат = НЗ.Отбор.Найти("ФорматМагазина", "Имя");
СтрокаОтбораФормат.Значение = Запись.ОбъектПланирования; // Пример маппинга полей
// Заполняем данные самой записи
СтрокаНабора = НЗ.Строки[0];
ЗаполнитьЗначенияСвойств(СтрокаНабора, Запись);
// Ручная корректировка полей, если имена в Источнике и Получателе отличаются
СтрокаНабора.ФорматМагазина = Запись.ОбъектПланирования;
СтрокаНабора.Документ = СокрЛП(Объект.Отбор.Регистратор.Значение);
// 5. Вызываем встроенную функцию КД для выгрузки в XML
// Параметры: (НаборДанных, ИмяПКО, ВходящиеДанные, ИсходящиеДанные, ИмяПВД)
ВыгрузитьРегистр(НЗ, , , , "АссортиментДТС");
КонецЦикла;
Рассмотрим подробнее, почему этот код решает проблему:
ВыгрузитьRegистр требует строго определенного формата первого параметра. Это должна быть структура с ключами Отбор и Строки. Обе таблицы должны содержать колонки, соответствующие метаданным регистра в Получателе.СтрокаОтбора.Использование = Истина, мы сообщаем платформе при загрузке, что перед записью этого набора нужно удалить старые данные в Получателе, соответствующие этому фильтру. Это критически важно для предотвращения дублей в независимых регистрах.СтрокаНабора.ФорматМагазина = Запись.ОбъектПланирования. Поскольку структуры разные, мы можем на лету переопределять, какие данные из Источника попадут в конкретные измерения Получателя.ЗначениеВСтрокуВнутр позволяет быстро получить чистую копию шаблона структуры для каждой итерации цикла. Однако, если записей очень много (десятки тысяч), рассмотрим более производительный вариант — просто очищать таблицы внутри цикла вместо пересоздания структуры.Одного программного кода в ПВД недостаточно. Проанализируем настройки ПКО, которое используется для загрузки этого регистра (в нашем примере это АссортиментДТС):
МенеджерЗаписи, что несовместимо с нашей структурой набора.Регистратор сопоставлять не нужно. Если вам нужно сохранить информацию о документе-основании, создайте в Получателе обычный реквизит (например, ДокументОснование) и передавайте туда ссылку или строковое представление регистратора из Источника.Отбор нашей структуры.Выясним причину возможного замедления процесса. Сериализация в строку (ЗначениеВСтрокуВнутр) внутри цикла — ресурсоемкая операция. Если вы переносите сотни тысяч записей, лучше изменить подход:
Вместо постоянного копирования шаблона, создайте структуру один раз в начале алгоритма. Внутри цикла используйте метод Очистить() для таблицы строк и просто обновляйте значения в таблице отбора. Это значительно сократит нагрузку на процессор и память сервера при выгрузке.
Также помните про периодичность. Если регистр в Получателе периодический, поле Период в таблице Строки является обязательным для заполнения. Если в Источнике период брался из даты документа, не забудьте явно присвоить это значение: СтрокаНабора.Период = Запись.Регистратор.Дата.
Мы разобрали, как осуществить перенос из подчиненного регистра в независимый, «обманув» стандартные механизмы выгрузки с помощью создания искусственного набора записей. Использование функции ВыгрузитьРегистр в сочетании с ручным формированием таблиц Отбор и Строки дает программисту полный контроль над тем, как данные будут записаны в целевую базу, гарантируя целостность и отсутствие дубликатов.