Как рассчитать итог по группировкам СКД по произвольной формуле (разности)?

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

Приветствуем вас, уважаемые коллеги! Сегодня мы детально разберем крайне интересную и практичную задачу из мира разработки на платформе 1С:Предприятие 8. При создании отчетов в Системе компоновки данных (СКД) мы регулярно сталкиваемся со стандартным суммированием ресурсов. Платформа по умолчанию складывает показатели снизу вверх. Но что делать, если перед нами стоит задача рассчитать общий итог по определенной математической формуле? Например, когда нам нужно получить результат вычитания одной номенклатурной группы из другой: НоменклатурнаяГруппа1 - НоменклатурнаяГруппа2 - НоменклатурнаяГруппа3.

Подобные задачи часто возникают при построении управленческого баланса, отчетов о прибылях и убытках (P&L), расчете маржинальности или анализе движения денежных средств, где доходы должны складываться, а расходы — вычитаться, хотя в структуре отчета они выводятся в виде одной линейной группировки.

Давайте вместе проанализируем ситуацию, рассмотрим все возможные способы решения этой задачи и выберем наиболее изящный и производительный вариант.

Способ 1. Реализация логики знаков внутри SQL-запроса

Рассмотрим классический подход, который часто выбирают разработчики баз данных. Наша цель — на уровне СУБД определить, какие группы должны увеличивать итоговую сумму, а какие — уменьшать. Для этого мы можем использовать конструкцию ВЫБОР КОГДА непосредственно в тексте запроса схемы компоновки данных.

Допустим, список вычитаемых групп у нас предопределен или задается пользователем через параметры. Шаг за шагом выполним следующие действия:

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

ВЫБРАТЬ
    Продажи.НоменклатурнаяГруппа КАК НоменклатурнаяГруппа,
    ВЫБОР
        КОГДА Продажи.НоменклатурнаяГруппа В (&ОтрицательныеГруппы)
            ТОГДА -Продажи.СуммаОборот
        ИНАЧЕ Продажи.СуммаОборот
    КОНЕЦ КАК СуммаДляИтога
ИЗ
    РегистрНакопления.ПродажиОбороты КАК Продажи

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

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

Способ 2. Использование вычисляемых полей СКД (без изменения запроса)

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

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

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

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

Способ 3. Расчет разности через функцию ВычислитьВыражение в ресурсах

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

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


ВычислитьВыражение("Сумма(Выбор Когда НоменклатурнаяГруппа = &Группа1 Тогда ПолеСуммы Иначе 0 Конец)", , "ОбщийИтог") 
- ВычислитьВыражение("Сумма(Выбор Когда НоменклатурнаяГруппа = &Группа2 Тогда ПолеСуммы Иначе 0 Конец)", , "ОбщийИтог") 
- ВычислитьВыражение("Сумма(Выбор Когда НоменклатурнаяГруппа = &Группа3 Тогда ПолеСуммы Иначе 0 Конец)", , "ОбщийИтог")

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

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

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

Рассмотрим структуру выражения для ресурса:


Сумма(ВычислитьВыражениеСГруппировкойМассив("Сумма(ПолеСуммы)", "НоменклатурнаяГруппа", "НоменклатурнаяГруппа = &Группа1")) 
- Сумма(ВычислитьВыражениеСГруппировкойМассив("Сумма(ПолеСуммы)", "НоменклатурнаяГруппа", "НоменклатурнаяГруппа = &Группа2"))
- Сумма(ВычислитьВыражениеСГруппировкойМассив("Сумма(ПолеСуммы)", "НоменклатурнаяГруппа", "НоменклатурнаяГруппа = &Группа3"))

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

Способ 5. Динамические нелинейные итоги через Таблицу Значений

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

Мы можем передать эту таблицу в функцию общего модуля конфигурации для обработки на встроенном языке 1С. Выражение ресурса будет выглядеть так:


ИмяВашегоОбщегоМодуля.РассчитатьСложныйИтог(
    ВычислитьВыражениеСГруппировкойТаблицаЗначений("НоменклатурнаяГруппа, Сумма(ПолеСуммы) КАК СуммаГруппы", "НоменклатурнаяГруппа")
)

А в самом общем модуле (который должен иметь признак «Вызов сервера» или «Клиент (обычное приложение), Сервер») мы пишем код обработки полученной таблицы:


Функция РассчитатьСложныйИтог(ТаблицаИтогов) Экспорт
    
    РезультатРасчета = 0;
    
    Для Каждого СтрокаДанных Из ТаблицаИтогов Цикл
        Если СтрокаДанных.НоменклатурнаяГруппа = Справочники.НоменклатурныеГруппы.Группа1 Тогда
            РезультатРасчета = РезультатРасчета + СтрокаДанных.СуммаГруппы;
        ИначеЕсли СтрокаДанных.НоменклатурнаяГруппа = Справочники.НоменклатурныеГруппы.Группа2 
            Или СтрокаДанных.НоменклатурнаяГруппа = Справочники.НоменклатурныеГруппы.Группа3 Тогда
            РезультатРасчета = РезультатРасчета - СтрокаДанных.СуммаГруппы;
        Иначе
            РезультатРасчета = РезультатРасчета + СтрокаДанных.СуммаГруппы;
        КонецЕсли;
    КонецЦикла;
    
    Возврат РезультатРасчета;
    
КонецФункции

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

Способ 6. Использование собственных макетов группировок

Если макет вашего отчета имеет строго фиксированную структуру (например, регламентированная финансовая форма), вы можете решить задачу визуально с помощью Макетов СКД:

  1. Перейдите на вкладку Макеты вашей схемы компоновки данных.
  2. Создайте индивидуальный макет для нужного поля группировки или общего итога.
  3. Определите ячейки макета и свяжите их с параметрами.
  4. В выражении параметра макета пропишите математическую формулу взаимодействия ресурсов, например: ПараметрИтога = РесурсГруппы1 - РесурсГруппы2.

Этот способ гарантирует идеальный внешний вид отчета, однако лишает его динамичности при изменении состава выводимых группировок пользователем.

Подводя итог, отметим: выбор метода полностью зависит от гибкости разрабатываемого отчета. Если структура и состав групп могут произвольно меняться пользователем в режиме Предприятия — используйте Способ 2 или Способ 3. Если же вам нужно реализовать сложный финансовый алгоритм — смело внедряйте расчеты через функции общего модуля (Способ 5).

← На главную