Как правильно заменить объект в базе-приемнике при обмене через «1С:Конвертация данных 2»?

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

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

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

Выясним причину: почему происходит переименование?

Поведение, при котором оригинальный объект в базе-приемнике переименовывается вместо подмены, связано с базовыми механизмами работы платформы КД 2:

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

Рассмотрим подробнее четыре надежных практических варианта решения этой задачи.

Вариант 1. Передача параметра и замена объекта при загрузке (Рекомендуемый)

Наиболее надежный, производительный и чистый с точки зрения архитектуры способ — произвести подмену непосредственно в базе-приемнике в процессе загрузки данных. Нам не потребуется передавать «фиктивные» данные через XML и ломать кэш выгрузки. Мы просто передадим признак необходимости замены.

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

Шаг 1. В ПКС нужного реквизита (например, ДоговорКонтрагента) в документе-источнике добавим передачу параметра. Для этого в обработчике Перед выгрузкой ПКС напишем следующий код:


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

Шаг 2. Перейдем в ПКО самого документа-приемника (например, ПоступлениеБезналичныхДенежныхСредств) и откроем обработчик После загрузки. Напишем алгоритм поиска нужного договора в базе-приемнике и подставим его в загруженный объект:


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

Этот способ хорош тем, что логика поиска инкапсулирована в приемнике, а XML-файл не раздувается лишними данными.

Вариант 2. Использование «Исходящих данных» и ПКО без источника

Если вам принципиально важно решить задачу на этапе выгрузки (например, если логика определения нужного договора завязана на сложные структуры базы-источника), то нельзя использовать в качестве источника реальную ссылку на Договор1. Нужно отвязать ПКО от физического объекта источника.

Проанализируем этот метод:

Шаг 1. Создадим новое ПКО для справочника договоров, назовем его, например, Договоры_ПоискВПриемнике. Поле «Источник» в заголовке этого ПКО обязательно **оставляем пустым**.

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

Шаг 3. В ПКС договора в документе-источнике в обработчике Перед выгрузкой переопределим правило конвертации и передадим структуру для поиска через ИсходящиеДанные:


Если Источник.ВидОперации = Перечисления.ВидыОперацийПоступлениеБезналичныхДенежныхСредств.ОплатаПокупателя Тогда
    
    ИмяПКО = "Договоры_ПоискВПриемнике";
    
    // Формируем структуру параметров, которые ожидает ПКО на входе
    СтруктураПоиска = Новый Структура;
    СтруктураПоиска.Вставить("Наименование", "Договор2");
    СтруктураПоиска.Вставить("Владелец", Источник.Контрагент); 
    СтруктураПоиска.Вставить("Организация", Источник.Организация);
    
    ИсходящиеДанные = СтруктураПоиска;
    
КонецЕсли;

Поскольку у ПКО нет источника, КД 2 не будет привязываться к уникальному идентификатору исходного договора. Поиск в базе-приемнике произойдет исключительно по переданным текстовым и ссылочным полям.

Вариант 3. Переопределение поиска через обработчик «Поля поиска»

Мы можем оставить текущую структуру с подменой ИмяПКО на этапе выгрузки, но жестко перехватить управление сопоставлением объектов при загрузке в приемник.

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

Шаг 1. В ПКО МеняемДоговор перейдем в обработчик «Поля поиска».

Шаг 2. Напишем код, который проверит, ищется ли в данный момент наш специфический договор, и если да — выполнит явный поиск в базе-приемнике в обход стандартных алгоритмов КД 2:


Если СвойстваПоиска.Получить("Наименование") = "Договор2" Тогда
    
    // Выполняем явный поиск в базе-приемнике
    НайденнаяСсылка = Справочники.ДоговорыКонтрагентов.НайтиПоНаименованию("Договор2", Истина, , СвойстваПоиска["Владелец"]);
    
    Если ЗначениеЗаполнено(НайденнаяСсылка) Тогда
        СсылкаНаОбъект = НайденнаяСсылка;
        ПрекратитьПоиск = Истина; // Прерываем выполнение стандартного поиска КД
    КонецЕсли;
    
КонецЕсли;

Важное примечание: Для успешного применения этого метода в настройках ПКО МеняемДоговор на вкладке «Настройки» должен быть обязательно **снят** флаг «Искать объект приемника по внутреннему идентификатору источника», иначе система сопоставит объект по UID еще до вызова обработчика полей поиска.

Вариант 4. Сброс кэша выгрузки и переопределение ключа выгружаемых данных

Если мы хотим использовать стандартное сопоставление, но избежать эффекта «переименования» из-за кэширования одинаковых ссылок источника, мы должны принудительно заставить КД 2 считать выгружаемый объект уникальным.

Выполним следующие настройки:

Шаг 1. В ПКО МеняемДоговор на вкладке «Настройки» установим флаг «Не запоминать выгруженные объекты».

Шаг 2. В ПКС договора в документе в обработчике Перед выгрузкой принудительно изменим КлючВыгружаемыхДанных:


Если Источник.ВидОперации = Перечисления.ВидыОперацийПоступлениеБезналичныхДенежныхСредств.ОплатаПокупателя Тогда
    
    ИмяПКО = "МеняемДоговор";
    
    // Формируем уникальный ключ, чтобы КД 2 не использовала закэшированный GUID источника
    КлючВыгружаемыхДанных = "Договор2_" + СокрЛП(Источник.Организация.УникальныйИдентификатор());
    
КонецЕсли;

Благодаря этому КД 2 сгенерирует в XML-файле узел с уникальным ключом, не связанным с Договором1, и в базе-приемнике отработает корректный поиск по полям.

Используйте наиболее подходящий для вашей конфигурации метод, и обмены будут работать стабильно и предсказуемо!

← На главную