При работе с Системой Компоновки Данных (СКД) в 1С часто возникает задача отобразить итоговые значения не только в конце таблицы, но и в её "шапке" или "подвале". Например, необходимо показать общую сумму по контрагенту или заказу прямо в заголовке соответствующей группировки. Стандартные средства СКД не позволяют сделать это декларативно через настройки. Проблема заключается в том, что на момент вывода заголовка группировки итоги по ней еще не рассчитаны. Давайте разберем, как решить эту задачу программно с помощью обработки результатов компоновки.
Основная сложность состоит в том, что СКД обрабатывает данные последовательно. Когда выводится заголовок группировки, система еще не знает, какие строки войдут в эту группу и какими будут итоговые значения. Прямое обращение к ресурсу (итоговому полю) из макета заголовка вернет либо ошибку, либо пустое значение.
Для решения этой задачи нам потребуется вмешаться в процесс формирования отчета уже после того, как компоновщик данных подготовил табличный документ, но до его финального отображения пользователю. Мы будем использовать специальные функции СКД, которые позволяют выполнять вычисления по уже скомпонованным данным. Наиболее подходящей для нашей цели является функция ВычислитьВыражениеСГруппировкойМассив(). Она позволяет рассчитать значение выражения для определенных группировок отчета.
Рассмотрим пошаговый алгоритм решения этой задачи.
Первым делом необходимо подготовить сам отчет в конфигураторе.
Теперь СКД знает, что в эту ячейку при формировании отчета нужно будет подставить значение параметра СуммаИтогПоГруппировке. Наша задача — вычислить это значение и передать его в ячейку.
Всю основную логику мы реализуем в коде модуля формы отчета. Нам понадобится обработчик события ПриКомпоновкеРезультата. Это событие срабатывает как раз в тот момент, когда данные уже скомпонованы, но результат еще не выведен на экран.
Давайте проанализируем код, который решает нашу задачу.
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
// Отменяем стандартную обработку, так как мы будем формировать результат сами
СтандартнаяОбработка = Ложь;
// 1. Инициализируем компоновщик макета и получаем процессор компоновки
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.ПолучитьНастройки(), ДанныеРасшифровки);
ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);
// 2. Инициализируем процессор вывода результата в табличный документ
ДокументРезультат.Очистить();
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);
// 3. Вычисляем и устанавливаем значения параметров в макеты
// Получаем все элементы результата (группировки, строки)
ЭлементРезультата = ПроцессорКомпоновкиДанных.ПолучитьЭлементРезультата();
// Обходим все элементы рекурсивно
Для Каждого Элемент Из ЭлементРезультата.Элементы Цикл
УстановитьПараметрыМакета(Элемент, ДокументРезультат);
КонецЦикла;
КонецПроцедуры
Процедура УстановитьПараметрыМакета(Элемент, ДокументРезультат)
// Если у текущего элемента есть вложенные элементы (т.е. это группировка)
Если Элемент.Элементы.Количество() > 0 Тогда
// 4. Вычисляем итог для текущей группировки
// Рассчитываем значение ресурса "Сумма" для текущей группировки
МассивИтогов = Элемент.ВычислитьВыражениеСГруппировкойМассив("Сумма(СуммаДокумента)", "Контрагент");
// Проверяем, что результат получен
Если МассивИтогов.Количество() > 0 Тогда
ИтоговаяСумма = МассивИтогов[0].Значение;
// 5. Находим ячейку в табличном документе и устанавливаем параметр
// Обходим все области макета, связанные с текущим элементом
Для Каждого Область Из Элемент.ПолучитьОбласти() Цикл
Если Область.ТипМакета = ТипМакетаКомпоновкиДанных.ЗаголовокГруппировки Тогда
// Ищем нашу ячейку по имени параметра
НайденнаяЯчейка = Область.Область.НайтиТекст("СуммаИтогПоГруппировке");
Если НайденнаяЯчейка <> Неопределено Тогда
// Устанавливаем вычисленное значение
НайденнаяЯчейка.Параметр = "СуммаИтогПоГруппировке";
НайденнаяЯчейка.Текст = Формат(ИтоговаяСумма, "ЧДЦ=2; ЧН=0.00");
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЕсли;
// 6. Рекурсивно обходим дочерние элементы
Для Каждого ВложенныйЭлемент Из Элемент.Элементы Цикл
УстановитьПараметрыМакета(ВложенныйЭлемент, ДокументРезультат);
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Давайте подробнее разберем, что происходит в приведенном коде.
ПриКомпоновкеРезультата мы сначала отменяем стандартную обработку (СтандартнаяОбработка = Ложь;), чтобы взять управление на себя. Затем стандартным образом инициализируем КомпоновщикМакета и ПроцессорКомпоновкиДанных.ДокументРезультат. На этом этапе отчет уже сформирован, но в ячейке нашего макета пока пусто или стоит имя параметра.ПроцессорКомпоновкиДанных.ПолучитьЭлементРезультата()) и начинаем обходить все его дочерние элементы. Для этого мы вызываем рекурсивную процедуру УстановитьПараметрыМакета.Элемент.Элементы.Количество() > 0), мы вызываем ключевую функцию:
МассивИтогов = Элемент.ВычислитьВыражениеСГруппировкойМассив("Сумма(СуммаДокумента)", "Контрагент");
Разберем её параметры:
Сумма() по полю СуммаДокумента. Это поле должно быть ресурсом в СКД.Функция возвращает массив, так как группировок с таким именем может быть несколько. В нашем случае для каждой итерации элемента мы получаем итог именно по текущей группировке, поэтому берем первый элемент массива МассивИтогов[0].Значение.
Элемент.ПолучитьОбласти()), находим макет заголовка (ТипМакета = ТипМакетаКомпоновкиДанных.ЗаголовокГруппировки) и в нем ищем ячейку по тексту-имени параметра, который мы задали на Шаге 1. Найдя ячейку, мы заменяем её текст на отформатированное итоговое значение.Таким образом, мы сначала позволяем СКД построить отчет, а затем "пробегаемся" по его структуре, вычисляем необходимые нам итоги "задним числом" и вставляем их в нужные места в уже готовом табличном документе. Этот метод является универсальным и подходит для сложных отчетов с несколькими уровнями группировок.