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