Вывод итогов для сложных вычисляемых полей в СКД 1С: подробное руководство

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

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

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

В чем суть проблемы: почему стандартные итоги СКД выдают пустые значения?

Представим типовую бизнес-задачу. У нас есть показатели Приход и Расход. Нам необходимо разделить общую колонку прихода на два отдельных вычисляемых поля в зависимости от наличия расхода по конкретной аналитике (например, связке Контрагент, Договор и НоменклатурнаяГруппа):

  1. ПриходБезРасхода — значение выводится только в том случае, если по данной аналитике расход равен нулю.
  2. ПриходСРасходом — значение выводится, если по аналитике зафиксирован ненулевой расход.

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

Когда мы пишем простое условие в вычисляемом поле, СКД пытается применить его на уровне общего итога. Но на уровне общего итога у системы нет конкретного контрагента или договора — эти поля находятся в состоянии NULL или агрегированы. В результате функция ВычислитьВыражение возвращает некорректный результат, и общий итог получается либо пустым, либо неверным.

Анализ классических ошибок разработчиков

Проанализируем типичные ошибки, которые часто допускают программисты при попытке решить эту задачу:

  1. Написание формул в выражении вычисляемого поля на первой вкладке. Попытка использовать сложные агрегатные функции и конструкции ВычислитьВыражение в качестве формулы самого вычисляемого поля неэффективна. На этой вкладке формулы рассчитываются для детальных записей (строк выборки), где агрегаты еще не сформированы.
  2. Неверный контекст в ресурсах. Использование выражения вида:
    
    Выбор когда ВычислитьВыражение("Сумма(Расход)", "Контрагент, Договор, НоменклатурнаяГруппа") <> 0 тогда 0 Иначе Сумма(ПриходВместе) Конец
    
    Такая формула отлично сработает на уровне самой группировки, но на уровне общего итога или родительских группировок контекст полей теряется, приводя к пустому результату.
  3. Синтаксическая ошибка сравнения массивов. Попытка написать условие: Выбор когда ВычислитьВыражениеСГруппировкойМассив(...) = 0 тогда ... Функция ВычислитьВыражениеСГруппировкойМассив возвращает массив значений. В языке выражений СКД сравнение массива с числом напрямую синтаксически некорректно и возвращает ошибку или пустой результат.

Способ 1. Использование универсального выражения ресурса (Рекомендуемый)

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

Разберем по шагам, как настроить этот вариант:

Шаг 1. Создание вычисляемых полей. Добавим на вкладке «Вычисляемые поля» наши колонки ПриходБезРасхода и ПриходСРасходом. Поля «Выражение» для них оставляем пустыми. Это важно, так как вся логика расчета будет сосредоточена в ресурсах.

Шаг 2. Настройка ресурса для поля "ПриходБезРасхода". Перейдем на вкладку «Ресурсы», добавим наше поле ПриходБезРасхода и в колонке «Выражение» напишем следующую формулу:


Сумма(ВычислитьВыражениеСГруппировкойМассив("Выбор когда Сумма(Расход) = 0 тогда Сумма(ПриходВместе) иначе 0 Конец", "Контрагент, Договор, НоменклатурнаяГруппа"))

Шаг 3. Настройка ресурса для поля "ПриходСРасходом". Аналогично добавим поле ПриходСРасходом в ресурсы со следующим выражением:


Сумма(ВычислитьВыражениеСГруппировкойМассив("Выбор когда Сумма(Расход) <> 0 тогда Сумма(ПриходВместе) иначе 0 Конец", "Контрагент, Договор, НоменклатурнаяГруппа"))

Как это работает:

Способ 2. Разделение расчета ресурсов по группировкам ("Рассчитывать по...")

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

Разберем, как распределить формулы:

Настройка для детальной группировки:

  1. Добавляем поле ПриходБезРасхода в ресурсы.
  2. В выражении пишем простую быструю формулу: Выбор когда Сумма(Расход) = 0 тогда Сумма(ПриходВместе) иначе 0 Конец
  3. В колонке «Рассчитывать по...» указываем конкретные группировки: Контрагент, Договор, НоменклатурнаяГруппа.

Настройка для вышестоящих группировок и Общего итога:

  1. Добавляем поле ПриходБезРасхода в ресурсы второй строкой.
  2. В выражении пишем формулу агрегации через массив:
    
    Сумма(ВычислитьВыражениеСГруппировкойМассив("Выбор когда Сумма(Расход) = 0 тогда Сумма(ПриходВместе) иначе 0 Конец", "Контрагент, Договор, НоменклатурнаяГруппа"))
    
  3. В колонке «Рассчитывать по...» выбираем все остальные группировки (например, организации, направления деятельности) и обязательно указываем значение Общий итог.

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

Способ 3. Перенос логики на уровень запроса (Метод автора темы)

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

Для этого мы воспользуемся механизмом временных таблиц и левым соединением. Рассмотрим пример реализации такого запроса:


// Шаг 1. Получаем агрегированные данные по расходу в разрезе ключевой аналитики
ВЫБРАТЬ
    Продажи.Контрагент КАК Контрагент,
    Продажи.Договор КАК Договор,
    Продажи.НоменклатурнаяГруппа КАК НоменклатурнаяГруппа,
    СУММА(Продажи.Расход) КАК Расход
ПОМЕСТИТЬ ВТ_РасходПоАналитике
ИЗ
    РегистрНакопления.Продажи КАК Продажи
СГРУППИРОВАТЬ ПО
    Продажи.Контрагент,
    Продажи.Договор,
    Продажи.НоменклатурнаяГруппа
;

////////////////////////////////////////////////////////////////////////////////
// Шаг 2. Соединяем основную таблицу с таблицей признака расхода
ВЫБРАТЬ
    ОсновнойЗапрос.Контрагент КАК Контрагент,
    ОсновнойЗапрос.Договор КАК Договор,
    ОсновнойЗапрос.НоменклатурнаяГруппа КАК НоменклатурнаяГруппа,
    ОсновнойЗапрос.ПриходВместе КАК ПриходВместе,
    ЕСТЬNULL(ВТ_РасходПоАналитике.Расход, 0) КАК Расход,
    // Раскидываем показатели по колонкам прямо в запросе
    ВЫБОР
        КОГДА ЕСТЬNULL(ВТ_РасходПоАналитике.Расход, 0) = 0
            ТОГДА ОсновнойЗапрос.ПриходВместе
        ИНАЧЕ 0
    КОНЕЦ КАК ПриходБезРасхода,
    ВЫБОР
        КОГДА ЕСТЬNULL(ВТ_РасходПоАналитике.Расход, 0) <> 0
            ТОГДА ОсновнойЗапрос.ПриходВместе
        ИНАЧЕ 0
    КОНЕЦ КАК ПриходСРасходом
ИЗ
    РегистрНакопления.Продажи КАК ОсновнойЗапрос
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_РасходПоАналитике КАК ВТ_РасходПоАналитике
        ПО ОсновнойЗапрос.Контрагент = ВТ_РасходПоАналитике.Контрагент
        И ОсновнойЗапрос.Договор = ВТ_РасходПоАналитике.Договор
        И ОсновнойЗапрос.НоменклатурнаяГруппа = ВТ_РасходПоАналитике.НоменклатурнаяГруппа

При таком подходе поля ПриходБезРасхода и ПриходСРасходом приходят в СКД уже заполненными на уровне детальных записей. Все, что остается сделать разработчику — это зайти на вкладку «Ресурсы» и добавить эти поля со стандартным выражением Сумма(ПриходБезРасхода) и Сумма(ПриходСРасходом) для всех группировок. Итоги рассчитаются автоматически и абсолютно корректно.

Сравнение подходов (СКД vs Запрос):

Дополнительные возможности: функция ВычислитьВыражениеСГруппировкойТаблицаЗначений

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

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

← На главную