В процессе разработки правил обмена часто возникает ситуация, когда структура данных в базе-источнике и базе-приемнике существенно различается. Одной из классических задач является разделение одного объекта (например, справочника с текстовым реквизитом) на два самостоятельных, но связанных между собой объекта в приемнике. Рассмотрим типичный пример: в источнике есть справочник ДисконтныеКарты, где ФИО владельца хранится просто строкой. В приемнике же требуется создать полноценный справочник Владельцы и установить ссылку на него в справочнике ДисконтныеКарты.
В этой статье мы разберем несколько подходов к решению этой задачи, проанализируем сильные и слабые стороны каждого метода и выясним, как обеспечить уникальность данных при такой трансформации.
Этот метод является наиболее предпочтительным, так как он использует штатные механизмы Конвертации данных 2.1 и минимизирует количество написанного кода. Основная идея заключается в том, чтобы заставить систему воспринимать строку как объект.
Разберем по шагам алгоритм настройки:
Владельцы. Настроим в нем поиск по наименованию.ДисконтныеКарты найдем реквизит Владелец (который в приемнике является ссылкой на справочник).Владелец и напишем следующий код:
// Значение — это то, что пойдет на вход ПКО "Владельцы"
// Источник.ВладелецФИО — это строковый реквизит из справочника в базе-источнике
Значение = Источник.ВладелецФИО;
При такой настройке система при выгрузке каждой карты увидит, что для свойства Владелец назначено отдельное правило. Она возьмет строку из Значение, создаст узел ссылки в XML и, встретив поиск по наименованию в приемнике, либо найдет существующего владельца, либо создаст нового.
Если логика создания владельца сложнее, чем просто передача строки (например, нужно заполнить дополнительные реквизиты владельца, которых нет в справочнике карт), можно воспользоваться методом ВыгрузитьПоПравилу. Рассмотрим ситуацию, когда нам нужно передать структуру данных.
Проанализируем пример кода в обработчике Перед выгрузкой ПКО ДисконтныеКарты:
// Формируем структуру данных для создания владельца
СтруктураВладельца = Новый Структура;
СтруктураВладельца.Вставить("Наименование", Источник.ВладелецФИО);
СтруктураВладельца.Вставить("Заметки", "Создан автоматически при переносе карты №" + Источник.Код);
// Выгружаем объект программно.
// Результат функции — это ссылка (Узел ссылки в XML), которую мы передадим в ПКС
ВходящиеДанные = СтруктураВладельца;
ВыгрузитьПоПравилу(Источник, , ВходящиеДанные, , "Владельцы");
В самом ПКО Владельцы на вкладке Настройки необходимо установить флаг Получать из входящих данных. Это позволит системе искать значения реквизитов не в объекте-источнике, а в переданной структуре ВходящиеДанные.
Рассмотрим критическую проблему, упомянутую в обсуждении: поиск владельца только по наименованию (ФИО) не всегда надежен, так как в базе могут быть полные однофамильцы. С другой стороны, у нас нет уникального идентификатора владельца в источнике, так как там это просто строка.
Один из изящных способов — использовать УникальныйИдентификатор самой дисконтной карты для синхронизации владельца, если мы точно знаем, что один владелец соответствует одной карте (или если нам допустимо создавать разных владельцев для разных карт одного человека).
В ПКО Владельцы в обработчике Поля поиска мы можем прописать произвольную логику. Но проще передать идентификатор карты в качестве ключа поиска. Проанализируем, как это реализовать:
ИД_Источника).Источник.УникальныйИдентификатор().Если же стоит задача объединять владельцев с одинаковыми именами, но при этом гарантировать отсутствие дублей, рекомендуется использовать обработчик Поля поиска в ПКО приемника:
// Пример в обработчике "Поля поиска" (ПКО Владельцы)
Если СвойстваПоиска.Свойство("Наименование") Тогда
СсылкаНаОбъект = Справочники.Владельцы.НайтиПоНаименованию(СвойстваПоиска.Наименование, Истина);
КонецЕсли;
Если стандартные механизмы ПКС не позволяют корректно связать объекты (например, владелец должен выбираться по очень сложному алгоритму уже в базе-приемнике), можно использовать событие После загрузки.
Выясним, как это работает: При выгрузке дисконтной карты мы можем передать ФИО владельца в какой-нибудь временный реквизит или параметр объекта. После того как объект карты загружен в приемник, выполняется скрипт:
// Событие "После загрузки" в ПКО ДисконтныеКарты
// Объект — это уже созданный/найденный справочник в приемнике
Если ЗначениеЗаполнено(Объект.ВладелецКартыСтрокой) Тогда
// Ищем владельца в справочнике приемника
НайденныйВладелец = Справочники.Владельцы.НайтиПоНаименованию(Объект.ВладелецКартыСтрокой);
Если НайденныйВладелец.Пустая() Тогда
// Создаем нового, если не нашли
НовыйВладелец = Справочники.Владельцы.СоздатьЭлемент();
НовыйВладелец.Наименование = Объект.ВладелецКартыСтрокой;
НовыйВладелец.Записать();
НайденныйВладелец = НовыйВладелец.Ссылка;
КонецЕсли;
// Устанавливаем связь
Объект.Владелец = НайденныйВладелец;
Объект.Записать();
КонецЕсли;
Этот метод часто называют "костылем", но в сложных корпоративных системах со специфической логикой поиска он обеспечивает 100% контроль над процессом связывания объектов.
Проанализировав все подходы, можно сделать следующие выводы:
ВходящимиДанными.ВыгрузитьПоПравилу позволяет создавать цепочки объектов любой вложенности, что полностью решает проблему "один во многие".Таким образом, задача разделения одного объекта на два взаимосвязанных решается в КД 2.1 достаточно элегантно через правильную настройку ПКО и ПКС, без необходимости написания сложных внешних обработок.