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