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

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

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

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

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

Способ 1. Раздельный расчет ресурсов и функция ВычислитьВыражениеСГруппировкойМассив

Проанализируем механизм работы вкладки Ресурсы в схеме компоновки данных. Платформа позволяет задавать различные формулы расчета для одного и того же ресурса в зависимости от текущей группировки (колонка Рассчитывать по...).

Для реализации логики нам потребуется поле признака выполнения плана. Вычислим его на уровне детальных записей или добавим в запрос булево поле ПланВыполнен (где истина означает, что фактические продажи превышают или равны плану: Факт >= План).

Настроим расчет ресурса Бонус на вкладке Ресурсы тремя отдельными строками:

  1. Для детальных записей (или группировки по товарам):

    Указываем расчет индивидуального бонуса строки:

    Сумма(Бонус)
  2. Для группировки Договор:

    Проверяем условие выполнения плана по всем строкам договора. Если хотя бы по одному обязательному товару план не выполнен, булево значение Минимум(ПланВыполнен) вернет значение Ложь (так как в 1С Ложь < Истина):

    
    ВЫБОР 
        КОГДА Минимум(ПланВыполнен) ТОГДА Сумма(Бонус) 
        ИНАЧЕ 0 
    КОНЕЦ
    

    Если требуется также проверять выполнение общего плана по договору, выражение можно расширить:

    
    ВЫБОР 
        КОГДА Минимум(ПланВыполнен) И Сумма(Факт) >= Сумма(План) ТОГДА Сумма(Бонус) 
        ИНАЧЕ 0 
    КОНЕЦ
    
  3. Для группировок Контрагент и Общий итог:

    Чтобы родительская группировка не суммировала исходные записи товаров напрямую, а сложила результаты, уже вычисленные для каждого договора, применим агрегатную функцию контекстного вычисления ВычислитьВыражениеСГруппировкойМассив():

    
    Сумма(ВычислитьВыражениеСГруппировкойМассив("ВЫБОР КОГДА Минимум(ПланВыполнен) ТОГДА Сумма(Бонус) ИНАЧЕ 0 КОНЕЦ", "Договор"))
    

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

Способ 2. Предварительный расчет показателей в тексте запроса

Использование сложных контекстных функций СКД на больших объемах данных может замедлять формирование отчета. Альтернативный и наиболее производительный подход — перенести расчет условий на уровень языка запросов с использованием временных таблиц.

Разберем структуру такого запроса:

  1. В первой временной таблице собираем продажи и планы в разрезе договоров и номенклатуры.
  2. Во второй временной таблице группируем данные до уровня договоров, проверяя минимальное выполнение условий и формируя итоговую сумму бонуса по договору.
  3. В основном запросе соединяем детальные строки с итогами договора.

Посмотрим на пример реализации запроса:


ВЫБРАТЬ
    Продажи.Контрагент КАК Контрагент,
    Продажи.Договор КАК Договор,
    Продажи.Номенклатура КАК Номенклатура,
    Продажи.План КАК План,
    Продажи.Факт КАК Факт,
    Продажи.Бонус КАК Бонус,
    ВЫБОР КОГДА Продажи.Факт >= Продажи.План ТОГДА 1 ИНАЧЕ 0 КОНЕЦ КАК ВыполненФлаг
ПОМЕСТИТЬ ВТ_Детализация
ИЗ
    РегистрНакопления.Продажи КАК Продажи
;

ВЫБРАТЬ
    ВТ.Договор КАК Договор,
    ВЫБОР 
        КОГДА МИНИМУМ(ВТ.ВыполненФлаг) = 1 И СУММА(ВТ.Факт) >= СУММА(ВТ.План) 
            ТОГДА СУММА(ВТ.Бонус) 
        ИНАЧЕ 0 
    КОНЕЦ КАК БонусДоговора
ПОМЕСТИТЬ ВТ_ИтогиПоДоговорам
ИЗ
    ВТ_Детализация КАК ВТ
СГРУППИРОВАТЬ ПО
    ВТ.Договор
;

ВЫБРАТЬ
    ВТ.Контрагент КАК Контрагент,
    ВТ.Договор КАК Договор,
    ВТ.Номенклатура КАК Номенклатура,
    ВТ.План КАК План,
    ВТ.Факт КАК Факт,
    ВТ.Бонус КАК БонусНоменклатуры,
    Итоги.БонусДоговора КАК БонусДоговора
ИЗ
    ВТ_Детализация КАК ВТ
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ИтогиПоДоговорам КАК Итоги
        ПО ВТ.Договор = Итоги.Договор

При таком подходе в СКД поле БонусДоговора рассчитывается как Максимум(БонусДоговора) на уровне договора, а на уровне контрагента и общего итога суммируется через Сумма(ВычислитьВыражениеСГруппировкойМассив("Максимум(БонусДоговора)", "Договор")), что исключает ошибки дублирования сумм при размножении строк.

Ограничения и рекомендации

Выясним важные технические особенности при работе с функцией ВычислитьВыражениеСГруппировкойМассив():

  1. Зависимость от структуры: Имя группировки (например, "Договор") жестко передается строковым литералом. Если пользователь отчета в режиме предприятия удалит эту группировку из настроек или переименует поле, отчет выдаст ошибку компоновки. Если отчет предполагает произвольную пользовательскую настройку структуры, расчет в запросе предпочтительнее.
  2. Потребление памяти: Формирование массивов значений в памяти платформы требует больше ресурсов, чем стандартная потоковая агрегация СУБД. На отчетах с сотнями тысяч записей рекомендуется выносить расчет условий в пакетные запросы.
← На главную