При настройке обмена данными между различными конфигурациями «1С» разработчики часто сталкиваются с нестандартными задачами сопоставления объектов. Типичный сценарий: в базе-источнике ведется детальный учет с разделением номенклатуры по типам, а в базе-приемнике необходимо свернуть эти позиции и сопоставить их с предопределенной или уже существующей номенклатурой, ориентируясь только на текстовое наименование типа источника. При этом перенос происходит внутри табличной части документа.
В этой статье мы подробно проанализируем, почему стандартные механизмы генерации правил могут выдавать пустые значения при загрузке, разберем грубые ошибки программирования в обработчиках конвертации и рассмотрим два надежных архитектурных решения этой задачи.
Для начала разберем типичные ошибки, которые совершают начинающие специалисты по интеграции на примере сообщений с форума. Рассмотрим исходный код, который использовался в правилах конвертации группы свойств (ПКГС) табличной части:
КоллекцияОбъектов = Новый Массив;
КоллекцияОбъектов = Источник.Товары.Выгрузить;
Для Каждого СтрокаТЧ Из Источник.Товары Цикл
НоваяСтрока = КоллекцияОбъектов.Добавить;
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаТЧ);
КонецЦикла;
В данном фрагменте кода кроется серьезная логическая ошибка — дублирование строк табличной части. Метод Выгрузить() возвращает полноценную таблицу значений, содержащую все строки табличной части источника. Последующий обход коллекции через цикл Для Каждого и принудительное добавление тех же самых строк приводит к тому, что в выгружаемую коллекцию попадает двойной набор данных. В итоге в приемник запишется в два раза больше строк, чем было в исходном документе.
Вторая критическая ошибка возникает при обращении к свойствам источника в обработчике Перед выгрузкой ПКС Наименование в ПКО НоменклатураТип:
Если СокрЛП(источник.ТипНоменклатуры.Наименование)="Сырье и материалы" тогда
Значение="10,1 Сырье и материалы";
КонецЕсли;
Если в качестве источника для ПКО выступает сам справочник ТипыНоменклатуры, то у него нет реквизита ТипНоменклатуры. Платформа закономерно вызывает исключение Поле объекта не обнаружено (ТипНоменклатуры). В данном контексте необходимо обращаться напрямую к свойствам текущего выгружаемого объекта: Источник.Наименование.
Третья проблема — загрузка пустой номенклатуры при успешной генерации XML-файла. Это происходит из-за конфликта идентификаторов (UUID). По умолчанию «1С:Конвертация данных» пытается сопоставить объекты по их уникальному идентификатору. Поскольку ПКО преобразует тип номенклатуры источника в саму номенклатуру приемника, система пытается найти номенклатуру с UUID типа номенклатуры. Поиск завершается неудачей, а стандартный поиск по наименованию не отрабатывает из-за установленных флагов запрета создания новых объектов или жестких ограничений иерархии справочника-приемника.
---
Самый чистый и надежный способ сопоставить произвольный текст с реальным элементом справочника — использовать правило конвертации объектов (ПКО) с пустым источником. В этом случае мы полностью убираем привязку к уникальным идентификаторам источника и работаем исключительно со строковым значением.
Выполним настройку по шагам:
НоменклатураПоНаименованиюТипа.СправочникСсылка.Номенклатура.Наименование. Поля источника у него не будет.
Значение = Источник;
Номенклатура в ТЧ Товары) в обработчике Перед выгрузкой пропишите алгоритм сопоставления. Здесь мы анализируем тип номенклатуры строки источника, вычисляем нужное текстовое название для приемника и передаем его в созданное ПКО:
// Получаем тип номенклатуры текущей строки табличной части
ТипНом = ОбъектКоллекции.Номенклатура.ТипНоменклатуры;
Если ЗначениеЗаполнено(ТипНом) Тогда
ИмяТипа = СокрЛП(ТипНом.Наименование);
// Сопоставляем типы источника с конкретными наименованиями в приемнике
Если ИмяТипа = "Сырье и материалы" Тогда
Значение = "10,1 Сырье и материалы";
ИначеЕсли ИмяТипа = "Материалы и спецоснастка" Тогда
Значение = "10,10 Специальная оснаска и специальная одежда на";
ИначеЕсли ИмяТипа = "Запасные части" Тогда
Значение = "10,5 Запасные части";
ИначеЕсли ИмяТипа = "Инвентарь и хоз.принадлежности" Тогда
Значение = "10,9 Инвентарь и хозяйственные принадлежности";
ИначеЕсли ИмяТипа = "ГСМ и заправочные жидкости" Тогда
Значение = "10,3 Топливо";
ИначеЕсли ИмяТипа = "Инструменты" Тогда
Значение = "10,11,2 Инструменты";
ИначеЕсли ИмяТипа = "Спецодежда" Тогда
Значение = "10,11,1 Спецодежда";
ИначеЕсли ИмяТипа = "Стройматериалы" Тогда
Значение = "10,8 Строительные материалы";
Иначе
Значение = "Прочие материалы"; // Значение по умолчанию
КонецЕсли;
// Явно указываем созданное нами ПКО для ручного поиска по тексту
ИмяПКО = "НоменклатураПоНаименованиюТипа";
Иначе
// Если тип не задан, исключаем выгрузку номенклатуры для этой строки
Отказ = Истина;
КонецЕсли;
При таком подходе в выгружаемом XML-файле сформируется чистая текстовая строка в узле свойства, а при загрузке обработчик платформы выполнит поиск в справочнике Номенклатура по точному совпадению наименования.
---
Бывают ситуации, когда стандартный поиск платформы по наименованию не справляется. Например, в базе-приемнике справочник Номенклатура имеет сложную иерархическую структуру (папки), включен полнотекстовый поиск или наименования могут содержать скрытые пробелы и символы переноса строки. В таких случаях надежнее всего написать собственный алгоритм поиска с помощью запроса непосредственно при загрузке данных.
Для реализации этого варианта выполним следующие действия:
НоменклатураПоНаименованиюТипа (настроенное по Варианту А).
// Выполняем поиск только на первом этапе сопоставления свойств
Если НомерВариантаПоиска = 1 Тогда
// Получаем переданное нами текстовое наименование
НаименованиеДляПоиска = СокрЛП(СвойстваПоиска["Наименование"]);
Если ЗначениеЗаполнено(НаименованиеДляПоиска) Тогда
// Сначала пробуем быстрый стандартный поиск по полному совпадению
НайденныйОбъект = Справочники.Номенклатура.НайтиПоНаименованию(НаименованиеДляПоиска, Истина);
Если ЗначениеЗаполнено(НайденныйОбъект) Тогда
СсылкаНаОбъект = НайденныйОбъект;
ПрекратитьПоиск = Истина; // Сообщаем системе, что объект успешно найден
Иначе
// Если точное совпадение не найдено, задействуем более гибкий поиск через запрос
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ ПЕРВЫЕ 1
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Наименование ПОДОБНО &Наименование
| И НЕ Номенклатура.ЭтоГруппа";
// Ищем с использованием оператора ПОДОБНО для исключения проблем с концевыми пробелами
Запрос.УстановитьПараметр("Наименование", НаименованиеДляПоиска + "%");
УстановитьПривилегированныйРежим(Истина);
РезультатЗапроса = Запрос.Выполнить();
УстановитьПривилегированныйРежим(ФлагПривилегированногоРежима);
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
СсылкаНаОбъект = Выборка.Ссылка;
ПрекратитьПоиск = Истина;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЕсли;
Использование обработчика Поля поиска гарантирует, что даже при наличии дублей в базе данных или сложной структуры папок, алгоритм выберет наиболее подходящую номенклатуру по заданному шаблону, минимизируя появление пустых строк в загруженных документах.