При организации обмена данными между конфигурациями «Управление торговлей 10.3» и «Бухгалтерия предприятия 2.0» разработчики часто сталкиваются с проблемой рассинхронизации справочника Номенклатура. Типичный сценарий: базы велись независимо, уникальные идентификаторы (UUID / ГУИД) элементов не совпадают, а сопоставление необходимо выполнить по какому-то специфическому реквизиту. Например, по реквизиту КодБухгалтерииНМД, который заполнен в УТ 10.3 лишь частично. Если данный код заполнен, поиск должен идти по нему, а если пуст — по наименованию и родителю номенклатуры.
В этой статье мы подробно разберем, почему популярные «быстрые» решения этой задачи (вроде отключения кэширования объектов) могут навредить производительности вашей системы, проанализируем ошибки в коде обработчиков и настроим правильный многошаговый поиск в правилах конвертации данных (КД 2.1).
Часто в попытках заставить правила обмена сопоставлять объекты по сложной логике разработчики прибегают к «костылю»: включают в ПКО (Правила конвертации объектов) номенклатуры флаг «Не запоминать выгруженные объекты» на вкладке «Настройки». На первый взгляд это решает проблему, но в долгосрочной перспективе на реальной базе (например, объемом в 38 000 позиций) это приведет к серьезным техническим проблемам.
Давайте разберем, как работает кэширование по умолчанию. Когда объект (товар «А») выгружается в первый раз, его структура полностью записывается в XML-файл обмена. При последующих упоминаниях этого товара (например, в табличных частях документов продаж) выгружается только его ссылка (UUID). При загрузке импортер сопоставляет UUID и мгновенно подставляет нужный объект из своей памяти, не запуская повторно тяжелые алгоритмы поиска в базе данных.
Если мы взводим флаг «Не запоминать выгруженные объекты», кэширование полностью отключается. Это приводит к следующим последствиям:
Рассмотрим типичные ошибки в обработчике Поля поиска, из-за которых платформа игнорирует попытки сопоставить объекты вручную.
Первая и самая частая ошибка — это игнорирование переменной ПрекратитьПоиск. Если вы выполняете ручной поиск с помощью запроса или методов менеджера справочника (например, НайтиПоКоду), то коду необходимо явно сообщить системе, что объект успешно найден и штатные алгоритмы сопоставления КД запускать больше не требуется. В противном случае платформа продолжит сопоставление по тем полям, для которых на уровне ПКС взведен флаг «Поиск», и затрет ваш результат пустой ссылкой.
Вторая ошибка связана с некорректной передачей пустого значения. Если в УТ 10.3 реквизит КодБухгалтерииНМД не заполнен, система все равно пытается выгрузить его (как пустое значение) и сопоставить объект по первому варианту поиска (по коду). Попытка сопоставить товар по пустому коду приводит к некорректной работе алгоритмов поиска.
Давайте настроим синхронизацию корректно, сохранив все преимущества кэширования объектов Конвертации данных.
Поскольку базы исторически велись отдельно и ГУИДы номенклатуры в них заведомо отличаются, нам необходимо отключить стандартное сопоставление по идентификатору. Для этого откроем ПКО Номенклатура на вкладке «Основные» и снимем флаг «Искать объект приемника по внутреннему идентификатору...».
Перейдем на вкладку «Правила конвертации свойств» в ПКО Номенклатура. Нам необходимо отметить флаг «Поиск» для следующих свойств:
Код (в это свойство мы будем передавать значение нашего реквизита КодБухгалтерииНМД);Наименование;ЭтоГруппа;Родитель.В ПКС для свойства Код в обработчике «Перед выгрузкой» напишем следующий код, который исключит передачу стандартного кода УТ, если специальный бухгалтерский код не заполнен:
Если ЗначениеЗаполнено(Источник.КодБухгалтерииНМД) Тогда
Значение = Источник.КодБухгалтерииНМД;
Иначе
// Если кода БП нет, очищаем значение, чтобы не передавать внутренний код УТ
Значение = "";
КонецЕсли;
Для реализации логики «если заполнен КодБухгалтерииНМД — ищем по нему, иначе — по наименованию и родителю», мы задействуем механизм вариантов поиска КД. Откроем обработчик Поля поиска в ПКО Номенклатура и напишем высокопроизводительный код, использующий встроенный функционал платформы без лишних запросов к базе данных:
Если НомерВариантаПоиска = 1 Тогда
// Вариант 1: Попытка поиска по коду БП, если он прилетел из источника
КодДляПоиска = СвойстваПоиска["Код"];
Если ЗначениеЗаполнено(КодДляПоиска) Тогда
СтрокаИменСвойствПоиска = "Код, ЭтоГруппа";
Иначе
// Если код пустой, сразу перенаправляем алгоритм на следующий шаг поиска
СтрокаИменСвойствПоиска = "";
КонецЕсли;
ИначеЕсли НомерВариантаПоиска = 2 Тогда
// Вариант 2: Если первый шаг не дал результатов или код был пуст, ищем по иерархии и наименованию
СтрокаИменСвойствПоиска = "Наименование, Родитель, ЭтоГруппа";
КонецЕсли;
Если вам по какой-то причине необходимо выполнить поиск программно (например, обратиться напрямую к менеджеру справочника или выполнить специфический запрос), обязательно используйте переменную ПрекратитьПоиск. Ниже приведен пример правильной реализации ручного поиска в обработчике Поля поиска:
Если НомерВариантаПоиска = 1 Тогда
КодДляПоиска = СвойстваПоиска["Код"];
Если ЗначениеЗаполнено(КодДляПоиска) Тогда
// Выполняем поиск элемента в базе БП по коду
НайденнаяСсылка = Справочники.Номенклатура.FindbyCode(КодДляПоиска);
Если Не НайденнаяСсылка.IsEmpty() Тогда
СсылкаНаОбъект = НайденнаяСсылка;
// КРИТИЧЕСКИ ВАЖНО: прекращаем дальнейший автоматический поиск платформы
ПрекратитьПоиск = Истина;
КонецЕсли;
КонецЕсли;
ИначеЕсли НомерВариантаПоиска = 2 Тогда
// Если ручной поиск не дал результатов, передаем управление стандартному поиску по наименованию
СтрокаИменСвойствПоиска = "Наименование, Родитель, ЭтоГруппа";
КонецЕсли;
Использование этого подхода гарантирует, что обмен данными будет проходить быстро, объем файла выгрузки останется минимальным, а номенклатура сопоставится без создания дублирующих позиций в базе-приемнике.