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