При разработке собственных отчетов и обработок выгрузки данных по основным средствам в Microsoft Excel разработчики часто сталкиваются с ситуацией, когда классификационные признаки (группа ОС, амортизационная группа) заполняются некорректно либо остаются пустыми. Рассмотрим причины возникновения этой проблемы, типичные ошибки при обращении к свойствам объектов и способы оптимизации программного кода.
Выясним, из-за чего в результирующем файле поле группы может оставаться пустым или вызывать исключение во время выполнения программы:
ОсновныеСредства папка, в которой лежит элемент, доступна через системный реквизит Родитель (тип СправочникСсылка.ОсновныеСредства). В то же время реквизит ГруппаОС (или ГруппаУчетаОС в зависимости от редакции конфигурации) представляет собой ссылку на системное перечисление (ПеречислениеСсылка.ГруппыОС). Если требуется вывести именно папку каталога, а программа обращается к реквизиту перечисления (или наоборот), на выходе получается пустое значение.ОсновноеСредство.ГруппаОС.Наименование платформа генерирует исключительную ситуацию «Поле объекта не обнаружено (Наименование)», поскольку у элементов перечислений нет свойства Наименование — строковое представление перечисления получается с помощью стандартной функции Строка() или конструкции ПРЕДСТАВЛЕНИЕ() в тексте запроса.ВЫБРАТЬ, а запрашивается в цикле через точку от результата выборки (например, РезультатЗапроса.ОсновноеСредство.ГруппаОС), платформа выполняет неявный SQL-запрос к СУБД для каждой строки (проблема N+1). Если связь нарушена или поле не заполнено в самом элементе, значение будет пустым.Проанализируем структуру исходного запроса. Чтобы исключить неявные обращения к базе данных внутри цикла и сразу получить корректные строковые представления, вынесем все реквизиты в текст запроса. Также обязательно приведем составной тип субконто к типу Справочник.ОсновныеСредства с помощью оператора ВЫРАЗИТЬ.
Посмотрим на пример оптимизированного текста запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Хозрасчетный.Регистратор КАК Регистратор,
| Хозрасчетный.Регистратор.Номер КАК НомерДокумента,
| Хозрасчетный.Регистратор.Дата КАК ДатаДокумента,
| Хозрасчетный.СчетДт.Код КАК СчетДтКод,
| Хозрасчетный.Сумма КАК Сумма,
| Хозрасчетный.СуммаНУДт КАК СуммаНУДт,
| Хозрасчетный.СуммаНУКт КАК СуммаНУКт,
| ВЫРАЗИТЬ(Хозрасчетный.СубконтоКт1 КАК Справочник.ОсновныеСредства) КАК ОсновноеСредство,
| ПРЕДСТАВЛЕНИЕ(ВЫРАЗИТЬ(Хозрасчетный.СубконтоКт1 КАК Справочник.ОсновныеСредства).НаименованиеПолное) КАК ОСПолноеНаименование,
| ПРЕДСТАВЛЕНИЕ(ВЫРАЗИТЬ(Хозрасчетный.СубконтоКт1 КАК Справочник.ОсновныеСредства).АмортизационнаяГруппа) КАК АмортизационнаяГруппа,
| ПРЕДСТАВЛЕНИЕ(ВЫРАЗИТЬ(Хозрасчетный.СубконтоКт1 КАК Справочник.ОсновныеСредства).ГруппаОС) КАК ГруппаУчетаОС,
| ПРЕДСТАВЛЕНИЕ(ВЫРАЗИТЬ(Хозрасчетный.СубконтоКт1 КАК Справочник.ОсновныеСредства).Родитель) КАК ИерархическаяГруппа,
| ПервоначальныеСведения.ПервоначальнаяСтоимость КАК ПервоначальнаяСтоимость,
| ПервоначальныеСведения.Период КАК ДатаВвода
|ИЗ
| РегистрБухгалтерии.Хозрасчетный.ДвиженияССубконто(
| &НачалоПериода,
| &КонецПериода,
| СчетДт В ИЕРАРХИИ (ЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.ОсновноеПроизводство), ЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.ОбщепроизводственныеРасходы), ЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.ОбщехозяйственныеРасходы))
| И СчетКт = ЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.АмортизацияОС_01),
| ,
| ) КАК Хозрасчетный
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ПервоначальныеСведенияОСБухгалтерскийУчет КАК ПервоначальныеСведения
| ПО (ПервоначальныеСведения.ОсновноеСредство = ВЫРАЗИТЬ(Хозрасчетный.СубконтоКт1 КАК Справочник.ОсновныеСредства))
|ГДЕ
| Хозрасчетный.Сумма <> 0";
Запрос.УстановитьПараметр("НачалоПериода", НачалоМесяца(ДатаНачала));
Запрос.УстановитьПараметр("КонецПериода", КонецМесяца(ДатаКонца));
Выборка = Запрос.Выполнить().Выбрать();
Разберем процесс заполнения листа книги Excel. Поскольку строковые представления всех перечислений и ссылок мы уже сформировали в запросе с помощью функции ПРЕДСТАВЛЕНИЕ(), в цикле выгрузки отпадает необходимость вызывать функции преобразования и обращаться к свойствам объектов через точку.
Посмотрим на пример заполнения строк:
Попытка
Excel = Новый COMОбъект("Excel.Application");
Исключение
Сообщить("Не удалось запустить Excel. Проверьте установку офисного пакета.");
Возврат;
КонецПопытки;
Книга = Excel.Workbooks.Open("C:\Temp\ExportOS.xlsx");
Лист = Книга.Sheets(1);
НомерСтроки = 1;
Пока Выборка.Следующий() Цикл
НомерСтроки = НомерСтроки + 1;
// Номер и дата регистратора
Лист.Cells(НомерСтроки, 1).NumberFormat = "@";
Лист.Cells(НомерСтроки, 1).Value = Выборка.НомерДокумента;
Лист.Cells(НомерСтроки, 2).NumberFormat = "ДД.ММ.ГГГГ";
Лист.Cells(НомерСтроки, 2).Value = Выборка.ДатаДокумента;
// Суммовые показатели
Лист.Cells(НомерСтроки, 10).NumberFormat = "0,00";
Лист.Cells(НомерСтроки, 10).Value = Выборка.Сумма;
// Сведения по основному средству
Лист.Cells(НомерСтроки, 14).NumberFormat = "@";
Лист.Cells(НомерСтроки, 14).Value = Выборка.ОСПолноеНаименование;
Лист.Cells(НомерСтроки, 15).NumberFormat = "@";
Лист.Cells(НомерСтроки, 15).Value = Выборка.АмортизационнаяГруппа;
Лист.Cells(НомерСтроки, 16).NumberFormat = "ДД.ММ.ГГГГ";
Лист.Cells(НомерСтроки, 16).Value = Выборка.ДатаВвода;
Лист.Cells(НомерСтроки, 17).NumberFormat = "0,00";
Лист.Cells(НомерСтроки, 17).Value = Выборка.ПервоначальнаяСтоимость;
// Классификационная группа учета (перечисление)
Лист.Cells(НомерСтроки, 18).NumberFormat = "@";
Лист.Cells(НомерСтроки, 18).Value = Выборка.ГруппаУчетаОС;
// Папка в справочнике (если нужна иерархия каталога)
Лист.Cells(НомерСтроки, 19).NumberFormat = "@";
Лист.Cells(НомерСтроки, 19).Value = Выборка.ИерархическаяГруппа;
КонецЦикла;
Книга.Save();
Excel.Quit();
Рассмотрим альтернативу работе через COMОбъект("Excel.Application"). Прямая поячеечная запись в COM-объект в больших циклах работает медленно и требует обязательного наличия установленного Excel на рабочем месте.
Рекомендуется формировать отчет с помощью встроенного объекта ТабличныйДокумент и сохранять его штатными средствами 1С:
ТабДок = Новый ТабличныйДокумент;
// Заполнение ТабДок данными отчета или макета...
// Сохранение готовой таблицы напрямую в XLSX без использования Excel
ТабДок.Записать("C:\Temp\ExportOS.xlsx", ТипФайлаТабличногоДокумента.XLSX);
Такой подход выполняется в разы быстрее, работает в среде Linux, в веб-клиенте и на сервере без вызова внешних компонентов.