При переходе на современные конфигурации «1С:Предприятие 8.3», такие как «Зарплата и управление персоналом 3.1» (ЗУП), многие разработчики сталкиваются с трудностями при переносе старого кода выгрузки в Excel. Традиционный подход с использованием COM-объекта Excel.Application, который отлично работал в обычных формах (например, в ЗУП 2.5 или Бухгалтерии 2.0), в управляемом приложении начинает вызывать ошибки или просто «тихо» завершается без каких-либо видимых результатов.
В этой статье мы подробно разберем, почему старый код отказывается работать на сервере, проанализируем архитектурные особенности клиент-серверного взаимодействия в 1С и рассмотрим несколько надежных способов реализации выгрузки данных в форматы Excel (XLS/XLSX).
Рассмотрим исходную ситуацию. Разработчик пытается запустить процедуру выгрузки под директивой компиляции &НаСервере. Внутри кода создается объект Новый COMObject("Excel.Application"), открывается файл, заполняются ячейки, после чего выполняется попытка сохранения. Однако на практике ничего не происходит: файл не создается, а в некоторых случаях система выдает ошибку о неопределенном типе или ошибку создания COM-объекта.
Давайте проанализируем основные причины такого поведения:
Excel.Application, на этом сервере должен быть установлен лицензионный пакет MS Office. Зачастую системные администраторы не устанавливают офисные пакеты на серверные ОС из соображений безопасности и экономии ресурсов.usr1cv8) не имеет прав на интерактивный запуск DCOM-компонентов. При попытке инициализировать Excel процесс просто зависает в диспетчере задач сервера.&НаКлиенте) для решения проблемы с COM приводит к другой ошибке: Тип не определен (Запрос). Это происходит потому, что в тонком клиенте базы данных 1С недоступен класс Запрос. Все обращения к базе данных и выборка информации должны происходить строго на сервере.Для решения этой задачи мы можем пойти двумя путями: адаптировать классический метод с COM-объектом под управляемые формы или использовать более современный и производительный штатный механизм 1С без привлечения стороннего ПО. Рассмотрим оба варианта пошагово.
Если вам по какой-то причине необходимо использовать именно COM-объект (например, требуется сложное форматирование готового Excel-файла по специфическому шаблону), код необходимо четко разделить на серверную и клиентскую части.
Логика работы должна быть следующей:
Рассмотрим пример реализации такого разделения:
&НаСервере
Функция ПолучитьДанныеДляВыгрузки(СДаты, ПоДату, Организация)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ОтражениеЗарплаты.Ссылка.Дата КАК Дата,
| ОтражениеЗарплаты.Ссылка.Организация КАК Организация,
| ОтражениеЗарплаты.ФизическоеЛицо КАК ФизическоеЛицо,
| ОтражениеЗарплаты.Подразделение КАК Подразделение,
| ОтражениеЗарплаты.ВидНачисленияОплатыТрудаДляНУ КАК ВидНачисленияОплатыТрудаДляНУ,
| ОтражениеЗарплаты.Сумма КАК Сумма
|ИЗ
| Документ.ОтражениеЗарплатыВБухучете.НачисленнаяЗарплатаИВзносы КАК ОтражениеЗарплаты
|ГДЕ
| ОтражениеЗарплаты.Ссылка.Дата МЕЖДУ &СДаты И &ПоДату
| И ОтражениеЗарплаты.Ссылка.Организация = &Организация";
Запрос.УстановитьПараметр("СДаты", СДаты);
Запрос.УстановитьПараметр("ПоДату", ПоДату);
Запрос.УстановитьПараметр("Организация", Организация);
Возврат Запрос.Выполнить().Выгрузить();
КонецФункции
&НаКлиенте
Процедура ВыгрузкаВЭксель()
// Получаем данные с сервера
ТаблицаДанных = ПолучитьДанныеДляВыгрузки(Объект.СДаты, Объект.ПоДату, Объект.Организация);
Если ТаблицаДанных.Количество() = 0 Тогда
ПоказатьПредупреждение(, "Нет данных для выгрузки за указанный период!");
Возврат;
КонецЕсли;
// Инициализируем Excel на клиенте
Попытка
Excel = Новый COMObject("Excel.Application");
Исключение
ПоказатьПредупреждение(, "Не удалось запустить Microsoft Excel. Проверьте, установлена ли программа.");
Возврат;
КонецПопытки;
Excel.Visible = Ложь;
Excel.DisplayAlerts = Ложь;
Книга = Excel.Workbooks.Add();
Лист = Книга.WorkSheets(1);
// Заполняем шапку таблицы
Лист.Cells(1, 1).Value = "Дата";
Лист.Cells(1, 2).Value = "Организация";
Лист.Cells(1, 3).Value = "Физическое лицо";
Лист.Cells(1, 4).Value = "Подразделение";
Лист.Cells(1, 5).Value = "Вид начисления";
Лист.Cells(1, 6).Value = "Сумма";
НомерСтроки = 2;
Для Каждого СтрокаДанных Из ТаблицаДанных Цикл
Лист.Cells(НомерСтроки, 1).Value = СтрокаДанных.Дата;
Лист.Cells(НомерСтроки, 2).Value = Строка(СтрокаДанных.Организация);
Лист.Cells(НомерСтроки, 3).Value = Строка(СтрокаДанных.ФизическоеЛицо);
Лист.Cells(НомерСтроки, 4).Value = Строка(СтрокаДанных.Подразделение);
Лист.Cells(НомерСтроки, 5).Value = Строка(СтрокаДанных.ВидНачисленияОплатыТрудаДляНУ);
Лист.Cells(НомерСтроки, 6).Value = СтрокаДанных.Сумма;
НомерСтроки = НомерСтроки + 1;
КонецЦикла;
// Сохраняем файл
ПолныйПуть = Объект.ПутьКФайлу + "\Выгрузка_ЗУП.xlsx";
Попытка
// Параметр 51 соответствует формату XLSX в современном Excel
Книга.SaveAs(ПолныйПуть, 51);
ПоказатьПредупреждение(, "Выгрузка успешно завершена! Файл сохранен по пути: " + ПолныйПуть);
Исключение
ПоказатьПредупреждение(, "Ошибка при сохранении файла: " + ОписаниеОшибки());
КонецПопытки;
// Обязательно закрываем процесс Excel
Excel.Quit();
Excel = Неопределено;
КонецПроцедуры
Важное примечание по оптимизации COM: Поячеечное заполнение файла в цикле (вызовы вида Лист.Cells(Row, Col).Value = ...) работает медленно на больших объемах данных, так как каждое обращение к ячейке инициирует межпроцессный вызов между 1С и Excel. Если строк больше нескольких тысяч, этот процесс может занять значительное время.
Для ускорения работы рекомендуется использовать объект COMSafeArray. С его помощью можно выгрузить всю таблицу данных в массив 1С, передать его в COM-объект и записать в диапазон ячеек Excel одной инструкцией, что ускоряет процесс в десятки раз.
Наиболее надежным, современным и быстрым способом выгрузки данных в формате Excel является использование встроенного механизма платформы 1С — объекта ТабличныйДокумент. Этот подход полностью исключает необходимость использования COM-технологий и имеет массу преимуществ:
Рассмотрим, как быстро реализовать такую выгрузку с использованием ПостроителяОтчета для автоматического формирования структуры таблицы:
&НаСервере
Функция СформироватьТабличныйДокументНаСервере(СДаты, ПоДату, Организация)
ТабДок = Новый ТабличныйДокумент;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ОтражениеЗарплаты.Ссылка.Дата КАК [Дата документа],
| ОтражениеЗарплаты.Ссылка.Организация КАК Организация,
| ОтражениеЗарплаты.ФизическоеЛицо КАК [Сотрудник],
| ОтражениеЗарплаты.Подразделение КАК Подразделение,
| ОтражениеЗарплаты.ВидНачисленияОплатыТрудаДляНУ КАК [Вид начисления],
| ОтражениеЗарплаты.Сумма КАК Сумма
|ИЗ
| Документ.ОтражениеЗарплатыВБухучете.НачисленнаяЗарплатаИВзносы КАК ОтражениеЗарплаты
|ГДЕ
| ОтражениеЗарплаты.Ссылка.Дата МЕЖДУ &СДаты И &ПоДату
| И ОтражениеЗарплаты.Ссылка.Организация = &Организация";
Запрос.УстановитьПараметр("СДаты", СДаты);
Запрос.УстановитьПараметр("ПоДату", ПоДату);
Запрос.УстановитьПараметр("Организация", Организация);
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
Возврат Неопределено;
КонецЕсли;
// Используем ПостроительОтчета для быстрого вывода таблицы в ТабДок
Построитель = Новый ПостроительОтчета;
Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(РезультатЗапроса.Выгрузить());
Построитель.Вывести(ТабДок);
Возврат ТабДок;
КонецФункции
&НаКлиенте
Процедура ВыгрузитьЧерезТабДок()
ТабДок = СформироватьТабличныйДокументНаСервере(Объект.СДаты, Объект.ПоДату, Объект.Организация);
Если ТабДок = Неопределено Тогда
ПоказатьПредупреждение(, "Нет данных для выгрузки!");
Возврат;
КонецЕсли;
ИмяФайла = Объект.ПутьКФайлу + "\Выгрузка_ЗУП_Нативная.xlsx";
Попытка
// Метод Записать работает на клиенте и поддерживает выгрузку напрямую в XLSX
ТабДок.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.XLSX);
ПоказатьПредупреждение(, "Файл успешно сохранен по пути: " + ИмяФайла);
Исключение
ПоказатьПредупреждение(, "Не удалось сохранить файл. Ошибка: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Этот способ является стандартом разработки под управляемый интерфейс 1С. Полученный файл .xlsx является полноценным валидным документом OpenXML, который без проблем откроется в Microsoft Excel, LibreOffice, Google Таблицах, а также сможет быть прочитан любой обработкой загрузки в вашей целевой базе данных (например, в «Бухгалтерии предприятия 2.0»).
Подводя итоги, дадим несколько практических советов для эффективной выгрузки данных:
&НаСервере) и заполнение Excel (выполняется &НаКлиенте).