При переводе обработок и печатных форм на управляемый интерфейс (например, при переходе на «1С:Зарплата и управление персоналом 3.1» или «1С:Бухгалтерия 3.0») разработчики часто сталкиваются с техническим ограничением: требуется сформировать сложный отчет по шаблону Microsoft Excel, однако сервер приложений 1С работает под управлением политики безопасности, запрещающей установку пакета MS Office на серверную машину.
Попытка вызвать Новый COMОбъект("Excel.Application") в серверном контексте в таком случае приводит к критической ошибке. Рассмотрим подробнее, как обойти это ограничение, правильно организовать передачу данных с сервера на клиент и сохранить исходную структуру шаблона Excel вместе с формулами и форматированием.
Проанализируем ключевые сложности, возникающие при адаптации старого кода к управляемым формам:
ТаблицаЗначений недоступен на клиенте в управляемом приложении. Данные, рассчитанные на сервере, необходимо конвертировать в клиентские типы (например, массив структур) или передавать через временное хранилище.ТабличныйДокумент.Записать(..., ТипФайлаТабличногоДокумента.XLSX) вычисляет и превращает все формулы Excel в статичные значения, а также может терять настройки печати и закрепления областей.Разберем наиболее надежный и проверенный алгоритм работы. Шаблон `.xlsx` сохраняется непосредственно внутри внешнего отчета или обработки 1С в виде макета с типом Двоичные данные. На сервере выполняются все расчеты, а на клиенте происходит выгрузка шаблона во временный файл, открывается COM-объект Excel и заполняются необходимые ячейки.
Рассмотрим пошагово реализацию этого механизма в коде.
Создадим функцию для сбора данных и конвертации таблицы значений в массив структур, который безопасно передается с сервера на клиент:
&НаСервере
Функция ПолучитьДанныеДляВыгрузки()
// 1. Выполняем расчеты и формируем таблицу значений
ТаблицаРезультат = Новый ТаблицаЗначений;
ТаблицаРезультат.Колонки.Добавить("СотрудникИмя", Новый ОписаниеТипов("Строка"));
ТаблицаРезультат.Колонки.Добавить("Оклад", Новый ОписаниеТипов("Число"));
ТаблицаРезультат.Колонки.Добавить("Надбавка", Новый ОписаниеТипов("Число"));
// (Заполнение таблицы данными расчетов...)
СтрокаТЗ = ТаблицаРезультат.Добавить();
СтрокаТЗ.СотрудникИмя = "Иванов И.И.";
СтрокаТЗ.Оклад = 50000;
СтрокаТЗ.Надбавка = 10000;
// 2. Преобразуем ТаблицуЗначений в Массив Структур для передачи на клиент
МассивРезультат = Новый Массив;
Для Каждого Стр Из ТаблицаРезультат Цикл
СтруктураСтроки = Новый Структура;
Для Каждого Колонка Из ТаблицаРезультат.Колонки Цикл
СтруктураСтроки.Вставить(Колонка.Имя, Стр[Колонка.Имя]);
КонецЦикла;
МассивРезультат.Добавить(СтруктураСтроки);
КонецЦикла;
Возврат МассивРезультат;
КонецФункции
&НаСервере
Функция ПолучитьМакетШаблона(ИмяМакета)
ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
Макет = ОбработкаОбъект.ПолучитьМакет(ИмяМакета); // Двоичные данные
Возврат ПоместитьВоВременноеХранилище(Макет, Новый УникальныйИдентификатор);
КонецФункции
На клиенте извлекаем двоичные данные, сохраняем их во временный файл `.xlsx`, запускаем Excel в фоновом режиме и заполняем строки:
&НаКлиенте
Процедура ВыгрузитьВExcelКлиент()
// 1. Извлекаем файл шаблона из временного хранилища
АдресХранилища = ПолучитьМакетШаблона("ШаблонExcel");
ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресХранилища);
ИмяФайлаШаблона = ПолучитьИмяВременногоФайла("xlsx");
ДвоичныеДанные.Записать(ИмяФайлаШаблона);
// 2. Получаем данные расчетов с сервера
ДанныеДляПечати = ПолучитьДанныеДляВыгрузки();
// 3. Подключаемся к MS Excel
Попытка
Excel = Новый COMОбъект("Excel.Application");
Исключение
ПоказатьПредупреждение(, "Не удалось создать объект MS Excel. Проверьте правильность установки приложения.");
Возврат;
КонецПопытки;
Попытка
// Отключаем обновление экрана для ускорения работы
Excel.ScreenUpdating = Ложь;
Excel.DisplayAlerts = Ложь;
Книга = Excel.WorkBooks.Open(ИмяФайлаШаблона);
Лист = Книга.WorkSheets(1);
НачальнаяСтрока = 5; // Номер строки в шаблоне, откуда начинается таблица
ТекущаяСтрока = НачальнаяСтрока;
Для Каждого СтрДанных Из ДанныеДляПечати Цикл
Лист.Cells(ТекущаяСтрока, 1).Value = СтрДанных.СотрудникИмя;
Лист.Cells(ТекущаяСтрока, 2).Value = СтрДанных.Оклад;
Лист.Cells(ТекущаяСтрока, 3).Value = СтрДанных.Надбавка;
// Задаем формулу Excel для итоговой суммы в строке
// Используем свойство FormulaLocal для формул на языке локализации (русский)
Лист.Cells(ТекущаяСтрока, 4).FormulaLocal = СТРЗаменить("=B%1+C%1", "%1", Строка(ТекущаяСтрока));
ТекущаяСтрока = ТекущаяСтрока + 1;
КонецЦикла;
// Включаем интерфейс обратно
Excel.ScreenUpdating = Истина;
Excel.DisplayAlerts = Истина;
Excel.Visible = Истина;
Исключение
Excel.Quit();
Excel = Неопределено;
ВызватьИсключение ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
КонецПопытки;
КонецПроцедуры
При работе с COM-объектом Microsoft Excel следует учитывать несколько важных технических особенностей:
Cells().Formula принимает названия функций исключительно на английском языке (например, =SUM(A1:A10)) с запятой в качестве разделителя аргументов. Если вы пишете формулы на русском языке (например, =СУММ(A1;A10)), используйте свойство Cells().FormulaLocal.Excel.ScreenUpdating = Ложь и Excel.DisplayAlerts = Ложь перед началом цикла заполнения. Это повышает скорость заполнения в 3–5 раз.Попытка ... Исключение. В противном случае процессы EXCEL.EXE будут зависать в диспетчере задач пользователя.Если требуется выполнять всю работу строго на сервере без применения клиентского COM-объекта, проанализируем архитектуру файла `.xlsx`. Файл формата OpenXML представляет собой обычный ZIP-архив, внутри которого находятся XML-файлы с содержимым и структурой книги.
Выясним порядок действий для редактирования файла средствами 1С на сервере:
ЧтениеZipФайла.ЧтениеXML, ЗаписьXML или DOMДокумент.ЗаписьZipФайла.Этот способ работает с высокой скоростью прямо на сервере, не требует наличия установленного пакета MS Office и полностью сохраняет исходное форматирование, макросы (`.xlsm`), графики и существующие формулы шаблона.