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