Загрузка из Эксель в документ Производство. УНФ (проходит аудит Fresh)
Загрузка данных из Excel в документ «Производство» УНФ — рутинная задача, которая на поверку оказывается минным полем для аудита Fresh. Стандартные циклы по строкам с созданием документов «в лоб» гарантированно проваливают проверку. Разбираем, почему объектная модель не подходит для массовой загрузки, и даём рабочий шаблон, проходящий аудит без замечаний.
Почему простая загрузка через объектную модель не проходит аудит Fresh
Большинство специалистов пишут обработку загрузки «на коленке»: открывают Excel через COMОбъект, в цикле обходят строки, для каждой строки создают новый документ «Производство» и сразу его записывают. Такой код работает, но аудит Fresh фиксирует сразу несколько нарушений.
Ловушка с блокировками и транзакциями
При записи каждого документа в отдельной транзакции (а платформа делает это автоматически при вызове Записать()) на время записи блокируются таблицы документа и связанных регистров. Если в Excel 500 строк — будет 500 коротких блокировок. Для аудита Fresh это сигнал «неоптимальное использование блокировок». Кроме того, при ошибке на 300-й строке первые 299 документов уже записаны — откатить их без ручного удаления невозможно.
Аудит Fresh фиксирует длительные блокировки при массовой загрузке через объектную модель. Даже если обработка работает, она будет помечена как «требует доработки».
Пример типичного «плохого» кода:
Процедура ЗагрузитьПлохо(АдресФайла)
Excel = Новый COMОбъект("Excel.Application");
Книга = Excel.Workbooks.Open(АдресФайла);
Лист = Книга.Worksheets(1);
НомерСтроки = 2; // первая строка — заголовок
Пока НЕ ПустаяСтрока(Лист.Cells(НомерСтроки, 1).Value) Цикл
Док = Документы.Производство.СоздатьДокумент();
Док.Дата = ТекущаяДата();
Док.ВидПроизводства = Перечисления.ВидыПроизводства.Собственное;
// заполнение табличной части Материалы
СтрокаТЧ = Док.Материалы.Добавить();
СтрокаТЧ.Номенклатура = Справочники.Номенклатура.НайтиПоКоду(Лист.Cells(НомерСтроки, 2).Value);
СтрокаТЧ.Количество = Лист.Cells(НомерСтроки, 3).Value;
// ... ещё поля
Док.Записать();
НомерСтроки = НомерСтроки + 1;
КонецЦикла;
Excel.Quit();
КонецПроцедуры
Здесь каждая итерация — отдельная транзакция, нет обработки ошибок, нет контроля дублей. Аудит Fresh найдёт как минимум: «Отсутствует транзакционная обёртка для группы операций», «Множественные блокировки таблиц», «Риск потери данных при сбое».
Правильный подход: пакетная загрузка через таблицу значений и один документ
Чтобы пройти аудит Fresh, нужно загрузить все данные из Excel в таблицу значений, затем создать один документ «Производство» и заполнить его табличную часть из этой таблицы. Весь процесс — в одной явной транзакции. Так мы получаем одну блокировку, атомарность и возможность отката.
Формирование табличной части документа через загрузку из Excel
Алгоритм:
- Прочитать Excel в
ТаблицаЗначений(через COMОбъект илиЧтениеФайлас парсингом). - Создать один документ «Производство».
- В цикле по строкам таблицы значений заполнить табличную часть документа.
- Записать документ внутри блока
НачатьТранзакцию() / ЗафиксироватьТранзакцию().
Процедура ЗагрузитьПравильно(АдресФайла)
// 1. Загрузка данных в ТЗ
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("КодНоменклатуры");
ТЗ.Колонки.Добавить("Количество");
// ... другие колонки
Excel = Новый COMОбъект("Excel.Application");
Книга = Excel.Workbooks.Open(АдресФайла);
Лист = Книга.Worksheets(1);
НомерСтроки = 2;
Пока НЕ ПустаяСтрока(Лист.Cells(НомерСтроки, 1).Value) Цикл
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.КодНоменклатуры = Лист.Cells(НомерСтроки, 2).Value;
НоваяСтрока.Количество = Лист.Cells(НомерСтроки, 3).Value;
НомерСтроки = НомерСтроки + 1;
КонецЦикла;
Excel.Quit();
// 2. Создание документа в транзакции
НачатьТранзакцию();
Попытка
Док = Документы.Производство.СоздатьДокумент();
Док.Дата = ТекущаяДата();
Док.ВидПроизводства = Перечисления.ВидыПроизводства.Собственное;
Для Каждого СтрокаТЗ Из ТЗ Цикл
СтрокаТЧ = Док.Материалы.Добавить();
СтрокаТЧ.Номенклатура = Справочники.Номенклатура.НайтиПоКоду(СтрокаТЗ.КодНоменклатуры);
СтрокаТЧ.Количество = СтрокаТЗ.Количество;
КонецЦикла;
Док.Записать(РежимЗаписиДокумента.Проведение);
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ЗаписьЖурналаРегистрации("Ошибка загрузки", УровеньЖурналаРегистрации.Ошибка,,,
КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
ВызватьИсключение "Не удалось загрузить данные: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
КонецПопытки;
КонецПроцедуры
Ключевое отличие: одна транзакция на весь объём данных. Аудит Fresh не найдёт лишних блокировок, а при ошибке все изменения откатываются.
Нюансы для УНФ: спецификация и материалы
Документ «Производство» в УНФ содержит несколько табличных частей: «Материалы», «Продукция», «Возвратные отходы». При загрузке из Excel часто путают, в какую часть писать. Аудит Fresh проверяет соответствие структуре документа — если вы попытаетесь записать продукцию в материалы, будет ошибка.
Особенности заполнения состава продукции
В Excel обычно одна строка — одна позиция материала. Но для продукции нужно указать количество выпускаемой номенклатуры. Лучше разделить листы Excel: один лист для материалов, другой для продукции. Либо использовать колонку-признак «Тип строки».
Пример сопоставления номенклатуры: используйте НайтиПоКоду или НайтиПоНаименованию. Если номенклатура не найдена — аудит Fresh зафиксирует «Обращение к несуществующему элементу справочника». Обязательно проверяйте результат поиска:
Номенклатура = Справочники.Номенклатура.НайтиПоКоду(СтрокаТЗ.КодНоменклатуры);
Если Номенклатура = Справочники.Номенклатура.ПустаяСсылка() Тогда
ВызватьИсключение "Номенклатура с кодом " + СтрокаТЗ.КодНоменклатуры + " не найдена";
КонецЕсли;
Сравнение подходов по ключевым метрикам аудита Fresh:
| Критерий | Объектный (цикл по строкам) | Пакетный (одна транзакция) |
|---|---|---|
| Количество транзакций | N (по числу строк) | 1 |
| Блокировки таблиц | Множественные, короткие | Одна длительная (но допустимая) |
| Возможность отката при ошибке | Нет (частично записанные документы остаются) | Да (полный откат) |
| Прохождение аудита Fresh | Не проходит (нарушение 2.3.1, 2.3 |
Попробовать НОПик →