Как в СКД рассчитать сложные ресурсы и проценты по группировкам при ограничении вложенных агрегатных функций

Программист 1С v8.3 (Управляемые формы) Управленческий учет IT и автоматизация бизнеса
← На главную

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

Анализ проблемы: почему возникает ошибка вложенности

Разберем ситуацию, когда нам необходимо получить сумму уже рассчитанного агрегатного показателя. В СКД ресурсы рассчитываются на основе детальных записей. Когда мы пишем Сумма(Продажи), система понимает это. Однако, если мы попытаемся написать что-то вроде Сумма(Сумма(Продажи)), система выдаст ошибку. Это происходит потому, что контекст вычисления ресурса не предполагает рекурсивного прохода по уже агрегированным данным в рамках одного выражения.

Особенно остро эта проблема проявляется при расчете процентов. Например, если нам нужно разделить сумму текущей строки на сумму по всей родительской группировке. Рассмотрим стандартный подход, который часто предлагают на форумах:

ВычислитьВыражение("Сумма(СуммаОборот)", "Контрагент")

Такой синтаксис допустим только в определенных контекстах, но как только формула усложняется (добавляются условия или расчеты средних величин), 1С блокирует выполнение. Выясним, как решать эту задачу правильно.

Решение 1: Использование функции ВычислитьВыражениеСГруппировкойМассив

Проанализируем наиболее эффективный способ обхода ошибки вложенных агрегатов. Вместо прямой агрегации агрегата мы можем использовать промежуточное преобразование данных в массив. Функция ВычислитьВыражениеСГруппировкойМассив собирает результаты вычислений по указанной группировке в коллекцию, к которой затем можно применить внешнюю агрегатную функцию.

Предположим, нам нужно просуммировать среднесуточные продажи по номенклатуре. Правильное выражение будет выглядеть так:


Сумма(ВычислитьВыражениеСГруппировкойМассив("Сумма(ВесРеализацияСреднесуточно)", "Номенклатура"))

Разберем по шагам, что здесь происходит:

  1. Внутренняя часть: "Сумма(ВесРеализацияСреднесуточно)" — это то, что мы хотим посчитать для каждой единицы группировки.
  2. Группировка: "Номенклатура" — уровень, на котором производится предварительный расчет.
  3. Массив: Система формирует массив из этих сумм для всех позиций номенклатуры.
  4. Внешняя агрегация: Функция Сумма(...) складывает элементы полученного массива.

Этот метод позволяет "обмануть" парсер СКД и выполнить двухуровневый расчет, не вызывая конфликта вложенности.

Решение 2: Работа с контекстом "ОбщийИтог" и "РодительскаяГруппировка"

Когда стоит задача рассчитать процент от общего итога или от итога вышестоящего уровня, важно правильно использовать второй параметр функции ВычислитьВыражение. Рассмотрим пример расчета веса номенклатуры относительно всей торговой точки:

Сумма(Вес) / ВычислитьВыражение("Сумма(Вес)", "ТорговаяТочка") * 100

Здесь крайне важно, чтобы имя "ТорговаяТочка" в кавычках точно совпадало с именем группировки в настройках отчета (вкладка "Настройки" -> "Структура"). Если нам нужно значение во всем отчете (независимо от фильтров в иерархии), используем системное слово "ОбщийИтог":

Сумма(Вес) / ВычислитьВыражение("Сумма(Вес)", "ОбщийИтог") * 100

Проанализируем ситуацию с кросс-таблицами (колонки по месяцам). В этом случае обычное суммирование может подвести. Чтобы получить итог "по горизонтали" (за все периоды), в функции предусмотрены параметры начала и конца диапазона. Рассмотрим пример:

ВычислитьВыражение("Сумма(Количество)", , , "Первая", "Последняя")

Решение 3: Вынос логики во внешние функции общих модулей

Если формула ресурса начинает занимать более 5 строк и превращается в "ад кавычек" (как в сообщении №11 из темы форума), пора переходить к использованию функций общих модулей. Это самый надежный способ для очень сложных расчетов.

Алгоритм действий следующий:

  1. Создаем (или используем существующий) общий модуль, например, МойОтчетВычисления. Важно: у модуля должны стоять галочки "Сервер" и "Вызов сервера", а сама функция должна быть помечена словом Экспорт.
  2. В выражении ресурса СКД вызываем эту функцию, передавая в нее данные.

Пример функции в модуле:


Функция РассчитатьСложныйПроцент(МассивДанных) Экспорт
    Итог = 0;
    Для Каждого Значение Из МассивДанных Цикл
        Итог = Итог + Значение;
    КонецЦикла;
    // Здесь может быть любая сложная логика
    Возврат Итог;
КонецФункции

Пример вызова в СКД (вкладка "Ресурсы"):


МойОтчетВычисления.РассчитатьСложныйПроцент(ВычислитьВыражениеСГруппировкойМассив("Сумма(Вес)", "Номенклатура"))

Такой подход позволяет отлаживать код обычным конфигуратором, ставить точки останова и проверять значения переменных, что абсолютно невозможно сделать внутри строки выражения СКД.

Важные нюансы и рекомендации

Посмотрим на несколько критических моментов, которые могут свести на нет все усилия по настройке:

1. Порядок расчета ресурсов. Вычисления в СКД происходят последовательно сверху вниз по списку ресурсов. Если ваш Ресурс_2 (например, Процент) использует значение Ресурс_1 (Сумма), то Ресурс_1 обязательно должен находиться выше в списке. В противном случае в поле процента вы получите пустое значение.

2. Разделение формулы (логика А/Б). Если система ругается на вложенность в длинном выражении, попробуйте разбить его. Вместо ВычислитьВыражение("Сумма(А/Б)") попробуйте написать ВычислитьВыражение("Сумма(А)") / ВычислитьВыражение("Сумма(Б)"). Это часто снимает конфликт типов данных в парсере.

3. Использование "Рассчитывать по...". СКД позволяет задавать разные формулы для одного и того же ресурса на разных уровнях группировок. Если на уровне "Номенклатура" вам нужна простая сумма, а на уровне "Итоги по магазину" — среднее значение от этих сумм, укажите это явно в таблице ресурсов, добавив две строки для одного поля, но с разными значениями в колонке "Рассчитывать по".

4. Вкладка "Вычисляемые поля". Помним, что на этой вкладке мы пишем только базовую математику (например, Цена * Количество). Любые агрегатные функции (Сумма, Среднее, ВычислитьВыражение) должны находиться исключительно на вкладке "Ресурсы".

Разберем типичный тупик, описанный автором темы: "Запросом вычислить не могу, так как параметр вычисляется уже после запроса". В таких случаях 1С действительно предлагает либо использовать "Набор данных - Объект" (формирование таблицы значений кодом и передача ее в СКД), либо описанные выше методы с ВычислитьВыражение. Однако, использование внешних функций модуля практически всегда позволяет избежать громоздкого переписывания отчета на "Объект", сохраняя удобство и скорость разработки запроса.

← На главную