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