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