При настройке обмена через «Конвертацию данных 2» (КД2) разработчики часто сталкиваются со специфическим поведением системы при переносе иерархических справочников. Рассмотрим классическую ситуацию: мы переносим справочник Номенклатура со всеми свойствами и иерархией. Поиск настроен по артикулу и флагу ЭтоГруппа, при этом стоит галочка «Не замещать, если элемент найден».
Ожидаемое поведение: если элемент не найден в базе-приемнике, он создается вместе со всеми папками-родителями. Если же элемент найден, свойства не замещаются, и, соответственно, лишние папки переноситься не должны. Однако на практике, даже если элемент найден, КД2 все равно создает всю цепочку родительских папок. Проанализируем причину такого поведения и разберем по шагам несколько надежных способов решения этой задачи.
Основная проблема при файловом обмене в КД2 заключается в том, что база-источник на момент выгрузки не знает, будет ли найден объект в базе-приемнике. Из-за архитектуры системы ссылочные объекты (в нашем случае реквизит Родитель) выгружаются в XML-файл до того, как выгрузится сам основной элемент.
Когда база-приемник начинает чтение XML-файла, она сначала встречает узлы с папками, добросовестно их создает, и только потом доходит до самого элемента. На этом этапе система понимает, что элемент существует (срабатывает флаг ОбъектНайден = Истина) и отменяет обновление его реквизитов. Но папки уже созданы в корне справочника, и откатить их создание стандартными средствами не получится. Выясним, как это можно обойти.
Этот метод является самым надежным для файлового обмена. Его суть заключается в том, чтобы вообще отказаться от стандартной выгрузки реквизита Родитель и перенести ответственность за создание папок на момент после загрузки элемента, когда статус объекта уже точно известен.
Разберем реализацию по шагам:
Родитель (снимаем флаг выгрузки).
// Передаем наименование родителя или другие ключевые поля для поиска
ИсходящиеДанные.Вставить("ИмяРодителя", Источник.Родитель.Наименование);
// Можно передать Артикул родителя, если он есть, для более точного поиска
Если НЕ ОбъектНайден Тогда
// Объект новый, значит папки нужны.
ИмяПапки = ПараметрыОбъекта.Получить("ИмяРодителя");
Если ЗначениеЗаполнено(ИмяПапки) Тогда
НайденнаяПапка = Справочники.Номенклатура.НайтиПоНаименованию(ИмяПапки, Истина);
Если НайденнаяПапка.Пустая() Тогда
// Создаем новую группу программно
НоваяГруппа = Справочники.Номенклатура.СоздатьГруппу();
НоваяГруппа.Наименование = ИмяПапки;
НоваяГруппа.Записать();
НайденнаяПапка = НоваяГруппа.Ссылка;
КонецЕсли;
// Присваиваем родителя новому элементу
Объект.Родитель = НайденнаяПапка;
КонецЕсли;
КонецЕсли;
Таким образом, мы полностью исключаем появление «мусорных» папок: иерархия строится только для действительно новых позиций номенклатуры.
Рассмотрим подход, предложенный на форуме. Он заключается в создании отдельных правил конвертации для элементов и для папок. Это позволяет гибко управлять логикой отказов при выгрузке.
Посмотрим на алгоритм реализации:
ПриВыгрузке прописываем:
// Отказываемся от выгрузки, если текущий объект - это папка
Отказ = Источник.ЭтоГруппа;
ПриВыгрузке пишем обратное условие:
// Отказываемся от выгрузки, если текущий объект - обычный элемент
Отказ = НЕ Источник.ЭтоГруппа;
После этого реквизит Родитель у элементов мы настраиваем на загрузку по второму ПКО. Метод рабочий, однако он не спасает от проблемы создания папок, если элемент все-таки выгружается. Поэтому для сложных ситуаций этот способ лучше комбинировать с последующей программной очисткой пустых папок регламентным заданием.
Если обмен идет не через файл XML, а посредством прямого подключения к базе-приемнику (COM-соединение), у нас появляется мощный инструмент: источник может «заглянуть» в приемник прямо во время выгрузки данных.
Проанализируем пример кода для обработчика «Перед выгрузкой» ПКО Номенклатуры:
// Проверяем наличие номенклатуры в базе-приемнике
НайденнаяНоменклатура = БазаПриемник.Справочники.Номенклатура.НайтиПоРеквизиту("Артикул", Источник.Артикул);
Если НайденнаяНоменклатура <> БазаПриемник.Справочники.Номенклатура.ПустаяСсылка() Тогда
// Объект в приемнике уже есть.
// Отменяем выгрузку реквизита Родитель для данного элемента.
УзелСсылки.Очистить(); // Либо снимаем флаги выгрузки конкретных ПКС
КонецЕсли;
Этот способ идеален для On-line обменов, так как позволяет экономить трафик и время, отсекая ненужные объекты до начала формирования пакета.
Рассмотрим менее гибкий, но быстрый вариант — использование штатных настроек ПКО. Для реквизита можно установить флаг «Не выгружать объекты свойств источника по ссылкам». При такой настройке КД2 выгрузит только поля поиска папки-родителя, но не станет тянуть весь объект группы со всеми ее реквизитами.
Чтобы база-приемник не плодила пустые ссылки-заглушки для ненайденных папок, в ПКО самой группы номенклатуры необходимо установить флаг «При переносе объекта по ссылке НЕ создавать новый объект, а только переносить ссылку».
Важный нюанс: у этого метода есть существенный недостаток. Если элемент действительно новый и папка ему необходима (а в приемнике ее еще нет), КД2 не создаст папку полноценно. Будет перенесена лишь пустая ссылка («битая» ссылка). Поэтому данный подход требует идеальной синхронизации нормативно-справочной информации между базами до начала обмена документами или элементами справочников.
Подводя итог, можно сказать, что для файловых обменов наиболее предпочтительным и контролируемым является Способ 1 (через ПараметрыОбъекта). Он гарантирует, что структура справочника в базе-приемнике останется чистой, а новые элементы корректно распределятся по нужным папкам.