При интеграции сторонних систем с платформой 1С 7.7 разработчики часто сталкиваются с ситуацией, когда шапка документа загружается корректно, а табличная часть с номенклатурой остается пустой. Проанализируем типичные причины такой ошибки, разберем работу с внешней компонентой v7plus.dll и рассмотрим альтернативный способ чтения XML с использованием COM-объекта MSXML2.DOMDocument.
Рассмотрим исходный алгоритм чтения XML-файла. Основная проблема при работе с компонентой v7plus.dll возникает из-за смешивания механизмов последовательного считывания (методы Спуститься, Следующий) и работы с объектами DOM (метод ТекущийЭлементВВидеОбъекта).
Когда считыватель подходить к тегу Товары, позиция указателя в файле сбивается. Попытка обратиться к подчиненным узлам без правильного спуска по иерархии тегов приводит к тому, что цикл чтения строк табличной части просто пропускается.
Исходный фрагмент обработки, содержащий логическую ошибку:
ИначеЕсли ( УзелНакладные.Наименование = "Товары" ) Тогда
ТипТега = ФайлXML.Спуститься;
Если ИмяТега = "Строка" Тогда
Объект = ФайлXML.ТекущийЭлементВВидеОбъекта;
КоличествоЛинийНакладной = Объект.КоличествоПодчиненных;
// Переменная ИмяТега не обновилась после метода Спуститься,
// из-за чего условие не выполняется и строки пролистываются.
КонецЕсли;
КонецЕсли;
Разберем по шагам, как исправить логику последовательного чтения через v7plus.dll. Нам необходимо при обнаружении тега Товары переключить контекст на текущий элемент как объект и явно получить все подчиненные узлы с именем Строка, а внутри них — реквизиты Номенклатура и Количество.
ИначеЕсли ( УзелНакладные.Наименование = "Товары" ) Тогда
ОбъектТовары = ФайлXML.ТекущийЭлементВВидеОбъекта;
КолвоПодчиненныхУзлов_Товары = ОбъектТовары.КоличествоПодчиненных;
Для сч_Товары = 1 По КолвоПодчиненныхУзлов_Товары Цикл
УзелТовары = ОбъектТовары.ПолучитьПодчиненныйПоНомеру(сч_Товары);
Если ( УзелТовары.Наименование = "Строка" ) Тогда
ОбъектСтрока = УзелТовары;
КоличествоЛинийСтроки = ОбъектСтрока.КоличествоПодчиненных;
Номенклатура = "";
Количество = "";
Для сч_Линий = 1 По КоличествоЛинийСтроки Цикл
УзелЛинии = ОбъектСтрока.ПолучитьПодчиненныйПоНомеру(сч_Линий);
Если ( УзелЛинии.Наименование = "Номенклатура" ) Тогда
Номенклатура = УзелЛинии.Значение;
ИначеЕсли ( УзелЛинии.Наименование = "Количество" ) Тогда
Количество = УзелЛинии.Значение;
КонецЕсли;
КонецЦикла;
// Заполняем строку таблицы значений
ТЗнач.НоваяСтрока();
ТЗнач.НомерДок = НомерДок;
ТЗнач.ДатаДок = ДатаДок;
ТЗнач.МестоХранения = СкладОтправитель;
ТЗнач.МестоХраненияВ = СкладПолучатель;
ТЗнач.Номенклатура = Номенклатура;
ТЗнач.Количество = Число(Количество);
КонецЕсли;
КонецЦикла;
КонецЕсли;
Посмотрим на другой способ решения задачи. Если файл загружается целиком через DOM-модель, можно применить методы выборки ВыбратьУзел и ВыбратьУзлы. Это избавляет от громоздких вложенных циклов по каждому уровню иерархии.
ИначеЕсли ( УзелНакладные.Наименование = "Товары" ) Тогда
УзелКорень = Объект.ВыбратьУзел("Товары");
Товары = УзелКорень.ВыбратьУзлы("Строка");
Для ш = 1 По Товары.КоличествоУзлов Цикл
ТекТовар = Товары.ПолучитьУзел(ш - 1);
ТЗнач.НоваяСтрока();
ТЗнач.НомерДок = НомерДок;
ТЗнач.ДатаДок = ДатаДок;
ТЗнач.МестоХранения = СкладОтправитель;
ТЗнач.МестоХраненияВ = СкладПолучатель;
ТЗнач.Номенклатура = ТекТовар.ВыбратьУзел("Номенклатура").Значение;
ТЗнач.Количество = Число(ТекТовар.ВыбратьУзел("Количество").Значение);
КонецЦикла;
КонецЕсли;
Использование компоненты v7plus.dll имеет ряд технических ограничений: она зависит от регистрации системных DLL и выгружает всю память процесса при работе с крупными файлами. Альтернативным и более стабильным вариантом является прямое обращение к Windows-объекту MSXML2.DOMDocument через COM-соединение.
Рассмотрим пример загрузки документа «ДвижениеМПЗ» с помощью XPath-запросов:
ДокXML = СоздатьОбъект("MSXML2.DOMDocument");
ДокXML.async = 0;
Если ДокXML.load("c:\Выгрузка.xml") = 0 Тогда
Предупреждение("Ошибка чтения файла XML!");
Возврат;
КонецЕсли;
СписокДокументов = ДокXML.selectNodes("//Документ");
Для счДок = 0 По СписокДокументов.length - 1 Цикл
УзелДок = СписокДокументов.item(счДок);
НомерДок = УзелДок.selectSingleNode("Номер").text;
ДатаДок = Дата(УзелДок.selectSingleNode("Дата").text);
СкладОтп = СокрЛП(УзелДок.selectSingleNode("СкладОтправитель").text);
СкладПол = СокрЛП(УзелДок.selectSingleNode("СкладПолучатель").text);
// Выбираем все строки текущего документа
СписокСтрок = УзелДок.selectNodes("Товары/Строка");
Для счСтр = 0 По СписокСтрок.length - 1 Цикл
УзелСтр = СписокСтрок.item(счСтр);
ТоварНаименование = СокрЛП(УзелСтр.selectSingleNode("Номенклатура").text);
КоличествоТовара = Число(УзелСтр.selectSingleNode("Количество").text);
// Добавление данных в ТаблицуЗначений или напрямую в документ 1С
КонецЦикла;
КонецЦикла;
При программном создании документов перемещения МПЗ в конфигурации «Производство + Услуги + Бухгалтерия» необходимо учитывать следующие моменты:
СокрЛП() перед поиском элементов в справочниках Справочник.Материалы или Справочник.МестаХранения.АвтоВремя() перед операцией Записать(), чтобы документы не записывались на одно и то же время 00:00:00, что критично для партионного учета.НачатьТранзакцию() — ЗафиксироватьТранзакцию() для предотвращения создания частичных данных при возникновении ошибок поиска номенклатуры.