При настройке правил обмена в конфигурации «Конвертация данных 2.0» (КД 2.0) для конфигурации «Управление торговлей 11» нередко возникает задача выборочной синхронизации. Например, требуется переносить данные справочников Партнеры и Контрагенты, а также связанные с ними подчиненные справочники и регистры сведений только в том случае, если основной объект является новым. Если же контрагент уже найден в базе-приемнике, перенос подчиненных сущностей необходимо полностью заблокировать, либо обновить родительский объект по специальному условию. Рассмотрим подробнее, как решить эту архитектурную задачу штатными механизмами КД 2.0.
Выясним причину, почему нельзя просто отсечь подчиненные объекты на этапе выгрузки. В стандартном сценарии выгрузка из базы-источника выполняется в отрыве от базы-приемника (через файл обмена). Вызов метода ВыгрузитьПоПравилу в обработчике события После выгрузки неизбежно сериализует все связанные справочники и наборы записей регистров сведений в результирующий XML-файл. На стороне источника невозможно достоверно узнать, существует ли уже контрагент в целевой базе, если не используется прямое COM- или Web-подключение. Следовательно, логика подавления загрузки подчиненных данных должна отрабатывать исключительно на стороне приемника.
Разберем по шагам, как пробросить информацию о факте нахождения основного объекта в правила загрузки подчиненных сущностей. Поскольку в файле обмена объекты загружаются последовательно, подчиненные правила не знают статус родителя без явной передачи контекста. Для сохранения состояния между обработчиками используем глобальную коллекцию Параметры.
Инициализируем структуру хранения в глобальном обработчике правил Перед загрузкой данных:
Параметры.Вставить("СуществующиеКонтрагенты", Новый Соответствие);
В ПКО Контрагенты (или Партнеры) переходим в обработчик ПриЗагрузке и фиксируем ссылку, если объект был найден в базе-приемнике:
Если ОбъектНайден Тогда
Параметры.СуществующиеКонтрагенты.Вставить(Объект.Ссылка, Истина);
КонецЕсли;
В ПКО подчиненных справочников (например, КонтактныеЛицаПартнеров, Файлы) и регистров сведений переходим в обработчик ПередЗагрузкой. Анализируем владельца или ведущее измерение, ссылающееся на контрагента:
// В обработчике ПередЗагрузкой подчиненного ПКО
СсылкаНаВладельца = Неопределено;
Если СвойстваПоиска.Свойство("Владелец", СсылкаНаВладельца) Тогда
Если Параметры.СуществующиеКонтрагенты[СсылкаНаВладельца] = Истина Тогда
Отказ = Истина;
КонецЕсли;
КонецЕсли;
Установка переменной Отказ = Истина в событии ПередЗагрузкой полностью исключает чтение данных объекта из входящего потока и блокирует его создание или перезапись, не нарушая общую целостность загрузки XML.
Проанализируем специфику регистров сведений, с которой часто сталкиваются разработчики при попытке применить стандартные флаги конвертации:
Замещение = Истина, приемник полностью очистит имеющиеся записи по указанному отбору и запишет новые. Поэтому единственным корректным способом предотвратить перезапись регистра в базе-приемнике является явное выставление Отказ = Истина в обработчике ПередЗагрузкой ПКО данного регистра сведений.Рассмотрим распространенный сценарий: требуется найти контрагента по каскадному алгоритму (GUID -> ИНН + КПП -> только ИНН). При этом, если контрагент найден по связке ИНН и КПП, его реквизиты нужно обновить (заместить). Если же он найден только по ИНН (например, у филиала не совпал КПП), существующий объект перезаписывать нельзя, но ссылку использовать необходимо.
Разберем реализацию такого алгоритма через обработчики ПоляПоиска и ПриЗагрузке.
В ПКО Контрагенты открываем обработчик ПоляПоиска. Запросом проверяем наличие записи сначала по двум реквизитам, а затем только по одному:
// Сначала ищем по ИНН и КПП
Запрос = Новый Запрос(
"ВЫБРАТЬ Ссылка ИЗ Справочник.Контрагенты
|ГДЕ ИНН = &ИНН И КПП = &КПП И НЕ ПометкаУдаления"
);
Запрос.УстановитьПараметр("ИНН", СвойстваПоиска["ИНН"]);
Запрос.УстановитьПараметр("КПП", СвойстваПоиска["КПП"]);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
СсылкаНаОбъект = Выборка.Ссылка;
ПрекратитьПоиск = Истина;
Параметры.Вставить("ТекущийНайденБезКПП", Ложь);
Иначе
// Если по связке не нашли, ищем только по ИНН
Запрос.Текст =
"ВЫБРАТЬ Ссылка ИЗ Справочник.Контрагенты
|ГДЕ ИНН = &ИНН И НЕ ПометкаУдаления";
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
СсылкаНаОбъект = Выборка.Ссылка;
ПрекратитьПоиск = Истина;
Параметры.Вставить("ТекущийНайденБезКПП", Истина);
КонецЕсли;
КонецЕсли;
В обработчике ПриЗагрузке ПКО Контрагенты управляем перезаписью данных с помощью предопределенной переменной НеЗамещатьОбъект:
Если ОбъектНайден И Параметры.ТекущийНайденБезКПП = Истина Тогда
НеЗамещатьОбъект = Истина;
КонецЕсли;
Важное предостережение: на форумах встречается рекомендация в обработчике После загрузки переопределять объект конструкцией Объект = Объект.Ссылка.ПолучитьОбъект(). Применять данный подход настоятельно не рекомендуется. В КД 2.0 переменная Объект после завершения обработчика передается на этап физической записи в базу данных. Подобная перечитка приводит к повторной записи объекта «самим собой», вызывая лишние блокировки таблиц, избыточные вызовы подписок на события и перезапись версий данных. Использование переменной НеЗамещатьОбъект = Истина в событии ПриЗагрузке полностью исключает этап записи существующего объекта в СУБД.
При переносе контрагентов в УТ 11 необходимо учитывать две важные особенности типовой конфигурации:
Партнер у контрагента является обязательным для заполнения. Если при загрузке нового контрагента отменить создание партнера через Отказ = Истина, контрагент запишется либо с пустой ссылкой, либо с битой ссылкой (при включенном режиме ОбменДанными.Загрузка = Истина). В дальнейшем пользователи не смогут перепроводить документы по данному клиенту. Партнера нельзя отменять, если создается новый контрагент.КонтактнаяИнформация, либо в одноименном регистре сведений. Если контактная информация находится в табличной части, блокировка загрузки регистров не повлияет на ее перенос. Для запрета обновления табличной части следует настраивать ПКГС (правила конвертации групп свойств), установив флаг Не замещать непосредственно для табличной части.