При разработке отчетов и обработок в 1С часто возникает задача: на форме отображаются сразу два, три или более полей табличного документа, и пользователю требуется выгрузить их все в один файл Excel. Стандартная попытка вызвать метод Записать() по очереди для каждого табличного документа приводит к тому, что в итоговом файле остается только последняя таблица, так как каждый вызов перезаписывает файл на диске.
Выясним причину такого поведения и подробно разберем все способы решения этой задачи: сохранение таблиц на отдельные листы книги с помощью специального объекта платформы, а также их объединение на одном общем листе.
Проанализируем ситуацию: стандартный объект ТабличныйДокумент в платформе 1С:Предприятие представляет собой отдельный документ (аналог одного листа). Когда мы выполняем команду сохранения:
ТабДок1.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.XLSX);
ТабДок2.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.XLSX);
ТабДок3.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.XLSX);
Платформа не добавляет новые листы в существующий файл, а полностью перезаписывает его с нуля при каждом вызове. В результате на диске остается только результат работы третьей строки. Чтобы получить полноценную книгу Excel, необходимо либо использовать штатный контейнер пакетов документов, либо объединять таблицы программно.
Начиная с версии платформы 8.3.12, разработчикам доступен нативный механизм выгрузки нескольких табличных документов в многостраничный файл Excel без использования сторонних библиотек и COM-объектов. Для этого применяется объект ПакетОтображаемыхДокументов.
Разберем по шагам, как устроен этот механизм:
ПакетОтображаемыхДокументов.Данные элемента состава пакета принимает именно строковый адрес во временном хранилище, а не сам объект ТабличныйДокумент. Передача объекта напрямую вызовет ошибку.Состав пакета, передавая полученный адрес в свойство Данные, а имя будущего листа Excel — в свойство Наименование.Записать() уже у самого пакета, указав тип файла ТипФайлаПакетаОтображаемыхДокументов.XLSX.Посмотрим на пример процедуры на сервере, которая формирует и сохраняет файл:
&НаСервере
Процедура СохранитьТриТаблицыВExcelНаСервере(ПутьКФайлу)
Пакет = Новый ПакетОтображаемыхДокументов;
// Лист 1
Элемент1 = Пакет.Состав.Добавить();
Элемент1.Данные = ПоместитьВоВременноеХранилище(Элементы.ПолеТабДок1.ТабличныйДокумент);
Элемент1.Наименование = "Отчет по продажам";
// Лист 2
Элемент2 = Пакет.Состав.Добавить();
Элемент2.Данные = ПоместитьВоВременноеХранилище(Элементы.ПолеТабДок2.ТабличныйДокумент);
Элемент2.Наименование = "Взаиморасчеты";
// Лист 3
Элемент3 = Пакет.Состав.Добавить();
Элемент3.Данные = ПоместитьВоВременноеХранилище(Элементы.ПолеТабДок3.ТабличныйДокумент);
Элемент3.Наименование = "Остатки на складах";
// Сохраняем все листы в один файл
Пакет.Записать(ПутьКФайлу, ТипФайлаПакетаОтображаемыхДокументов.XLSX);
КонецПроцедуры
Рассмотрим подробнее правила задания свойства Наименование для листов Excel. Чтобы файл успешно открывался и не вызывал системных сбоев, необходимо соблюдать ограничения формата Excel:
\ / ? * [ ] :.Если бизнес-логика требует расположить данные всех трех полей на одном листе (например, друг под другом с небольшим отступом или в виде сводной таблицы), нам потребуется создать новый результирующий ТабличныйДокумент и последовательно перенести в него данные.
Разберем по шагам реализацию вертикального объединения:
ТабличныйДокумент.Вывести() последовательно добавляем в него исходные таблицы. Метод Вывести() добавляет строки ниже уже существующих. Если требуется расположить таблицы рядом по горизонтали, используется метод Присоединить().Записать() результирующего табличного документа.Посмотрим на пример реализации объединения друг под другом:
&НаСервере
Процедура ОбъединитьИСохранитьНаОдинЛист(ПутьКФайлу)
ИтоговыйТабДок = Новый ТабличныйДокумент;
// Выводим первую таблицу
ИтоговыйТабДок.Вывести(ТабДок1);
// Добавляем пустую строку-разделитель
ПустаяТаблица = Новый ТабличныйДокумент;
ПустаяТаблица.ВывестиСтроку();
ИтоговыйТабДок.Вывести(ПустаяТаблица);
// Выводим вторую таблицу
ИтоговыйТабДок.Вывести(ТабДок2);
ИтоговыйТабДок.Вывести(ПустаяТаблица);
// Выводим третью таблицу
ИтоговыйТабДок.Вывести(ТабДок3);
// Записываем единый лист в файл Excel
ИтоговыйТабДок.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.XLSX);
КонецПроцедуры
Важный момент при объединении: у результирующего листа будет общая сетка колонок. Если у первой таблицы ширина первой колонки составляет 15 единиц, а у второй таблицы — 40 единиц, одна из таблиц может визуально исказиться (текст обрежется или ячейка станет слишком широкой). Если у таблиц кардинально разная структура и ширина колонок, рекомендуется использовать Способ 1 с выводом на разные листы.
Если решение разрабатывается на платформе версии ниже 8.3.12, где у объекта ПакетОтображаемыхДокументов еще не было метода Записать() в формат XLSX, объединить листы можно через Windows COM-соединение с приложением Excel.
Схема решения в этом случае выглядит так:
Записать().Новый COMОбъект("Excel.Application") открывается сессия Microsoft Excel.Ограничения COM-подхода: данный метод работает исключительно в операционной системе Windows, требует наличия установленного пакета Microsoft Office на клиенте или сервере и не может использоваться в веб-клиенте или на серверах под управлением Linux. Поэтому на современных версиях платформы всегда следует отдавать предпочтение объекту ПакетОтображаемыхДокументов.