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

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

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

Способ 1. Базовая настройка ресурсов по группировкам

Самый простой и очевидный способ реализовать разные итоги для разных группировок — это использование стандартной вкладки «Ресурсы» в конструкторе схемы компоновки данных. Платформа 1С позволяет добавлять одно и то же поле в список ресурсов несколько раз, задавая для каждого индивидуальное выражение расчета и жестко привязывая его к конкретной группировке.

Выполним настройку по шагам:

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

Аналогичные действия необходимо выполнить для всех остальных полей остатков и оборотов (Приход, Расход, ОстатокНаКонецПериода), если для них требуется такое же поведение.

Проблема задвоения данных при простом суммировании

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

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

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

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


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

Разберем детально, как работает эта конструкция:

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

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

Как защитить отчет от «поломки» при изменении структуры пользователем

Главный недостаток жесткой привязки формул ресурсов к конкретным именам полей в колонке «Рассчитывать по...» заключается в том, что эта схема нестабильна при пользовательских настройках. Если конечный пользователь в режиме «1С:Предприятие» решит изменить структуру отчета (например, перетащит Регистратор выше Номенклатуры, добавит промежуточную группировку по складам или вовсе отключит вывод регистраторов), расчет итогов сломается либо начнет выдавать пустые значения.

Рассмотрим несколько вариантов, как мы можем защитить наш отчет от подобных проблем:

Вариант А. Ограничение прав пользователя на изменение структуры

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

Для этого в конфигураторе на вкладке «Настройки» выберем группировку Регистратор, перейдем в свойства этой группировки (в нижней части экрана) и снимем флаг «Доступность изменения для пользователя». Аналогично поступим с группировкой Номенклатура. Таким образом, структура отчета превратится в жесткий, стабильный каркас, формулы ресурсов никогда не потеряют связь со своими группировками.

Вариант Б. Свертка данных на уровне SQL-запроса

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


ВЫБРАТЬ
    РегистрНакопления.Номенклатура КАК Номенклатура,
    РегистрНакопления.Регистратор КАК Регистратор,
    МАКСИМУМ(РегистрНакопления.ОстатокНаНачало) КАК ОстатокНаНачалоПериода
ИЗ
    РегистрНакопления.ИмяРегистра КАК РегистрНакопления
СГРУППИРОВАТЬ ПО
    РегистрНакопления.Номенклатура,
    РегистрНакопления.Регистратор

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

← На главную