При интеграции 1С с внешними системами, интернет-магазинами или поставщиками одной из частых задач является загрузка прайс-листов и каталогов товаров из файлов формата XML. В процессе разработки обработки загрузки разработчики часто сталкиваются с ситуациями, когда элементы не создаются, пропускаются отдельные теги или в базе появляются дубли номенклатуры.
Рассмотрим подробно, как правильно организовать чтение XML-файла с помощью встроенного объекта ЧтениеXML, заполнить реквизиты справочника Номенклатура, обработать ссылочные типы данных и реализовать эффективный поиск существующих элементов для предотвращения задвоения.
Разберем типичную проблему: код обработки выполняется без видимых ошибок, но новые элементы в справочнике не появляются или создаются с пустыми значениями. Основная причина заключается в нерасхождении названий узлов в коде и в самом XML-файле.
Посмотрим на пример обработки тегов при чтении файла. Если условие проверки имени узла настроено на один тег (например, ЭлементСправочника), а в реальности структура файла содержит разделение по категориям (tires, rims, camera), алгоритм записи просто не будет вызываться.
Выясним, как устроена общая схема обхода XML-документа с помощью объекта ЧтениеXML. Проанализируем базовый пример последовательного чтения узлов:
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл("C:\Upload\data.xml");
Пока ЧтениеXML.Прочитать() Цикл
Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
ИмяУзла = ЧтениеXML.Имя;
ИначеЕсли ЧтениеXML.ТипУзла = ТипУзлаXML.Текст Тогда
// Сохраняем значение текщего узла в структуру
ДанныеУзла.Вставить(ИмяУзла, ЧтениеXML.Значение);
ИначеЕсли ЧтениеXML.ТипУзла = ТипУзлаXML.КонецЭлемента Тогда
// При достижении закрывающего тега товара вызываем процедуру записи
Если ЧтениеXML.Имя = "tires" Или ЧтениеXML.Имя = "rims" Тогда
СоздатьИлиОбновитьТовар(ДанныеУзла);
КонецЕсли;
КонецЕсли;
КонецЦикла;
ЧтениеXML.Закрыть();
При записи данных в справочник Номенклатура важно различать примитивные типы (строки, числа, булево) и ссылочные реквизиты (подчиненные справочники брендов, размеров, категорий). Если реквизит Brand или Width имеет тип СправочникСсылка.Бренды, прямое присвоение строкового значения из XML вызовет ошибку или сбросит значение в пустую ссылку.
Для корректного заполнения ссылочных полей необходимо выполнить поиск нужной записи в соответствующем справочнике. Если элемент не найден, его следует создать или оставить значение незаполненным в зависимости от бизнес-логики.
Рассмотрим пример процедуры создания элемента справочника Номенклатура с заполнением основных свойств:
Процедура ДобавитьЭлементШина(МассивДанных)
НовыйЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
НовыйЭлемент.Артикул = МассивДанных.cae;
НовыйЭлемент.Наименование = МассивДанных.name;
// Поиск и заполнение ссылочного реквизита "Бренд"
СсылкаБренд = Справочники.Производители.НайтиПоНаименованию(МассивДанных.brand);
Если НЕ ЗначениеЗаполнено(СсылкаБренд) И ЗначениеЗаполнено(МассивДанных.brand) Тогда
ОбъектБренд = Справочники.Производители.СоздатьЭлемент();
ОбъектБренд.Наименование = МассивДанных.brand;
ОбъектБренд.Записать();
СсылкаБренд = ОбъектБренд.Ссылка;
КонецЕсли;
НовыйЭлемент.Бренд = СсылкаБренд;
// Заполнение числовых и строковых характеристик
НовыйЭлемент.Модель = МассивДанных.model;
НовыйЭлемент.Ширина = МассивДанных.width;
НовыйЭлемент.Диаметр = МассивДанных.diameter;
НовыйЭлемент.Записать();
КонецПроцедуры
При регулярном импорте данных необходимо предотвратить создание повторяющихся позиций. Наиболее распространенным уникальным идентификатором товара во внешних файлах выступает артикул (код товара у поставщика).
Посмотрим, как организовать проверку наличия товара по артикулу перед созданием объекта с помощью метода НайтиПоРеквизиту:
Процедура ЗагрузитьДиски(МассивДисков)
Для Каждого СтрокаДанных Из МассивДисков Цикл
Артикул = СтрокаДанных.cae;
Если НЕ ЗначениеЗаполнено(Артикул) Тогда
Продолжить;
КонецЕсли;
// Поиск существующего элемента в базе
СсылкаНоменклатура = Справочники.Номенклатура.НайтиПоРеквизиту("Артикул", Артикул);
Если СсылкаНоменклатура = Справочники.Номенклатура.ПустаяСсылка() Тогда
ОбъектНоменклатура = Справочники.Номенклатура.СоздатьЭлемент();
Иначе
ОбъектНоменклатура = СсылкаНоменклатура.ПолучитьОбъект();
КонецЕсли;
ОбъектНоменклатура.Артикул = Артикул;
ОбъектНоменклатура.Наименование = СтрокаДанных.name;
ОбъектНоменклатура.Записать();
КонецЦикла;
КонецПроцедуры
Рассмотренный выше метод НайтиПоРеквизиту отлично подходит для небольших файлов. Однако если XML содержит десятки тысяч строк, обращение к базе данных в цикле существенно снизит скорость загрузки (проблема N+1 queries). Для высокой производительности и подготовки алгоритма к автоматическому заполнению через регламентные задания рекомендуется придерживаться следующих правил:
Справочник.Номенклатура с условием В (&МассивАртикулов) и выгрузите результат в Соответствие. Это позволит выполнять поиск существующих ссылок в оперативной памяти мгновенно.Артикул в Конфигураторе выставлено свойство «Индексировать». Без этого поиск по реквизиту производит полное сканирование таблицы информационной базы.НачатьТранзакцию() и ЗафиксироватьТранзакцию(). Запись порциями (по 500–1000 элементов) ускоряет процесс импорта в десятки раз.Соответствие, чтобы не производить повторные поисковые запросы к СУБД для одинаковых значений.ОбновитьИнтерфейс() или Предупреждение()). Это позволит запускать обработку в фоновых и регламентных заданиях на сервере без участия пользователя.