Фоновое формирование отчета СКД на УФ средствами БСП

Опубликовал Сан Саныч (herfis) в раздел Программирование - Практика программирования

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

Начну с недоработок (при желании - устранимых).

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

2) по аналогичной причине (лень) не реализована фоновая расшифровка - для этого необходимо перехватывать штатную расшифровку

Возможно, перечисленные недоработки будут устранены позднее.

Да, еще момент. Предполагается, что дорабатываемый отчет имеет стандартные наименования своих компонентов - поле табличного документа на форме называется "Результат", есть строковый реквизит формы "ДанныеРасшифровки", основной реквизит формы называется "Отчет".

Итак. Сам алгоритм, исполняемый в фоновом задании, размещается в модуле менеджера целевого отчета. Тут все просто и никакой магии:

Процедура ВыполнитьКомпоновкуВФоне(СтруктураНастроек, АдресРезультатаВоВременномХранилище) Экспорт
    
    ТабличныйДокументРезультат = Новый ТабличныйДокумент;
    ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;
    
    Отчет = Отчеты[СтруктураНастроек.ИмяОтчета].Создать();
    Отчет.КомпоновщикНастроек.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(Отчет.СхемаКомпоновкиДанных));
    Отчет.КомпоновщикНастроек.ЗагрузитьНастройки(СтруктураНастроек.НастройкиКомпоновщика);
    Отчет.СкомпоноватьРезультат(ТабличныйДокументРезультат, ДанныеРасшифровки);
    
    ПоместитьВоВременноеХранилище(ДанныеРасшифровки, СтруктураНастроек.АдресДанныхРасшифровки);
    ПоместитьВоВременноеХранилище(ТабличныйДокументРезультат, АдресРезультатаВоВременномХранилище);
    
КонецПроцедуры

Замечу, что выполняется штатная компоновка отчета со всем тем, что напихано в ПриКомпоновкеРезультата.

Осталось это выполнить в фоне, получить результат и красиво все показать пользователю.
Очевидно, что для этого нужно перехватить событие компоновки на клиенте. К сожалению, человеческого способа это сделать нет. Придется подсовывать свою команду вместо стандартной. Отключаем в доступных командах отчета штатное "Сформировать", выводим на форму кнопку со своей командой. Оформляем, чтобы выглядела аналогично.
Подсказка - если кнопка появляется в панели отчета после каких-то стандартных команд типа выбора варианта - просто добавьте эти стандартные команды в панель явно - тогда их расположением можно будет управлять.
Обработчик нашей команды "Сформировать":
&НаКлиенте
Процедура Сформировать(Команда)
	
	ФоновоеФормированияОтчета = ЗапускФормированияОтчетаСервер();
	Если ФоновоеФормированияОтчета <> Неопределено Тогда
		НастройкиОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтотОбъект);
		ОбработчикЗавершения = Новый ОписаниеОповещения("ЗавершениеФоновогоФормированияОтчетаКлиент", ЭтотОбъект);
		ДлительныеОперацииКлиент.ОжидатьЗавершение(ФоновоеФормированияОтчета, ОбработчикЗавершения, НастройкиОжидания);
	КонецЕсли;	
	
КонецПроцедуры

Вот в этих нескольких строчках и зарыта вся магия БСП. Этот код реализует целую кучу вещей - запускается фоновый алгоритм, открывается форма ожидания его завершения (форма с крутящимся колесиком, в которой фоновое задание можно отменить), ожидается завершение фонового задания в обработке ожидания и при завершении задания - запускается указанный обработчик для обработки результата. Все это БСП делает за нас и не переусложняет наш отчет. За что ей большое спасибо.

Теперь собственно запуск фонового задания по протоколу БСП:
&НаСервере
Функция ЗапускФормированияОтчетаСервер()
	
	ОтчетОбъект = РеквизитФормыВЗначение("Отчет");
	МетаданныеОтчета = ОтчетОбъект.Метаданные();
	ИмяОтчета = МетаданныеОтчета.Имя;
	
	Если НЕ Метаданные.Отчеты.Содержит(МетаданныеОтчета) Тогда // если отчет внешний, то выполняем компоновку стандартно
		СкомпоноватьРезультат();
		Возврат Неопределено;
	КонецЕсли;
	
	Если ПустаяСтрока(ДанныеРасшифровки) Тогда
		ДанныеРасшифровки = ПоместитьВоВременноеХранилище(Новый ДанныеРасшифровкиКомпоновкиДанных, УникальныйИдентификатор);
	КонецЕсли;
	
	ПараметрыФормирования = Новый Структура;
	ПараметрыФормирования.Вставить("ИмяОтчета", ИмяОтчета);
	ПараметрыФормирования.Вставить("АдресДанныхРасшифровки", ДанныеРасшифровки);
	ПараметрыФормирования.Вставить("НастройкиКомпоновщика", Отчет.КомпоновщикНастроек.ПолучитьНастройки());
	
	НастройкиЗапуска = ДлительныеОперации.ПараметрыВыполненияВФоне(УникальныйИдентификатор);
	
	Возврат ДлительныеОперации.ВыполнитьВФоне(СтрШаблон("Отчеты.%1.ВыполнитьКомпоновкуВФоне", ИмяОтчета), ПараметрыФормирования, НастройкиЗапуска);
	
КонецФункции

Остался последний штрих - обработчик результата фонового задания (замечу, что данные расшифровки уже были положены по нужному адресу еще в фоновом задании, поэтому осталось получить только табличный документ):

&НаКлиенте
Процедура ЗавершениеФоновогоФормированияОтчетаКлиент(РезультатФоновойЗадачи, ДополнительныеПараметры) Экспорт
	
	Если РезультатФоновойЗадачи <> Неопределено И РезультатФоновойЗадачи.Статус = "Выполнено" Тогда
		Результат = ПолучитьИзВременногоХранилища(РезультатФоновойЗадачи.АдресРезультата);
		Элементы.Результат.ОтображениеСостояния.ДополнительныйРежимОтображения = ДополнительныйРежимОтображения.НеИспользовать;
		Элементы.Результат.ОтображениеСостояния.Текст = "";
	КонецЕсли;
	
КонецПроцедуры

Это все. Как видим - благодаря БСП нужно добавить лишь небольшое количество достаточно универсального кода (тестировалось под БСП 2.3.2.45).

См. также

Комментарии
1. Николай Васильев (vasilev2015) 214 17.05.17 13:50 Сейчас в теме
2. Владимир Клименко (KliMich) 17.05.17 19:47 Сейчас в теме
Респект!
Кратко и содержательно
3. Игорь Хитров (Новенький_2209) 18.05.17 14:59 Сейчас в теме
Краткость - сестра таланта! Спасибо!
4. Роман Уничкин (unichkin) 401 19.05.17 01:46 Сейчас в теме
Плюс за пример работы с длительными операциями. Но вообще при разработке отчетов на БСП лучше избегать создания формы - тогда автоматически решается куча проблем, в т.ч. и фоновое формирование. И кстати - с формой все-равно можно будет работать, правда только программно.
5. Сан Саныч (herfis) 111 19.05.17 09:20 Сейчас в теме
(4) Спасибо за замечание! В самом деле - если используется подсистема вариантов отчетов и отчет ее использует, то проблема уже решена в общей форме отчетов этой подсистемы. Там делается тоже самое - подменяется стандартная команда и выполняется компоновка в фоне. Так что при полном использовании БСП моя публикация является велосипедом. Я совсем упустил этот момент, т.к. активно дорабатываю самописку с выборочным внедрением БСП (без подсистемы "Вариантов отчетов"). Сейчас отражу это в публикации.
Оставьте свое сообщение