Как исправить ошибку и выгрузить данные в Excel из 1С:ЗУП 3.1 на управляемых формах?

Программист 1С v8.3 (Управляемые формы) 1С:Зарплата и Управление Персоналом IT и автоматизация бизнеса
← На главную

При переходе на современные конфигурации «1С:Предприятие 8.3», такие как «Зарплата и управление персоналом 3.1» (ЗУП), многие разработчики сталкиваются с трудностями при переносе старого кода выгрузки в Excel. Традиционный подход с использованием COM-объекта Excel.Application, который отлично работал в обычных формах (например, в ЗУП 2.5 или Бухгалтерии 2.0), в управляемом приложении начинает вызывать ошибки или просто «тихо» завершается без каких-либо видимых результатов.

В этой статье мы подробно разберем, почему старый код отказывается работать на сервере, проанализируем архитектурные особенности клиент-серверного взаимодействия в 1С и рассмотрим несколько надежных способов реализации выгрузки данных в форматы Excel (XLS/XLSX).

Выясняем причину: почему код не работает На Сервере?

Рассмотрим исходную ситуацию. Разработчик пытается запустить процедуру выгрузки под директивой компиляции &НаСервере. Внутри кода создается объект Новый COMObject("Excel.Application"), открывается файл, заполняются ячейки, после чего выполняется попытка сохранения. Однако на практике ничего не происходит: файл не создается, а в некоторых случаях система выдает ошибку о неопределенном типе или ошибку создания COM-объекта.

Давайте проанализируем основные причины такого поведения:

  1. Отсутствие установленного Microsoft Excel на сервере. Сервер приложений 1С физически работает на отдельной машине (сервере). Чтобы там инициализировался COM-объект Excel.Application, на этом сервере должен быть установлен лицензионный пакет MS Office. Зачастую системные администраторы не устанавливают офисные пакеты на серверные ОС из соображений безопасности и экономии ресурсов.
  2. Ограничение прав системной службы 1С. Даже если MS Excel установлен на сервере, служба «1С:Предприятие» (обычно запускаемая от имени специального пользователя, например, usr1cv8) не имеет прав на интерактивный запуск DCOM-компонентов. При попытке инициализировать Excel процесс просто зависает в диспетчере задач сервера.
  3. Разрядность систем. Если сервер 1С работает в 64-битном режиме (x64), а на сервере установлен 32-битный MS Office, прямое создание COM-объекта завершится ошибкой совместимости. Потребуется сложная настройка COM+ приложений в операционной системе Windows Server.
  4. Конфликт контекстов выполнения. Попытка перенести весь код на клиент (директива &НаКлиенте) для решения проблемы с COM приводит к другой ошибке: Тип не определен (Запрос). Это происходит потому, что в тонком клиенте базы данных 1С недоступен класс Запрос. Все обращения к базе данных и выборка информации должны происходить строго на сервере.

Для решения этой задачи мы можем пойти двумя путями: адаптировать классический метод с COM-объектом под управляемые формы или использовать более современный и производительный штатный механизм 1С без привлечения стороннего ПО. Рассмотрим оба варианта пошагово.

Решение №1: Клиент-серверное разделение для работы с COM-объектом

Если вам по какой-то причине необходимо использовать именно COM-объект (например, требуется сложное форматирование готового Excel-файла по специфическому шаблону), код необходимо четко разделить на серверную и клиентскую части.

Логика работы должна быть следующей:

  1. На клиенте пользователь инициирует процесс и указывает путь для сохранения файла.
  2. Программа обращается к серверной функции, которая выполняет запрос к базе данных, формирует таблицу значений с нужной информацией и возвращает эту таблицу на клиент.
  3. На клиенте (где гарантированно установлен Microsoft Excel и есть права на запуск локальных приложений) создается COM-объект, перебираются строки полученной таблицы и записываются в файл.

Рассмотрим пример реализации такого разделения:


&НаСервере
Функция ПолучитьДанныеДляВыгрузки(СДаты, ПоДату, Организация)
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    ОтражениеЗарплаты.Ссылка.Дата КАК Дата,
        |    ОтражениеЗарплаты.Ссылка.Организация КАК Организация,
        |    ОтражениеЗарплаты.ФизическоеЛицо КАК ФизическоеЛицо,
        |    ОтражениеЗарплаты.Подразделение КАК Подразделение,
        |    ОтражениеЗарплаты.ВидНачисленияОплатыТрудаДляНУ КАК ВидНачисленияОплатыТрудаДляНУ,
        |    ОтражениеЗарплаты.Сумма КАК Сумма
        |ИЗ
        |    Документ.ОтражениеЗарплатыВБухучете.НачисленнаяЗарплатаИВзносы КАК ОтражениеЗарплаты
        |ГДЕ
        |    ОтражениеЗарплаты.Ссылка.Дата МЕЖДУ &СДаты И &ПоДату
        |    И ОтражениеЗарплаты.Ссылка.Организация = &Организация";
        
    Запрос.УстановитьПараметр("СДаты", СДаты);
    Запрос.УстановитьПараметр("ПоДату", ПоДату);
    Запрос.УстановитьПараметр("Организация", Организация);
    
    Возврат Запрос.Выполнить().Выгрузить();
    
КонецФункции

&НаКлиенте
Процедура ВыгрузкаВЭксель()
    
    // Получаем данные с сервера
    ТаблицаДанных = ПолучитьДанныеДляВыгрузки(Объект.СДаты, Объект.ПоДату, Объект.Организация);
    
    Если ТаблицаДанных.Количество() = 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 одной инструкцией, что ускоряет процесс в десятки раз.

Решение №2: Использование встроенного «Табличного Документа» (Рекомендуемый способ)

Наиболее надежным, современным и быстрым способом выгрузки данных в формате Excel является использование встроенного механизма платформы 1С — объекта ТабличныйДокумент. Этот подход полностью исключает необходимость использования COM-технологий и имеет массу преимуществ:

Рассмотрим, как быстро реализовать такую выгрузку с использованием ПостроителяОтчета для автоматического формирования структуры таблицы:


&НаСервере
Функция СформироватьТабличныйДокументНаСервере(СДаты, ПоДату, Организация)
    
    ТабДок = Новый ТабличныйДокумент;
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    ОтражениеЗарплаты.Ссылка.Дата КАК [Дата документа],
        |    ОтражениеЗарплаты.Ссылка.Организация КАК Организация,
        |    ОтражениеЗарплаты.ФизическоеЛицо КАК [Сотрудник],
        |    ОтражениеЗарплаты.Подразделение КАК Подразделение,
        |    ОтражениеЗарплаты.ВидНачисленияОплатыТрудаДляНУ КАК [Вид начисления],
        |    ОтражениеЗарплаты.Сумма КАК Сумма
        |ИЗ
        |    Документ.ОтражениеЗарплатыВБухучете.НачисленнаяЗарплатаИВзносы КАК ОтражениеЗарплаты
        |ГДЕ
        |    ОтражениеЗарплаты.Ссылка.Дата МЕЖДУ &СДаты И &ПоДату
        |    И ОтражениеЗарплаты.Ссылка.Организация = &Организация";
        
    Запрос.УстановитьПараметр("СДаты", СДаты);
    Запрос.УстановитьПараметр("ПоДату", ПоДату);
    Запрос.УстановитьПараметр("Организация", Организация);
    
    РезультатЗапроса = Запрос.Выполнить();
    
    Если РезультатЗапроса.Пустой() Тогда
        Возврат Неопределено;
    КонецЕсли;
    
    // Используем ПостроительОтчета для быстрого вывода таблицы в ТабДок
    Построитель = Новый ПостроительОтчета;
    Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(РезультатЗапроса.Выгрузить());
    Построитель.Вывести(ТабДок);
    
    Возврат ТабДок;
    
КонецФункции

&НаКлиенте
Процедура ВыгрузитьЧерезТабДок()
    
    ТабДок = СформироватьТабличныйДокументНаСервере(Объект.СДаты, Объект.ПоДату, Объект.Организация);
    
    Если ТабДок = Неопределено Тогда
        ПоказатьПредупреждение(, "Нет данных для выгрузки!");
        Возврат;
    КонецЕсли;
    
    ИмяФайла = Объект.ПутьКФайлу + "\Выгрузка_ЗУП_Нативная.xlsx";
    
    Попытка
        // Метод Записать работает на клиенте и поддерживает выгрузку напрямую в XLSX
        ТабДок.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.XLSX);
        ПоказатьПредупреждение(, "Файл успешно сохранен по пути: " + ИмяФайла);
    Исключение
        ПоказатьПредупреждение(, "Не удалось сохранить файл. Ошибка: " + ОписаниеОшибки());
    КонецПопытки;
    
КонецПроцедуры

Этот способ является стандартом разработки под управляемый интерфейс 1С. Полученный файл .xlsx является полноценным валидным документом OpenXML, который без проблем откроется в Microsoft Excel, LibreOffice, Google Таблицах, а также сможет быть прочитан любой обработкой загрузки в вашей целевой базе данных (например, в «Бухгалтерии предприятия 2.0»).

Резюме и рекомендации

Подводя итоги, дадим несколько практических советов для эффективной выгрузки данных:

← На главную