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