Как из кода 1С сформировать файл Excel: COM-объект, табличный документ и разбор ошибок

· 10 мин

Содержание

Выгрузить таблицу из 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Объект не сработает.

Порядок такой:

  1. Данные получаем на сервере запросом. Таблицу значений на клиент передать нельзя, поэтому превращаем её в массив массивов с примитивными значениями.
  2. На клиенте (директива &НаКлиенте) создаём экземпляр Excel и заполняем книгу.
  3. Ссылки на объекты базы (контрагенты, номенклатура) переводим в строку ещё на сервере. 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 = Ложь ускоряет запись, но сам принцип «по ячейке» остаётся медленным.

Для больших таблиц есть два приёма:

  1. Запись диапазона двумерным массивом. Значения собирают в COMSafeArray и присваивают свойству Value диапазона одним вызовом. Перед боевым запуском проверьте порядок измерений массива на таблице 2×3: если строки и колонки поменялись местами, переставьте индексы в SetValue.
  2. Гибрид. Сохраняете файл табличным документом (это быстро), затем открываете его через 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
6CSV.csv

Расширение в имени файла и код формата должны совпадать. Иначе Excel при открытии предупредит, что формат не соответствует расширению.

Вместо сохранения можно отдать книгу пользователю на экран: уберите SaveAs, Close и Quit и в конце выполните Excel.Visible = Истина. Дальше человек сам решит, сохранять ли файл и куда.

Порядок завершения, при котором EXCEL.EXE не остаётся в памяти:

  1. Книга.Close(Ложь) — закрыть книгу без вопроса о сохранении (она уже сохранена).
  2. Обнулить переменные, которые держат объекты Excel: листы, диапазоны, книгу.
  3. Excel.Quit() — выйти из приложения.
  4. 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 в веб-клиенте?
Нет. В браузере COM-объекты недоступны. Сформируйте файл на сервере табличным документом и передайте его пользователю через временное хранилище.
Можно ли выгрузить данные в готовый шаблон Excel с логотипом и шапкой?
Да. Вместо Workbooks.Add() вызовите Workbooks.Open(ПутьКШаблону), заполните нужные ячейки и сохраните книгу через SaveAs под новым именем, чтобы не испортить шаблон.
Как выгрузить несколько таблиц на разные листы?
Через COM: Книга.Worksheets.Add() добавляет лист, свойство Name задаёт его имя. Имя листа не длиннее 31 символа и без знаков \ / ? * [ ] :.
Подойдёт ли LibreOffice вместо Excel?
Код на Excel.Application с LibreOffice не работает: у него другая объектная модель. Табличный документ 1С пишет xlsx без всяких офисных программ, и такой файл открывается и в Excel, и в LibreOffice.
Сколько строк можно выгрузить в один лист?
Лист xlsx вмещает 1 048 576 строк, формат xls — 65 536. При больших объёмах дробите выгрузку на несколько листов или файлов.