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