Как программно сформировать и сохранить отчет в 1С без ошибки «Отчет формируется»?

Программист 1С v8.3 (Управляемые формы) 1C:Бухгалтерия Бухгалтерский учет IT и автоматизация бизнеса
← На главную

При автоматизации рутинных операций нередко возникает задача: программно открыть отчет (например, оборотно-сальдовую ведомость), сформировать его с заданными отборами и сразу же выгрузить результат в файл на диск. Однако при попытке сохранить табличный документ в той же процедуре разработчики сталкиваются с исключением платформы: «Отчет формируется».

При этом проявляется характерная особенность: если отчет формируется быстро (например, с одним простым показателем), сохранение проходит успешно. Но стоит добавить показатели (БУ, НУ, валютная сумма), как код падает с ошибкой. Проанализируем природу этой проблемы и разберем правильные варианты ее решения.

Выясним причину ошибки: почему отчет «не успевает» сохраниться

В современных конфигурациях («Бухгалтерия предприятия», «ERP Управление предприятием», «Комплексная автоматизация») практически все стандартные отчеты построены с использованием библиотечных механизмов (БСП) и подсистемы ДлительныеОперации. Построение отчета запускается не синхронно в интерфейсном потоке, а в отдельном фоновом задании на сервере, чтобы окно программы не зависало у пользователя.

Механизм запуска фонового задания работает следующим образом:

  1. При старте формирования система делает короткое синхронное ожидание (обычно от 1 до 2 секунд).
  2. Если запрос простой и сервер успел построить отчет за этот интервал, результат немедленно передается на форму, и табличный документ заполняется.
  3. Если отчет тяжелый и расчет занимает больше времени, управление сразу же возвращается в клиентский код со статусом выполнения «В процессе» («Отчет формируется»). Сам расчет продолжается в фоновом потоке, а форма периодически опрашивает сервер через обработчик ожидания.

Когда вы пытаетесь сохранить табличный документ формы сразу после команды формирования, в случае длительного расчета он фактически еще пуст или заблокирован фоновым процессом. Попытка обращения к нему вызывает закономерное исключение.

Почему искусственная пауза не работает

Первая идея, которая часто приходит в голову начинающим разработчикам — вставить задержку перед сохранением. Начиная с платформы 8.3.24, появился метод ВызватьПаузу(), однако его использование в данном сценарии является ошибочным:

  1. Метод ВызватьПаузу() предназначен строго для клиента и вызывает ошибку времени выполнения при попытке применить его в серверном контексте или внутри клиент-серверного вызова.
  2. Синхронное усыпление клиентского потока блокирует интерфейс. Пока клиент «спит», стандартные периодические обработчики ожидания (ПодключитьОбработчикОжидания), которые должны проверить завершение фонового задания и забрать готовый отчет с сервера, просто не могут выполниться.
  3. Задавать фиксированное время ожидания ненадежно: при пиковой нагрузке на сервер расчет может длиться дольше установленного лимита.

Способ 1. Синхронное формирование отчета на сервере без формы (рекомендуемый)

Если задача заключается исключительно в сохранении отчета в файл (или отправке по почте), открывать интерактивную форму вообще не требуется. Форма создана для человека. Программно гораздо надежнее создать объект отчета на сервере, передать настройки в компоновщик и выполнить компоновку напрямую в ТабличныйДокумент. В этом случае расчет гарантированно выполнится синхронно.

Рассмотрим пример формирования оборотно-сальдовой ведомости на сервере:


&НаСервере
Процедура СформироватьИСохранитьОСВНаСервере(ПутьКФайлу, Организация, НачалоПериода, КонецПериода)
    
    // Создаем объект отчета
    ОтчетОбъект = Отчеты.ОборотноСальдоваяВедомость.Создать();
    
    // Инициализируем типовые настройки отчета
    ОтчетОбъект.ЗаполнитьНачальныеНастройки();
    
    // Получаем компоновщик настроек
    Настройки = ОтчетОбъект.КомпоновщикНастроек.Настройки;
    
    // Устанавливаем параметры периода и организации
    Настройки.ПараметрыДанных.УстановитьЗначениеПараметра("НачалоПериода", НачалоПериода);
    Настройки.ПараметрыДанных.УстановитьЗначениеПараметра("КонецПериода", КонецПериода);
    Настройки.ПараметрыДанных.УстановитьЗначениеПараметра("Организация", Организация);
    
    // Создаем результирующий табличный документ
    ТабДок = Новый ТабличныйДокумент;
    
    // Выполняем синхронную компоновку результата
    ОтчетОбъект.СкомпоноватьРезультат(ТабДок);
    
    // Сохраняем готовый результат в требуемом формате
    ТабДок.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.XLSX);
    
КонецПроцедуры

Разберем достоинства такого подхода: код выполняется линейно и предсказуемо, не зависит от элементов пользовательского интерфейса, не создает фоновых заданий и может без доработок использоваться в регламентных заданиях или внешних обработках.

Способ 2. Ожидание завершения длительной операции на клиенте

Если по условиям задачи отчет обязательно должен остаться открытым перед глазами пользователя на форме, сохранение необходимо привязывать не к моменту нажатия кнопки, а к событию фактического завершения фонового расчета.

Разберем по шагам алгоритм работы с длительными операциями:

  1. При запуске формирования перехватываются параметры запущенного фонового задания (структура длительной операции).
  2. На клиенте вызывается метод подсистемы БСП ДлительныеОперацииКлиент.ОжидатьЗавершение(), в который передаются параметры фонового задания и ОписаниеОповещения завершения.
  3. Интерфейс не блокируется, опрос фонового задания берет на себя БСП.
  4. В процедуре-обработчике оповещения проверяется успешность выполнения фонового задания. Как только статус равен "Выполнено", из формы считывается готовый табличный документ и вызывается метод Записать().

Если доступ к модулям БСП ограничен, аналогичный опрос организуется вручную через метод платформы ПодключитьОбработчикОжидания() с интервалом в 1–2 секунды. Обработчик проверяет готовность данных на сервере, и при получении готового результата отключается методом ОтключитьОбработчикОжидания(), после чего выполняет сохранение.

Резюме: попытка вставить паузу при работе с асинхронными формами является тупиковым решением. Для программной выгрузки всегда предпочтительно прямое серверное формирование отчета в ТабличныйДокумент через СкомпоноватьРезультат().

← На главную