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