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