В процессе разработки отчетов на базе Системы компоновки данных (СКД) часто возникает задача не просто вывести результат на экран, но и программно сохранить его в файл (например, в формате Excel, XML или JSON) по определенному условию. Если отчет использует стандартную форму из Библиотеки стандартных подсистем (БСП), разработчик сталкивается с серьезным ограничением: серверный код модуля отчета не может напрямую инициировать интерактивное действие на клиенте, такое как сохранение файла. В этой статье мы подробно разберем, как обойти это ограничение и реализовать корректную выгрузку данных.
Рассмотрим типичную ситуацию: в модуле объекта отчета в процедуре ПриКомпоновкеРезультата выполняется проверка параметра «Сохранить в файл». Если флаг установлен, разработчик использует ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений, получает таблицу, конвертирует ее в нужный формат и записывает на диск сервера. Однако процедура ПриКомпоновкеРезультата исполняется строго на сервере (или в фоновом задании). Методы платформы для передачи файла пользователю, такие как НачатьПолучениеФайлаССервера или ПолучитьФайл, требуют клиентского контекста.
Проблема усугубляется тем, что при использовании общих форм БСП у отчета нет собственного клиентского модуля, где можно было бы прописать вызов после завершения формирования. Нам необходимо найти «точку входа», которая позволит связать серверную логику подготовки файла с клиентской логикой его получения.
Это самый быстрый и прямолинейный метод. Если нам нужно добавить специфическое поведение для конкретного отчета, мы можем отказаться от использования общей формы БСП в пользу локальной копии.
ОбщиеФормы — ФормаОтчета (стандартная форма БСП).Отчет.В модуле формы найдем обработчик команды Сформировать. Нам нужно дождаться окончания формирования отчета на сервере и проверить, был ли создан файл. Рассмотрим пример кода для клиентской части:
&НаКлиенте
Процедура ПослеЗавершенияФормирования(Результат, ДополнительныеПараметры) Экспорт
Если Результат = "Выполнено" Тогда
// Вызываем серверный метод, который проверит наличие файла во временном хранилище
АдресФайла = ПолучитьАдресПодготовленногоФайлаНаСервере();
Если ЗначениеЗаполнено(АдресФайла) Тогда
ПараметрыПолучения = Новый ПараметрыПолученияФайла;
ПараметрыПолучения.Заголовок = "Сохранение выгрузки отчета";
НачатьПолучениеФайлаССервера(Новый ОписаниеОповещения("ЗавершениеПолученияФайла", ЭтотОбъект), АдресФайла, "Выгрузка.xlsx", ПараметрыПолучения);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Этот метод надежен, но имеет минус: при обновлении БСП вам придется вручную синхронизировать изменения в скопированной форме.
Если мы хотим сохранить чистоту основной конфигурации и не копировать формы, следует использовать Расширения. В современных версиях БСП формирование отчета часто происходит асинхронно через механизм длительных операций. Мы можем перехватить процедуру завершения формирования отчета в расширении формы.
Проанализируем шаги реализации через расширение:
ФормаОтчета.&После для метода СформироватьНаСервереЗавершение (или аналогичного в зависимости от версии БСП).Рассмотрим пример логики в модуле объекта отчета (в процедуре ПриКомпоновкеРезультата), которая подготавливает данные:
Процедура ПриКомпоновкеРезультата(ДанныеРасшифровки, СтандартнаяОбработка)
// Проверяем значение параметра в СКД
ПараметрВыгрузки = КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти("СохранятьВФайл");
Если ПараметрВыгрузки <> Неопределено И ПараметрВыгрузки.Использование И ПараметрВыгрузки.Значение = Истина Тогда
// Отключаем стандартный вывод, если не хотим показывать отчет на экране
// СтандартнаяОбработка = Ложь;
// Выводим в таблицу значений или сразу в файл
ТабДок = Новый ТабличныйДокумент;
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ТабДок);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
// Сохраняем во временное хранилище
ИмяФайла = ПолучитьИмяВременногоФайла("xlsx");
ТабДок.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.XLSX);
ДанныеФайла = Новый ДвоичныеДанные(ИмяФайла);
АдресХранилища = ПоместитьВоВременноеХранилище(ДанныеФайла, ЭтаФорма.УникальныйИдентификатор);
// Передаем адрес обратно в настройки или дополнительные свойства,
// чтобы клиентская форма могла его увидеть
ЭтотОбъект.ДополнительныеСвойства.Вставить("АдресФайлаВыгрузки", АдресХранилища);
КонецЕсли;
КонецПроцедуры
БСП предоставляет механизм ВариантыОтчетовПереопределяемый, который позволяет программно добавлять кнопки в подменю «Печать» или «Еще» формы отчета без изменения самой формы. Это наиболее архитектурно правильный подход, так как он разделяет формирование экранного представления и выгрузку данных.
Разберем, как это реализовать:
ВариантыОтчетовПереопределяемый в процедуре НастроитьКомандыОтчетов добавим описание нашей команды выгрузки.Проанализируем пример серверной функции в модуле менеджера отчета:
Функция СформироватьФайлВыгрузки(ПараметрыОтчета) Экспорт
ОтчетОбъект = Отчеты.НашОтчет.Создать();
ОтчетОбъект.КомпоновщикНастроек.ЗагрузитьНастройки(ПараметрыОтчета.Настройки);
// Программная компоновка (аналогично ПриКомпоновкеРезультата)
// ... логика формирования ТабличногоДокумента ...
Адрес = ПоместитьВоВременноеХранилище(ДанныеФайла);
Возврат Адрес;
КонецФункции
На клиенте мы просто вызываем эту функцию и передаем результат в НачатьПолучениеФайлаССервера. Этот метод позволяет пользователю четко разделять действия: «Посмотреть отчет» и «Выгрузить файл для банка/налоговой».
1. Управление памятью. При выгрузке больших отчетов через ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений память сервера может быстро закончиться. Если данных очень много, рассмотрите возможность вывода напрямую в текстовый файл (CSV, XML) через ЗаписьТекста или ЗаписьXML, минуя промежуточные таблицы значений.
2. Использование фоновых заданий. Поскольку выгрузка в файл может занимать длительное время, крайне желательно использовать механизм ДлительныеОперации БСП. Это позволит пользователю продолжать работу с системой, пока файл готовится на сервере. По завершении операции система оповестит клиента, и файл будет скачан автоматически.
3. Временное хранилище. Всегда привязывайте адрес временного хранилища к уникальному идентификатору формы (ЭтаФорма.УникальныйИдентификатор), иначе данные могут быть удалены сборщиком мусора до того, как клиент успеет их запросить.
Подводя итог, можно сказать, что решение задачи сохранения отчета в файл зависит от требований к обновляемости конфигурации. Копирование формы — это быстрое решение для разовых задач, в то время как программное добавление команд через БСП — это профессиональный подход, обеспечивающий стабильность и чистоту кода.