В практике разработки на платформе 1С:Предприятие часто возникают задачи, связанные с формированием отчетов в формате Excel. Стандартный табличный документ 1С не всегда покрывает все потребности пользователей, особенно когда требуются сложные фильтры, специфические диаграммы, защищенные области или сохранение сложного условного форматирования. В таких случаях мы прибегаем к заполнению заранее подготовленных шаблонов Excel.
Однако возникает важный вопрос: как открыть этот шаблон пользователю таким образом, чтобы он не мог случайно перезаписать исходный файл "пустышку"? Идеальный сценарий — Excel открывается с заполненными данными, заголовок окна гласит "Книга1" (или аналогичное), а при нажатии кнопки "Сохранить" программа предлагает выбрать имя и место сохранения, оставляя шаблон нетронутым.
Рассмотрим подробно несколько способов решения этой задачи, от наиболее нативных до алгоритмически сложных, с использованием технологии OLE Automation.
Самый элегантный и правильный с точки зрения объектной модели Excel способ — использование метода Add коллекции Workbooks. Многие привыкли вызывать этот метод без параметров для создания пустой книги, но он может принимать путь к файлу в качестве аргумента.
Если передать в Workbooks.Add путь к обычному файлу .xlsx или шаблону .xltx, Excel автоматически создаст новую книгу на основе этого файла. Это в точности повторяет действие "Создать" -> "Из существующего файла".
Разберем пример кода:
Процедура ОткрытьШаблонКакНовуюКнигу(ПутьКФайлуШаблона)
// Создаем COM-объект приложения Excel
Попытка
ExcelApp = Новый COMОбъект("Excel.Application");
Исключение
Сообщить("Не удалось инициализировать Excel. Возможно, он не установлен.");
Возврат;
КонецПопытки;
// Скрываем приложение на время манипуляций, чтобы ускорить процесс
// и не пугать пользователя мельканием окон
ExcelApp.Visible = Ложь;
ExcelApp.DisplayAlerts = Ложь;
Попытка
// ВАЖНО: Мы не используем Open, мы используем Add с параметром пути.
// Это создает НОВУЮ книгу (копию в памяти) на основе шаблона.
НоваяКнига = ExcelApp.Workbooks.Add(ПутьКФайлуШаблона);
// Здесь можно выполнить заполнение данными
Лист = НоваяКнига.Sheets(1);
Лист.Cells(1, 1).Value = "Прайс-лист сформирован: " + ТекущаяДата();
// Возвращаем видимость и управление пользователю
ExcelApp.Visible = Истина;
ExcelApp.UserControl = Истина; // Передаем управление пользователю
ExcelApp.DisplayAlerts = Истина;
Исключение
// Если произошла ошибка, закрываем процесс, чтобы он не висел в памяти
ExcelApp.Quit();
ExcelApp = Неопределено;
Сообщить("Ошибка при формировании Excel: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Преимущества этого метода:
Этот метод обсуждался в сообществе как альтернатива. Суть его заключается в том, чтобы открыть исходный файл, создать рядом пустую новую книгу и скопировать листы из источника в приемник. Это может быть полезно, если нам нужно собрать итоговый файл из нескольких разных шаблонов.
Проанализируем алгоритм:
Open.Add (без параметров).Посмотрим на реализацию:
Процедура СформироватьСборныйФайл(ПутьКИсточнику)
ExcelApp = Новый COMОбъект("Excel.Application");
ExcelApp.Visible = Ложь;
// Открываем исходник (он будет открыт в режиме "Только чтение" по факту, так как мы его закроем)
ИсходнаяКнига = ExcelApp.Workbooks.Open(ПутьКИсточнику);
// Создаем новую пустую книгу
НоваяКнига = ExcelApp.Workbooks.Add();
// Удаляем пустые листы из новой книги (обычно там создается Лист1, Лист2...)
// Оставим пока один, чтобы не возникало ошибок, удалим его в конце
// Копируем листы из исходника в конец новой книги
Для Каждого Лист Из ИсходнаяКнига.Sheets Цикл
// Метод Copy в Excel принимает параметры (Before, After)
// Копируем ПОСЛЕ последнего листа новой книги
Лист.Copy(, НоваяКнига.Sheets(НоваяКнига.Sheets.Count));
КонецЦикла;
// Удаляем начальные пустые листы из новой книги (опционально)
// ExcelApp.DisplayAlerts = Ложь;
// НоваяКнига.Sheets(1).Delete();
// ExcelApp.DisplayAlerts = Истина;
// Закрываем исходную книгу без сохранения изменений
ИсходнаяКнига.Close(Ложь);
// Показываем результат
ExcelApp.Visible = Истина;
ExcelApp.UserControl = Истина;
КонецПроцедуры
Недостатки метода:
Add с шаблоном.Если по каким-то причинам предыдущие методы не подходят (например, проблемы с правами доступа или защитой структуры книги), можно использовать стратегию "физического копирования". Мы копируем файл средствами 1С во временную папку с уникальным именем, заполняем его и отдаем пользователю.
Здесь есть нюанс: файл технически уже будет иметь имя (например, "tmp12345.xlsx") и путь на диске. Но для пользователя это безопасно, так как оригинал не затрагивается.
Процедура ОткрытьЧерезВременнуюКопию(ПутьКФайлу)
// 1. Создаем копию файла
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xlsx");
КопироватьФайл(ПутьКФайлу, ИмяВременногоФайла);
// 2. Запускаем Excel
ExcelApp = Новый COMОбъект("Excel.Application");
ExcelApp.Visible = Ложь;
// Открываем уже нашу временную копию
РабочаяКнига = ExcelApp.Workbooks.Open(ИмяВременногоФайла);
// ... Здесь код заполнения данными ...
// 3. Показываем пользователю
ExcelApp.Visible = Истина;
// ВАЖНО: Так как файл лежит во временной папке, при нажатии "Сохранить"
// он сохранится там же. Чтобы заставить пользователя выбрать путь,
// можно пометить файл как "Только для чтения" или программно вызвать диалог,
// но это усложняет логику.
// Освобождаем переменную
ExcelApp = Неопределено;
КонецПроцедуры
При работе с COM-объектами Excel из 1С необходимо учитывать несколько критических моментов для стабильной работы системы.
Если ваш код прервется ошибкой до того, как ExcelApp.Visible станет Истина, или если вы не корректно завершите работу, процесс EXCEL.EXE останется висеть в диспетчере задач. При частом запуске это может "съесть" всю оперативную память.
Всегда используйте конструкцию Попытка ... Исключение. В блоке исключения обязательно вызывайте метод Quit() (если приложение невидимо) и обнуляйте переменную:
Если ExcelApp <> Неопределено Тогда
ExcelApp.Quit();
ExcelApp = Неопределено;
КонецЕсли;
В обсуждениях часто возникает вопрос, где выполнять этот код. Категорически рекомендуется выполнять OLE-автоматизацию Excel только на стороне Клиента.
ДвоичныеДанные, передавайте на клиент, и уже там открывайте.Перед манипуляциями с книгой полезно проверить, не защищена ли ее структура. Это поможет избежать ошибок при попытке копирования листов:
Если ИсходнаяКнига.ProtectStructure Тогда
Сообщить("Внимание: Структура книги защищена паролем, копирование может не сработать.");
КонецЕсли;
Для задачи "открыть как новый из шаблона" наилучшим решением является метод ExcelApp.Workbooks.Add(ПутьКФайлу). Он работает быстро, корректно обрабатывает связи и не требует сложного кода для очистки временных файлов. Методы с копированием листов или файлов стоит использовать только в специфических сценариях компоновки данных из разных источников.