Как из кода 1С сформировать файл Excel: COM-объект, табличный документ и разбор ошибок
Содержание
Выгрузить таблицу из 1С в Excel программно можно двумя основными путями. Первый — запустить Excel через Новый COMОбъект("Excel.Application") и заполнить ячейки листа. Второй — собрать ТабличныйДокумент и вызвать Записать(ИмяФайла, ТипФайлаТабличногоДокумента.XLSX): так файл получается без установленного Excel, в том числе на сервере.
COM даёт полный контроль над книгой: формулы, фильтры, несколько листов, любые форматы. Табличный документ работает быстрее, не зависит от Office и не оставляет зависших процессов. Для обоих вариантов есть рабочий код на платформе 8.3 с управляемыми формами, плюс приёмы, которые решают типичные проблемы: потерю ведущих нулей, медленную запись и «висящий» EXCEL.EXE.
Какой способ выгрузки в Excel выбрать
Метод зависит от того, где выполняется код и что нужно получить на выходе.
| Способ | Нужен ли Excel | Где работает | Скорость | Когда брать |
|---|---|---|---|---|
COM-объект Excel.Application | Да, на той машине, где выполняется код | Клиент Windows (тонкий, толстый), сервер Windows | Низкая при записи по ячейкам | Сложное оформление, формулы, дописать в готовый шаблон |
ТабличныйДокумент.Записать(…, XLSX) | Нет | Клиент и сервер, Windows и Linux | Высокая | Отчёты, реестры, выгрузки на сервере и по расписанию |
Текстовый файл CSV через ЗаписьТекста | Нет | Везде | Самая высокая | Сырые данные для дальнейшей обработки |
| Гибрид: табличный документ → открыть в Excel через COM | Да | Клиент Windows | Высокая | Большой объём плюс точечное оформление |
💡 Если сомневаетесь — начните с табличного документа. COM нужен там, где без возможностей самого Excel не обойтись.
Подготовка: контекст выполнения и запуск Excel через COMОбъект
COM-объекты доступны только в Windows. В веб-клиенте и на Linux-сервере Новый COMОбъект не сработает.
Порядок такой:
- Данные получаем на сервере запросом. Таблицу значений на клиент передать нельзя, поэтому превращаем её в массив массивов с примитивными значениями.
- На клиенте (директива
&НаКлиенте) создаём экземпляр Excel и заполняем книгу. - Ссылки на объекты базы (контрагенты, номенклатура) переводим в строку ещё на сервере. COM не умеет принимать ссылку 1С.
Функция подготовки данных на примере справочника «Номенклатура» — подставьте свой запрос:
&НаСервереБезКонтекста
Функция ДанныеДляВыгрузки()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Код КАК Код,
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.Артикул КАК Артикул
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ЭтоГруппа";
ТЗ = Запрос.Выполнить().Выгрузить();
Заголовки = Новый Массив;
Для Каждого Колонка Из ТЗ.Колонки Цикл
Заголовки.Добавить(Колонка.Имя);
КонецЦикла;
Строки = Новый Массив;
Для Каждого СтрокаТЗ Из ТЗ Цикл
Значения = Новый Массив;
Для Каждого Колонка Из ТЗ.Колонки Цикл
Значение = СтрокаТЗ[Колонка.Имя];
Тип = ТипЗнч(Значение);
Если Тип = Тип("Число") Или Тип = Тип("Булево") Тогда
Значения.Добавить(Значение);
ИначеЕсли Тип = Тип("Дата") Тогда
// пустую дату в Excel не передаём, иначе получим странное значение
Значения.Добавить(?(Значение = Дата(1, 1, 1), "", Значение));
Иначе
Значения.Добавить(Строка(Значение));
КонецЕсли;
КонецЦикла;
Строки.Добавить(Значения);
КонецЦикла;
Возврат Новый Структура("Заголовки, Строки", Заголовки, Строки);
КонецФункции
Запуск Excel всегда оборачивайте в Попытка. Если Office не установлен или повреждена его регистрация, конструктор упадёт с исключением, и пользователь должен увидеть понятное сообщение, а не трассировку.
Попытка
Excel = Новый COMОбъект("Excel.Application");
Исключение
ПоказатьПредупреждение(, "Не удалось запустить Excel: "
+ КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
Возврат;
КонецПопытки;
Excel.Visible = Ложь; // работаем в фоне
Excel.DisplayAlerts = Ложь; // без вопросов «Заменить файл?»
Excel.ScreenUpdating = Ложь; // не перерисовывать экран при записи
Запись данных в ячейки и диапазоны листа
Нумерация строк и колонок в Excel начинается с 1, а индексы массивов 1С — с 0. Именно здесь чаще всего появляется сдвиг на одну ячейку.
Основные обращения через COM:
- 📌
Лист.Cells(Строка, Колонка).Value = Значение— одна ячейка; - 📌
Лист.Range("A1:C1")— диапазон по адресу; - 📌
Лист.Range(Лист.Cells(1, 1), Лист.Cells(1, 3))— диапазон по номерам, удобно в цикле; - 📌
Книга.Worksheets(1)— лист по номеру,Книга.Worksheets.Add()— новый лист.
Процедура выгрузки целиком:
&НаКлиенте
Процедура ВыгрузитьВExcel(Команда)
Данные = ДанныеДляВыгрузки();
ЧислоКолонок = Данные.Заголовки.Количество();
ЧислоСтрок = Данные.Строки.Количество();
Попытка
Excel = Новый COMОбъект("Excel.Application");
Исключение
ПоказатьПредупреждение(, "Не удалось запустить Excel: "
+ КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
Возврат;
КонецПопытки;
Excel.Visible = Ложь;
Excel.DisplayAlerts = Ложь;
Excel.ScreenUpdating = Ложь;
Попытка
Книга = Excel.Workbooks.Add();
Лист = Книга.Worksheets(1);
Лист.Name = "Номенклатура";
// Колонка «Код» и «Артикул» — текст, чтобы не потерять ведущие нули.
// Формат ставим ДО записи значений.
Лист.Columns(1).NumberFormat = "@";
Лист.Columns(3).NumberFormat = "@";
// Шапка
Для Кол = 1 По ЧислоКолонок Цикл
Лист.Cells(1, Кол).Value = Данные.Заголовки[Кол - 1];
КонецЦикла;
// Строки данных начинаем со второй строки листа
Для Стр = 1 По ЧислоСтрок Цикл
Значения = Данные.Строки[Стр - 1];
Для Кол = 1 По ЧислоКолонок Цикл
Лист.Cells(Стр + 1, Кол).Value = Значения[Кол - 1];
КонецЦикла;
КонецЦикла;
ОформитьЛист(Лист, ЧислоСтрок + 1, ЧислоКолонок);
ИмяФайла = ПолучитьИмяВременногоФайла("xlsx");
Книга.SaveAs(ИмяФайла, 51); // 51 — формат книги xlsx
Книга.Close(Ложь);
Сообщить("Файл сохранён: " + ИмяФайла);
Исключение
Сообщить("Ошибка выгрузки: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
Лист = Неопределено;
Книга = Неопределено;
Excel.Quit();
Excel = Неопределено;
КонецПроцедуры
Каждое обращение к Cells — отдельный вызов между процессами 1С и Excel. На 1 000 строк по 10 колонок это 10 000 вызовов, и выгрузка тянется заметное время. ScreenUpdating = Ложь ускоряет запись, но сам принцип «по ячейке» остаётся медленным.
Для больших таблиц есть два приёма:
- Запись диапазона двумерным массивом. Значения собирают в
COMSafeArrayи присваивают свойствуValueдиапазона одним вызовом. Перед боевым запуском проверьте порядок измерений массива на таблице 2×3: если строки и колонки поменялись местами, переставьте индексы вSetValue. - Гибрид. Сохраняете файл табличным документом (это быстро), затем открываете его через
Excel.Workbooks.Open(ИмяФайла)и через COM добавляете только то, чего нет в табличном документе: автофильтр, формулы, условное оформление.
Оформление: шрифты, форматы чисел и автоподбор ширины колонок
Оформление удобнее накладывать на диапазон целиком, а не на каждую ячейку отдельно. Так и быстрее, и код короче.
&НаКлиенте
Процедура ОформитьЛист(Лист, ПоследняяСтрока, ПоследняяКолонка)
Шапка = Лист.Range(Лист.Cells(1, 1), Лист.Cells(1, ПоследняяКолонка));
Шапка.Font.Bold = Истина;
Шапка.Interior.ColorIndex = 15; // серая заливка
Шапка.HorizontalAlignment = -4108; // по центру
Таблица = Лист.Range(Лист.Cells(1, 1), Лист.Cells(ПоследняяСтрока, ПоследняяКолонка));
Таблица.Borders.LineStyle = 1; // тонкие границы у всех ячеек
Таблица.AutoFilter(); // фильтры в шапке
Лист.UsedRange.Columns.AutoFit(); // автоподбор ширины колонок
КонецПроцедуры
Что ещё пригодится при оформлении:
- 🔢
NumberFormat = "#,##0.00"— суммы с разделителем разрядов и двумя знаками. СвойствоNumberFormatпринимает формат в английской записи, а на экране Excel покажет его по региональным настройкам. Для локальной записи естьNumberFormatLocal. - 📅
NumberFormat = "dd.mm.yyyy"— даты без времени. Дата 1С приходит в Excel как дата, а не строка, поэтому сортировка и фильтры работают. - 🔤
NumberFormat = "@"— текстовый формат. Нужен для ИНН, кодов, артикулов, номеров счетов: иначе0012превратится в12, а длинное число — в экспоненту. - 📏
Columns(N).ColumnWidth = 40— фиксированная ширина, еслиAutoFitрастягивает колонку с длинными наименованиями на весь экран . - ↩️
WrapText = Истина— перенос текста по словам внутри ячейки.
Числа передавайте числами, а не строками. Строка "1500,50" при записи в ячейку будет разобрана по региональным настройкам машины. На компьютере с английской локалью она может остаться текстом, и формулы СУММ её не увидят.
Сохранение файла и завершение процесса Excel
Метод SaveAs принимает путь и код формата. Самые ходовые коды:
| Код | Формат | Расширение |
|---|---|---|
| 51 | Книга Excel | .xlsx |
| 56 | Книга Excel 97–2003 | .xls |
| 6 | CSV | .csv |
Расширение в имени файла и код формата должны совпадать. Иначе Excel при открытии предупредит, что формат не соответствует расширению.
Вместо сохранения можно отдать книгу пользователю на экран: уберите SaveAs, Close и Quit и в конце выполните Excel.Visible = Истина. Дальше человек сам решит, сохранять ли файл и куда.
Порядок завершения, при котором EXCEL.EXE не остаётся в памяти:
Книга.Close(Ложь)— закрыть книгу без вопроса о сохранении (она уже сохранена).- Обнулить переменные, которые держат объекты Excel: листы, диапазоны, книгу.
Excel.Quit()— выйти из приложения.Excel = Неопределено— отпустить сам COM-объект.
Главное — чтобы Quit() выполнялся и при ошибке. Поэтому в примере выше он стоит после блока Попытка … Исключение, а не внутри него.
Обработка ошибок и выгрузка без установленного Excel
Ошибки при работе с COM приходят исключениями с текстом вида Ошибка при вызове метода контекста (SaveAs). Текст после двоеточия — это описание от самого Excel. Выводите его через КраткоеПредставлениеОшибки(ИнформацияОбОшибке()), а в журнал регистрации пишите ПодробноеПредставлениеОшибки — там будет номер строки модуля.
Если Excel недоступен или код должен работать на сервере, переходите на табличный документ. Он не требует Office и одинаково работает в Windows и Linux.
&НаСервере
Функция ВыгрузитьЧерезТабличныйДокумент()
Данные = ДанныеДляВыгрузки(); // та же функция, что и для COM
ЧислоКолонок = Данные.Заголовки.Количество();
ТабДок = Новый ТабличныйДокумент;
Для Кол = 1 По ЧислоКолонок Цикл
Ячейка = ТабДок.Область(1, Кол, 1, Кол);
Ячейка.Текст = Данные.Заголовки[Кол - 1];
Ячейка.Шрифт = Новый Шрифт(, , Истина);
КонецЦикла;
Для Стр = 1 По Данные.Строки.Количество() Цикл
Значения = Данные.Строки[Стр - 1];
Для Кол = 1 По ЧислоКолонок Цикл
Ячейка = ТабДок.Область(Стр + 1, Кол, Стр + 1, Кол);
Значение = Значения[Кол - 1];
Если ТипЗнч(Значение) = Тип("Число") Тогда
Ячейка.СодержитЗначение = Истина;
Ячейка.ТипЗначения = Новый ОписаниеТипов("Число");
Ячейка.Значение = Значение; // в Excel придёт числом
Иначе
Ячейка.Текст = Строка(Значение);
КонецЕсли;
КонецЦикла;
КонецЦикла;
ИмяФайла = ПолучитьИмяВременногоФайла("xlsx");
ТабДок.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.XLSX);
Адрес = ПоместитьВоВременноеХранилище(Новый ДвоичныеДанные(ИмяФайла), УникальныйИдентификатор);
УдалитьФайлы(ИмяФайла);
Возврат Адрес;
КонецФункции
Файл лежит на сервере, поэтому пользователю его передают через временное хранилище. На клиенте адрес отдают в ПолучитьФайл(Адрес, "Выгрузка.xlsx", Истина) или, если в конфигурации включён асинхронный режим, в ФайловаяСистемаКлиент.СохранитьФайл из БСП.
💡 Если ячейке задать только Текст, число попадёт в Excel строкой. Для сумм и количеств заполняйте Значение с признаком СодержитЗначение, как в примере.
Третий запасной вариант — CSV через ЗаписьТекста с разделителем ;. Excel откроет такой файл двойным щелчком, но форматов, ширины колонок и нескольких листов в нём не будет. Для обмена с внешними программами этого часто достаточно.
Частые проблемы при выгрузке в Excel и их решение
Выберите симптом, чтобы увидеть причину и решение.
Отдельно про серверную автоматизацию Office. Запуск Excel из службы сервера 1С технически возможен, но Microsoft не поддерживает такой сценарий: Excel может зависнуть на невидимом диалоге, и сеанс 1С будет ждать бесконечно. Для регламентных заданий и выгрузок на сервере используйте табличный документ, а не COM.
Частые вопросы
Работает ли выгрузка через COM в веб-клиенте?
Можно ли выгрузить данные в готовый шаблон Excel с логотипом и шапкой?
Workbooks.Add() вызовите Workbooks.Open(ПутьКШаблону), заполните нужные ячейки и сохраните книгу через SaveAs под новым именем, чтобы не испортить шаблон.Как выгрузить несколько таблиц на разные листы?
Книга.Worksheets.Add() добавляет лист, свойство Name задаёт его имя. Имя листа не длиннее 31 символа и без знаков \ / ? * [ ] :.Подойдёт ли LibreOffice вместо Excel?
Excel.Application с LibreOffice не работает: у него другая объектная модель. Табличный документ 1С пишет xlsx без всяких офисных программ, и такой файл открывается и в Excel, и в LibreOffice.