Как настроить перенос данных из двух справочников источника в один справочник приемника в «Конвертации данных 2.1»?

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

При настройке регулярных обменов между конфигурациями 1С часто возникает архитектурная задача: в базе-источнике информация разделена по двум разным справочникам, а в базе-приемнике ее необходимо объединить в один объект. Типичный пример — перенос номенклатуры из устаревшей конфигурации «Управление торговлей», где параметры маркировки, классификаторы или специфические коды ТН ВЭД хранятся в отдельном подчиненном или связанном справочнике (назовем его Справочник2), в то время как в современной «Рознице» все эти сведения должны записываться напрямую в реквизиты справочника Номенклатура.

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

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


Решение 1. Использование механизма «Входящие данные» в ПКО (Базовый метод)

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

Выясним причину, почему нельзя делать запросы в обработчиках каждого отдельного правила конвертации свойств (ПКС). Если для каждого из 10 реквизитов писать собственный запрос в ПКС «Перед выгрузкой», то при выгрузке 1000 товаров система сделает 10 000 отдельных запросов к СУБД. Это приведет к зависанию базы данных. Поэтому мы напишем один единственный запрос, который соберет все нужные реквизиты из связанного справочника, и поместим их в структуру.

Рассмотрим по шагам, как это настроить в КД 2.1:

  1. Создадим одно ПКО для справочника Номенклатура (например, Номенклатура_Основное).
  2. Внутри этого ПКО создадим новое правило конвертации свойства (ПКС). Поля «Источник» и «Приемник» оставим пустыми. Сдвинем это свойство в самый верх списка (оно должно выполняться первым).
  3. В обработчике события «Перед выгрузкой» этого фиктивного ПКС напишем код, который выбирает данные из связанного справочника и инициализирует структуру входящих данных:

// Инициализируем структуру для передачи параметров
ВходящиеДанные = Новый Структура;

// Задаем значение по умолчанию, чтобы ПКС гарантированно отработало
Значение = "ВычисленоПрограммно";

// Строим запрос к связанному справочнику
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Спр2.ОсобенностьУчета КАК ОсобенностьУчета,
    |    Спр2.КодТНВЭД КАК КодТНВЭД,
    |    Спр2.ТабачнаяПродукция КАК ТабачнаяПродукция,
    |    Спр2.ОбувнаяПродукция КАК ОбувнаяПродукция
    |ИЗ
    |    Справочник.Справочник2 КАК Спр2
    |ГДЕ
    |    Спр2.Номенклатура = &НоменклатураСсылка";

Запрос.УстановитьПараметр("НоменклатураСсылка", Источник.Ссылка);
Выборка = Запрос.Выполнить().Выбрать();

Если Выборка.Следующий() Тогда
    // Заполняем входящие данные свойствами из полученной выборки
    ВходящиеДанные.Вставить("ОсобенностьУчета", Выборка.ОсобенностьУчета);
    ВходящиеДанные.Вставить("КодТНВЭД", Выборка.КодТНВЭД);
    ВходящиеДанные.Вставить("ТабачнаяПродукция", Выборка.ТабачнаяПродукция);
    ВходящиеДанные.Вставить("ОбувнаяПродукция", Выборка.ОбувнаяПродукция);
КонецЕсли;
  1. Для каждого реквизита номенклатуры в приемнике (например, ОсобенностьУчета, КодТНВЭД), который должен заполниться из связанного справочника, создадим ПКС. В поле «Источник» оставим пустоту, в поле «Приемник» укажем соответствующий реквизит.
  2. В настройках этих ПКС обязательно установим флаг «Получать из входящих данных».

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


Решение 2. Оптимизация производительности через глобальное кэширование (Продвинутый метод)

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

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

Разберем по шагам реализацию этого алгоритма:

  1. Перейдем в свойства самой конвертации на вкладку «Алгоритмы/Параметры» и добавим параметр с именем КэшДополнительныхСвойств.
  2. В глобальном обработчике событий конвертации «Перед выгрузкой данных» напишем код, который сформирует таблицу соответствий и поместит ее в структуру параметров в виде быстрого объекта Соответствие:

// Создаем соответствие для быстрого поиска по ссылке-ключу
КэшСвойств = Новый Соответствие;

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Спр2.Номенклатура КАК НоменклатураСсылка,
    |    Спр2.ОсобенностьУчета КАК ОсобенностьУчета,
    |    Спр2.КодТНВЭД КАК КодТНВЭД,
    |    Спр2.ТабачнаяПродукция КАК ТабачнаяПродукция,
    |    Спр2.ОбувнаяПродукция КАК ОбувнаяПродукция
    |ИЗ
    |    Справочник.Справочник2 КАК Спр2
    |ГДЕ
    |    НЕ Спр2.Номенклатура.ПометкаУдаления"; // Оптимизируем выборку при необходимости

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

// Сохраняем кэш в глобальных параметрах обмена
Параметры.Вставить("КэшДополнительныхСвойств", КэшСвойств);
  1. Теперь вернемся к нашему ПКО справочника Номенклатура. В фиктивном ПКС, расположенном на самом верху списка свойств, нам больше не нужно делать тяжелые обращения к базе данных. Перепишем обработчик «Перед выгрузкой» следующим образом:

ВходящиеДанные = Новый Структура;
Значение = "ВычисленоИзКэша";

// Проверяем наличие глобального кэша в параметрах конвертации
Если Параметры.Свойство("КэшДополнительныхСвойств") И Параметры.КэшДополнительныхСвойств <> Неопределено Тогда
    // Получаем данные из оперативной памяти без обращений к СУБД
    ДанныеОбъекта = Параметры.КэшДополнительныхСвойств.Получить(Источник.Ссылка);
    
    Если ДанныеОбъекта <> Неопределено Тогда
        // Копируем значения свойств в структуру ВходящиеДанные
        ЗаполнитьЗначенияСвойств(ВходящиеДанные, ДанныеОбъекта);
    КонецЕсли;
КонецЕсли;

Этот подход сокращает нагрузку на сервер баз данных до абсолютного минимума. Вместо тысяч мелких запросов система выполняет ровно один пакетный запрос, после чего моментально сопоставляет данные в оперативной памяти.


Решение 3. Накопление объектов в ПВД с последующей выгрузкой

Рассмотрим еще один интересный и гибкий архитектурный подход, который часто применяется профессиональными разработчиками при проектировании обменов. Если вы хотите полностью контролировать состав выгружаемой выборки, можно использовать Правила выгрузки данных (ПВД) с произвольным алгоритмом.

Идея заключается в том, чтобы перехватить стандартную выборку регистрируемых объектов номенклатуры, собрать их в массив, сформировать по ним один точечный запрос со всеми необходимыми левыми соединениями к подчиненным справочникам и выгрузить объекты программно.

Посмотрим на пример реализации данного алгоритма:

  1. В ПВД для справочника Номенклатура устанавливаем способ выборки данных — **Произвольный алгоритм**.
  2. В обработчике событий ПВД «Перед обработкой» создаем массив для накопления ссылок зарегистрированных к выгрузке объектов.
  3. В обработчике «Перед выгрузкой объекта» ПВД пишем код накопления:

// Накапливаем ссылки на выгружаемые объекты номенклатуры
Если НЕ Параметры.Свойство("МассивНоменклатуры") Тогда
    Параметры.Вставить("МассивНоменклатуры", Новый Массив);
КонецЕсли;

Параметры.МассивНоменклатуры.Добавить(Объект.Ссылка);

// Отменяем стандартную автоматическую выгрузку объекта
Отказ = Истина;
  1. В глобальном обработчике событий конвертации «После выгрузки данных» пишем код пакетной обработки накопленного массива:

Если Параметры.Свойство("МассивНоменклатуры") И Параметры.МассивНоменклатуры.Количество() > 0 Тогда
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    Номенклатура.Ссылка КАК Ссылка,
        |    Номенклатура.Код КАК Код,
        |    Номенклатура.Наименование КАК Наименование,
        |    Спр2.ОсобенностьУчета КАК ОсобенностьУчета,
        |    Спр2.КодТНВЭД КАК КодТНВЭД
        |ИЗ
        |    Справочник.Номенклатура КАК Номенклатура
        |    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Справочник2 КАК Спр2 
        |        ПО Спр2.Номенклатура = Номенклатура.Ссылка
        |ГДЕ
        |    Номенклатура.Ссылка В (&МассивНоменклатуры)";
        
    Запрос.УстановитьПараметр("МассивНоменклатуры", Параметры.МассивНоменклатуры);
    Выборка = Запрос.Выполнить().Выбрать();
    
    Пока Выборка.Следующий() Цикл
        // Формируем структуру входящих данных для передачи в ПКО
        Реквизиты = Новый Структура("ОсобенностьУчета, КодТНВЭД");
        ЗаполнитьЗначенияСвойств(Реквизиты, Выборка);
        
        // Программно вызываем выгрузку объекта по конкретному ПКО
        ВыгрузитьПоПравилу(Выборка.Ссылка, , Реквизиты, , "Номенклатура_Основное");
    КонецЦикла;
    
КонецЕсли;

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


Решение проблемы с автоматической регистрацией и очисткой изменений

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


<ИнформацияОРегистрацииОбъекта ПустойНабор="false" ТипПриемника="" ТипИсточника="СправочникСсылка.Номенклатура" ... />

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

Когда база-приемник получает пакет данных, она пытается записать сопоставление ГУИДов в специальный системный регистр сведений СоответствияОбъектовИнформационныхБаз. Поля «ТипИсточника» и «ТипПриемника» являются ключевыми измерениями этого регистра. Запись записи с пустым значением ТипПриемника вызывает скрытую транзакционную ошибку СУБД на стороне приемника. В результате пакет подтверждения отправляется обратно без отметки об успешном сопоставлении, и база-источник не снимает регистрацию изменений с этих объектов.

Решение проблемы:

  1. Убедитесь, что в используемой базе «Конвертации данных» загружены самые актуальные метаданные как конфигурации-источника, так и конфигурации-приемника.
  2. Откройте ваше ПКО для номенклатуры и жестко перевыберите объект-приемник из выпадающего списка метаданных (он должен четко указывать на класс СправочникСсылка.Номенклатура или аналогичный в приемнике).
  3. Если вы используете временные вспомогательные ПКО, у которых отключен поиск или отключена запись объектов («Не запоминать сопоставленные объекты»), проверьте, чтобы они не вызывались основными механизмами регистрации.

Автоматическая синхронизация при изменении вспомогательного справочника

При постоянном обмене на планах обмена возникает важный нюанс: пользователь в базе-источнике может изменить свойства (например, изменить признак маркировки или код ТН ВЭД) в Справочнике2, при этом сам элемент справочника Номенклатура останется нетронутым.

Поскольку Справочник2 не выгружается в приемник напрямую и не имеет собственного активного ПКО с авторегистрацией, данные изменения никогда не попадут в базу-приемник, пока кто-нибудь принудительно не перепишет карточку номенклатуры. Чтобы решить эту проблему, настроим Правила регистрации объектов (ПРО) для Справочник2.

В конфигурации-источнике в модуле управления регистрацией (или в правилах регистрации КД) в обработчике события «При регистрации изменения» для Справочник2 напишем следующий скрипт:


// При изменении подчиненного или связанного справочника регистрируем основную номенклатуру
Если ЗначениеЗаполнено(Объект.Номенклатура) Тогда
    // Получаем узел обмена, для которого выполняется регистрация
    ПланыОбмена.ЗарегистрироватьИзменения(Узел, Объект.Номенклатура);
КонецЕсли;

// Отменяем регистрацию самого элемента Справочник2, чтобы не засорять очередь обмена
Отказ = Истина;

Данное простое правило гарантирует: любое изменение дополнительных реквизитов во вспомогательной таблице мгновенно инициирует повторную отправку основного объекта номенклатуры со всеми актуальными и объединенными свойствами.


Альтернативный вариант передачи данных через «Параметры объекта»

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

В ПКС источника мы можем направить значение в параметр XML-узла, указав имя параметра в настройках правила. При чтении файла на стороне приемника эти параметры будут доступны в обработчиках ПКО. Рассмотрим пример кода на стороне приемника в обработчике «После загрузки»:


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

Такой подход обеспечивает максимальную гибкость интеграции, разделяя логику извлечения данных в источнике и логику их интеграции/сопоставления в базе-приемнике.

← На главную