Как улучшить код обработки XML в УТ 11.5, заполняющий заказ поставщику в 1С?

База знаний Одина — Одинэсника Форумы ODIN — Форум по 1С Предприятию Как улучшить код обработки XML в УТ 11.5, заполняющий заказ поставщику в 1С?

Просмотр 1 ветки ответов
  • Автор
    Сообщения
    • #36972
      Фото аватараOdineski
      Участник

        Проверь код можешь дал рекомендации по улучшению. Надо было в типовой конфигурации Управление Торговлей 11.5 сделать обработку которая из файла с расширение xml Заполняет данные документа заказ поставщику. Прилаживаю фото кода #Область ОбработчикиКомандФормы

        &НаКлиенте
        Процедура ЗаполнитьЗаказ(Команда)

        ФормаДокумента = ЭтотОбъект.ВладелецФормы;
        СсылкаНаДокумент = ЭтотОбъект.ВладелецФормы.Объект.Ссылка;
        ЗаполнитьРасходыНаСервере(СсылкаНаДокумент);

        ФормаДокумента.Прочитать();
        ФормаДокумента.Модифицированность = Истина;

        Закрыть();

        КонецПроцедуры

        &НаКлиенте
        Процедура ЗагрузитьФайл(Команда)

        ОчиститьСообщения();
        Товары.Очистить();
        ДиалогВыбораФайлаАсинх()

        КонецПроцедуры

        #КонецОбласти

        #Область ОбработчикиСобытийЭлементовТаблицыФормыТовары

        &НаКлиенте
        Процедура ТаблицаТоваровПослеУдаления(Элемент)

        ПересчитатьНомераСтрок()

        КонецПроцедуры

        &НаКлиенте
        Процедура ТаблицаТоваровПриНачалеРедактирования(Элемент, НоваяСтрока, Копирование)

        Если НоваяСтрока И Не Копирование Тогда

        ПересчитатьНомераСтрок();

        КонецЕсли;

        КонецПроцедуры

        #КонецОбласти

        #Область СлужебныеПроцедурыИФункции

        &НаКлиенте
        Асинх Процедура ДиалогВыбораФайлаАсинх()

        ДиалогОткрытия = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
        ДиалогОткрытия.МножественныйВыбор = Ложь;
        ДиалогОткрытия.Фильтр = «xml-файлы|*.xml»;
        ДиалогОткрытия.Заголовок = «Выберете Xml файл с данными заказа»;

        РезультатВыбора = Ждать ДиалогОткрытия.ВыбратьАсинх();

        Если РезультатВыбора = Неопределено Тогда
        Возврат;
        КонецЕсли;

        ПутьКФайлу = РезультатВыбора[0];

        ПрочитатьФайлXml(ПутьКФайлу);

        КонецПроцедуры

        &НаКлиенте
        Процедура ПрочитатьФайлXml(ПутьКфайлу)

        ЧтениеXML = Новый ЧтениеXML;
        ЧтениеXML.ОткрытьФайл(ПутьКФайлу);

        ПостроительDOM = Новый ПостроительDOM;
        ДокументDOM = ПостроительDOM.Прочитать(ЧтениеXML);

        // Поиск ИНН поставщика
        ИННПоставщика = «»;

        УзлыСвЮЛУч = ДокументDOM.ПолучитьЭлементыПоИмени(«СвЮЛУч»);

        Для каждого УзелЮЛ Из УзлыСвЮЛУч Цикл
        АтрИНН = УзелЮЛ.Атрибуты.ПолучитьИменованныйЭлемент(«ИННЮЛ»);
        Если АтрИНН <> Неопределено Тогда
        ИННПоставщика = АтрИНН.Значение;
        КонецЕсли;
        КонецЦикла;

        Если ЗначениеЗаполнено(ИННПоставщика) Тогда
        НайденныйКонтрагент = НайтиКонтрагентаПоИНН(ИННПоставщика);
        Если НайденныйКонтрагент <> Неопределено Тогда
        Поставщик = НайденныйКонтрагент;
        Иначе
        Сообщить(«Контрагент с ИНН » + ИННПоставщика + » не найден в базе.»);
        Поставщик = Неопределено;
        КонецЕсли;
        КонецЕсли;

        УзлыСведТов = ДокументDOM.ПолучитьЭлементыПоИмени(«СведТов»);
        НомерСтроки = 1;

        Для каждого УзелТовара Из УзлыСведТов Цикл

        // Достаём атрибуты
        АтрНаимТов = УзелТовара.Атрибуты.ПолучитьИменованныйЭлемент(«НаимТов»);
        АтрНаимЕдИзм = УзелТовара.Атрибуты.ПолучитьИменованныйЭлемент(«НаимЕдИзм»);
        АтрКолТов = УзелТовара.Атрибуты.ПолучитьИменованныйЭлемент(«КолТов»);
        АтрНалСт = УзелТовара.Атрибуты.ПолучитьИменованныйЭлемент(«НалСт»);
        АтрСтТовУчНал = УзелТовара.Атрибуты.ПолучитьИменованныйЭлемент(«СтТовУчНал»);

        // Значения атрибутов
        НаимТов = ?(АтрНаимТов <> Неопределено, АтрНаимТов.Значение, «»);
        НаимЕдИзм = ?(АтрНаимЕдИзм <> Неопределено, АтрНаимЕдИзм.Значение, «»);
        КолТов = ?(АтрКолТов <> Неопределено, Число(АтрКолТов.Значение), 0);
        НалСт = ?(АтрНалСт <> Неопределено, АтрНалСт.Значение, «»);
        СтТовУчНал = ?(АтрСтТовУчНал <> Неопределено, Число(АтрСтТовУчНал.Значение), 0);

        Если АтрНалСт <> Неопределено Тогда
        СтавкаНДС = ПолучитьСтавкуНДС(НалСт);
        КонецЕсли;

        // Достаём КодТов из вложенного ДопСведТов
        КодТов = «»;
        СуммаНДС = 0;

        Для каждого Дочерний Из УзелТовара.ДочерниеУзлы Цикл
        Если Дочерний.ИмяУзла = «ДопСведТов» Тогда
        АтрКодТов = Дочерний.Атрибуты.ПолучитьИменованныйЭлемент(«КодТов»);
        КодТов = АтрКодТов.Значение;

        ИначеЕсли Дочерний.ИмяУзла = «СумНал» Тогда
        // Внутри этого узла лежит ещё один <СумНал> с текстовым значением
        Для каждого Внутренний Из Дочерний.ДочерниеУзлы Цикл
        Если Внутренний.ИмяУзла = «СумНал» Тогда
        СуммаНДС = Число(Внутренний.ТекстовоеСодержимое);
        Прервать; // Внутренний цикл больше не нужен
        КонецЕсли;
        КонецЦикла;
        КонецЕсли;
        КонецЦикла;

        НовСтр = Товары.Добавить();
        НовСтр.НомерСтроки = НомерСтроки;
        НовСтр.Наименование = НаимТов;
        НовСтр.Артикул = КодТов;
        НовСтр.КоличествоУпаковок = КолТов;
        НовСтр.Сумма = СтТовУчНал;
        Если КолТов > 0 Тогда
        НовСтр.Цена = СтТовУчНал / КолТов;
        Иначе
        НовСтр.Цена = 0;
        КонецЕсли;
        НовСтр.СуммаНДС = СуммаНДС;
        НовСтр.СтавкаНДС = СтавкаНДС;
        НовСтр.СуммаСНДС = СтТовУчНал;

        НомерСтроки = НомерСтроки + 1

        КонецЦикла;

        НайденнаяНоменклатура();

        КонецПроцедуры

        &НаСервере
        Процедура НайденнаяНоменклатура()

        Запрос = Новый Запрос;
        Запрос.Текст =
        «ВЫБРАТЬ
        | ДанныеФайла.НомерСтроки КАК НомерСтроки,
        | ДанныеФайла.Артикул КАК Артикул
        |ПОМЕСТИТЬ ВТ_ДанныеФайла
        |ИЗ
        | &ДанныеФайла КАК ДанныеФайла
        |;
        |
        |////////////////////////////////////////////////////////////////////////////////
        |ВЫБРАТЬ
        | Номенклатура.Ссылка КАК Ссылка,
        | Номенклатура.Артикул КАК Артикул
        |ПОМЕСТИТЬ ВТ_НоменклатураДоступная
        |ИЗ
        | Справочник.Номенклатура КАК Номенклатура
        |ГДЕ
        | Номенклатура.ПометкаУдаления = ЛОЖЬ
        | И Номенклатура.ЭтоГруппа = ЛОЖЬ
        |;
        |
        |////////////////////////////////////////////////////////////////////////////////
        |ВЫБРАТЬ
        | ВТ_ДанныеФайла.НомерСтроки КАК НомерСтроки,
        | ВТ_ДанныеФайла.Артикул КАК Артикул,
        | КОЛИЧЕСТВО(ВТ_НоменклатураДоступная.Ссылка) КАК КоличествоСовпадений,
        | ЕСТЬNULL(МАКСИМУМ(ВТ_НоменклатураДоступная.Ссылка), ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)) КАК Номенклатура
        |ИЗ
        | ВТ_ДанныеФайла КАК ВТ_ДанныеФайла
        | ЛЕВОЕ СОЕДИНЕНИЕ ВТ_НоменклатураДоступная КАК ВТ_НоменклатураДоступная
        | ПО ВТ_ДанныеФайла.Артикул = ВТ_НоменклатураДоступная.Артикул
        |
        |СГРУППИРОВАТЬ ПО
        | ВТ_ДанныеФайла.НомерСтроки,
        | ВТ_ДанныеФайла.Артикул»;

        Запрос.УстановитьПараметр(«ДанныеФайла», Товары.Выгрузить());
        РезультатЗапроса = Запрос.Выполнить();

        Выборка = РезультатЗапроса.Выбрать();
        Пока Выборка.Следующий() Цикл
        СтруктураПоиска = Новый Структура(«НомерСтроки», Выборка.НомерСтроки);
        НайденныеСтроки = Товары.НайтиСтроки(СтруктураПоиска);
        СтрокаТЧ = НайденныеСтроки[0];

        Если Выборка.КоличествоСовпадений = 0 Тогда

        СтрокаТЧ.Номенклатура = Справочники.Номенклатура.ПустаяСсылка();

        Сообщение = Новый СообщениеПользователю;
        Сообщение.Текст = СтрШаблон(«Артикул %1 не найден в базе. Строка № %2», Выборка.Артикул, Выборка.НомерСтроки);
        Сообщение.Сообщить();

        ИначеЕсли Выборка.КоличествоСовпадений = 1 Тогда

        СтрокаТЧ.Номенклатура = Выборка.Номенклатура;

        ИначеЕсли Выборка.КоличествоСовпадений > 1 Тогда

        СтрокаТЧ.Номенклатура = Справочники.Номенклатура.ПустаяСсылка();

        Сообщение = Новый СообщениеПользователю;
        Сообщение.Текст = СтрШаблон(«Для артикула %1 найдено %2 номенклатур. Заполните вручную. Строка № %3», Выборка.Артикул, Выборка.КоличествоСовпадений, Выборка.НомерСтроки);
        Сообщение.Сообщить();

        КонецЕсли;
        КонецЦикла

        КонецПроцедуры

        &НаСервереБезКонтекста
        Функция НайтиКонтрагентаПоИНН(ИННКонтрагента)

        Запрос = Новый Запрос;
        Запрос.Текст =
        «ВЫБРАТЬ
        | Контрагенты.Партнер.Ссылка КАК ПартнерСсылка
        |ИЗ
        | Справочник.Контрагенты КАК Контрагенты
        |ГДЕ
        | Контрагенты.ИНН = &ИНН
        | И Контрагенты.ПометкаУдаления = ЛОЖЬ»;

        Запрос.УстановитьПараметр(«ИНН», ИННКонтрагента);

        РезультатЗапроса = Запрос.Выполнить();

        Выборка = РезультатЗапроса.Выбрать();

        Если РезультатЗапроса.Пустой() Тогда
        Возврат Неопределено
        Иначе
        Выборка.Следующий();
        Возврат Выборка.ПартнерСсылка;
        КонецЕсли;

        КонецФункции

        &НаСервереБезКонтекста
        Функция ПолучитьСтавкуНДС(НалСТ)

        Запрос = Новый Запрос;
        Запрос.Текст =
        «ВЫБРАТЬ
        | СтавкиНДС.Ссылка КАК Ссылка
        |ИЗ
        | Справочник.СтавкиНДС КАК СтавкиНДС
        |ГДЕ
        | СтавкиНДС.Наименование = &Наименование»;

        Запрос.УстановитьПараметр(«Наименование», НалСТ);

        РезультатЗапроса = Запрос.Выполнить();

        Выборка = РезультатЗапроса.Выбрать();

        Если Выборка.Следующий() Тогда
        Возврат Выборка.Ссылка;
        КонецЕсли;

        Возврат Неопределено;

        КонецФункции

        &НаСервере
        Процедура ЗаполнитьРасходыНаСервере(СсылкаНаДокумент)

        ДокОбъект = СсылкаНаДокумент.ПолучитьОбъект();
        ДокОбъект.Товары.Очистить();
        ДокОбъект.Партнер = Поставщик;
        ДокОбъект.ЦенаВключаетНДС = Истина;

        Для Каждого ТекСтр Из Товары Цикл

        Если ТекСтр.Номенклатура = ПредопределенноеЗначение(«Справочник.Номенклатура.ПустаяСсылка») Тогда
        Продолжить;
        КонецЕсли;

        НовСтр = ДокОбъект.Товары.Добавить();
        ЗаполнитьЗначенияСвойств(НовСтр,ТекСтр);

        КонецЦикла;

        ДокОбъект.Записать();

        КонецПроцедуры

        &НаКлиенте
        Процедура ПересчитатьНомераСтрок()

        Для Номер = 1 По Товары.Количество() Цикл
        Товары[Номер — 1].НомерСтроки = Номер;
        КонецЦикла;

        КонецПроцедуры

        #КонецОбласти

      • #36973
        bot_avaOdin — Одинэсник
        Участник

          Код в целом рабочий, но есть несколько рекомендаций и замечаний по улучшению и повышению надежности, читаемости и производительности:

          1. Обработка ошибок при чтении XML
          Сейчас при открытии и чтении XML-файла нет обработки ошибок. Если файл поврежден или невалиден, будет ошибка.
          Рекомендую обернуть чтение в Попытка...Исключение и выводить понятное сообщение пользователю.

          2. Проверка наличия узлов и атрибутов
          В коде много обращений к атрибутам и узлам без проверки на Неопределено. Лучше добавить проверки, чтобы избежать ошибок.
          Например:

          3. Оптимизация поиска контрагента по ИНН
          В функции НайтиКонтрагентаПоИНН возвращается Контрагенты.Партнер.Ссылка. Если в базе есть прямые ссылки на контрагентов, лучше возвращать их напрямую, чтобы не путать с партнерами.
          Также можно добавить индекс по ИНН в справочник для ускорения поиска.

          4. Использование структуры для хранения данных товаров
          Сейчас данные товаров хранятся в табличной части Товары, а потом выгружаются в запрос. Можно рассмотреть вариант использования временной таблицы или структуры для хранения данных перед запросом, чтобы не зависеть от формы.

          5. Обработка ситуации с несколькими номенклатурами по одному артикулу
          В процедуре НайденнаяНоменклатура при множественных совпадениях выводится сообщение, но не предлагается способ выбора. Можно добавить форму выбора номенклатуры пользователем.

          6. Пересчет номеров строк
          Процедура ПересчитатьНомераСтрок вызывается при удалении и добавлении строк — это хорошо. Можно добавить вызов после загрузки из XML, чтобы гарантировать корректные номера.

          7. Использование ЗаполнитьЗначенияСвойств
          В процедуре ЗаполнитьРасходыНаСервере используется ЗаполнитьЗначенияСвойств(НовСтр,ТекСтр). Убедитесь, что свойства в табличной части и в документе совпадают по именам и типам, иначе возможны ошибки.

          8. Закрытие формы после заполнения
          В процедуре ЗаполнитьЗаказ вызывается Закрыть(). Если это метод формы, то всё ок, но лучше явно указать ЭтотОбъект.ВладелецФормы.Закрыть(), чтобы не было путаницы.

          9. Асинхронность загрузки файла
          Использование асинхронного диалога выбора файла — хорошо, но после загрузки XML желательно блокировать интерфейс или показывать индикатор загрузки, чтобы пользователь понимал, что идет обработка.

          10. Кодировка файла
          При открытии XML файла через ЧтениеXML.ОткрытьФайл(ПутьКФайлу) можно явно указать кодировку, если есть сомнения, например "UTF-8".

          11. Комментарии и форматирование
          Добавьте больше комментариев, особенно в сложных местах, чтобы облегчить поддержку.
          Также можно разбить длинные процедуры на более мелкие.

          12. Проверка на пустой ИНН
          Если ИНН не найден или пустой, стоит обработать этот случай, например, предложить выбрать контрагента вручную.

          13. Проверка на пустой список товаров
          Если в XML нет товаров, стоит предупредить пользователя.

          14. Оптимизация запроса по номенклатуре
          В запросе НайденнаяНоменклатура можно добавить индекс по артикулу, если его нет, для ускорения.

          15. Использование &НаСервереБезКонтекста
          Для функций, которые не зависят от контекста сеанса, это хорошо. Убедитесь, что все такие функции действительно не используют контекст.

          Если кратко, то основные улучшения — это обработка ошибок, проверки на Неопределено, улучшение UX (индикаторы загрузки, выбор при неоднозначностях), и оптимизация запросов.

          Если нужно, могу помочь с конкретными доработками по коду.

      Просмотр 1 ветки ответов
      • Для ответа в этой теме необходимо авторизоваться.
      База знаний 1С