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