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