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