При разработке отчетов в Системе Компоновки Данных (СКД) мы часто сталкиваемся с задачей расчета производных показателей: средних цен, коэффициентов расхода материалов, процентов выполнения плана и так далее. Распространенной проблемой, с которой сталкиваются разработчики (как начинающие, так и опытные), является некорректный расчет этих показателей в группировках и общих итогах.
Рассмотрим ситуацию, описанную в теме: в детальных записях (строках номенклатуры) коэффициент рассчитывается верно, но в строке "Итого" мы видим бессмысленное огромное число — сумму всех коэффициентов, тогда как нам нужно среднее значение или пересчитанное отношение по всем данным. Разберем подробно, почему так происходит и как настроить отчет правильно.
Прежде чем переходить к настройкам в 1С, давайте поймем логику ошибки. Допустим, у нас есть данные по выпуску продукции и расходу материалов:
Если мы рассчитываем коэффициент прямо в запросе SQL, то для СКД это просто числовое поле. Когда СКД считает общий итог, она по умолчанию применяет функцию Сумма.
Неверный итог (как сейчас): 5 + 5 = 10. Это число не имеет физического смысла.
Правильный итог: Нам нужно разделить общую сумму материалов на общую сумму продукции.
(10 + 20) / (2 + 4) = 30 / 6 = 5.
Чтобы добиться правильного результата, нам нужно перенести логику вычисления из текста запроса на уровень настроек ресурсов СКД.
В исходном примере автор темы пытается выполнить деление непосредственно внутри запроса:
ЕСТЬNULL(ВЫРАЗИТЬ(ПоискМатериаловПоОтчету.КоличествоУпаковокМатериалы КАК ЧИСЛО(15, 4)), 0)
/ (ВЫРАЗИТЬ(ПроизводствоПереработчика.Количество КАК ЧИСЛО(15, 4)))
КАК СреднийРасходныйКоэфициент
Это главная ошибка. Если поле СреднийРасходныйКоэфициент приходит из запроса готовым числом, СКД не знает, как оно было получено, и просто складывает эти числа в итогах.
Что нужно сделать:
В запросе мы должны выбрать отдельно числитель и знаменатель. Не делите их в запросе! Выбирайте поля как есть (можно использовать ЕСТЬNULL для замены NULL на 0, если соединение таблиц внешнее).
Пример правильной структуры выборки в запросе:
ВЫБРАТЬ
...
ЕСТЬNULL(ПоискМатериаловПоОтчету.КоличествоУпаковокМатериалы, 0) КАК КоличествоМатериалов,
ЕСТЬNULL(ПроизводствоПереработчика.Количество, 0) КАК КоличествоПродукции,
...
Переходим на вкладку Ресурсы в конструкторе схемы компоновки данных. Именно здесь происходит магия правильных итогов.
Нам нужно добавить новое поле ресурса. Если у вас нет отдельного поля для коэффициента в наборе данных (потому что мы убрали его из запроса), мы можем создать Вычисляемое поле.
Шаг 2.1. Создание вычисляемого поля (опционально, но рекомендуется для порядка)
СреднийРасходныйКоэффициент.Шаг 2.2. Настройка формулы ресурса
Теперь на вкладке Ресурсы добавляем наше поле СреднийРасходныйКоэффициент.
В колонке Выражение мы должны написать формулу, которая говорит системе: "Сначала просуммируй все материалы, потом просуммируй всю продукцию, и только потом дели".
Формула:
Сумма(КоличествоМатериалов) / Сумма(КоличествоПродукции)
Обратите внимание: мы используем агрегатную функцию Сумма() к каждому операнду отдельно.
Сумма(Поле) равна самому значению поля, поэтому деление сработает корректно.В отличие от запроса, где мы контролируем данные, в итогах может возникнуть ситуация, когда знаменатель равен нулю (например, продукция не выпущена, а материалы списаны, или данные отфильтрованы). Если знаменатель будет равен 0, отчет выдаст ошибку "Деление на 0" при формировании.
Поэтому выражение ресурса необходимо сделать безопасным. Используем конструкцию ВЫБОР языка выражений СКД:
ВЫБОР
КОГДА Сумма(КоличествоПродукции) = 0
ТОГДА 0
ИНАЧЕ Сумма(КоличествоМатериалов) / Сумма(КоличествоПродукции)
КОНЕЦ
Иногда требуется, чтобы в детальных строках выводилось одно значение (или оно вообще не выводилось), а в итогах — расчетное.
В колонке Рассчитывать по... на вкладке ресурсов можно указать конкретную группировку. Например:
КоличествоМатериалов. Выражение: Сумма(КоличествоМатериалов). Рассчитывать по: (пусто, для всех).СреднийРасходныйКоэффициент. Выражение: Сумма(КоличествоМатериалов) / Сумма(КоличествоПродукции). Рассчитывать по: ОбщийИтог, Организация.Однако в вашем случае формула Сумма(А)/Сумма(Б) универсальна и подходит для всех уровней, если вам нужно видеть коэффициент и в строках.
В обсуждении прозвучал очень полезный совет по отладке отчетов, когда кажется, что СКД игнорирует ваши формулы. Если вы меняете формулу в ресурсах, а результат в отчете не меняется (или выводится просто сумма), попробуйте следующее:
123. Сформируйте отчет.
123 — значит, ресурс подключен верно, ошибка в вашей формуле или данных.В исходном сообщении текст запроса был перегружен конструкциями ВЫРАЗИТЬ(... КАК ЧИСЛО(15,4)). Давайте разберем, нужно ли это.
Пользователь использовал:
ЕСТЬNULL(ВЫРАЗИТЬ(ПоискМатериаловПоОтчету.КоличествоУпаковокМатериалы КАК ЧИСЛО(15, 4)), 0)
Это избыточно. Если поле КоличествоУпаковокМатериалы в базе данных имеет тип Число, то ВЫРАЗИТЬ обычно не требуется, если только вы не хотите принудительно отбросить знаки после запятой до агрегации (что редко нужно).
Достаточно использовать:
ЕСТЬNULL(ПоискМатериаловПоОтчету.КоличествоУпаковокМатериалы, 0)
Функция ЕСТЬNULL обязательна, если вы используете левое соединение (LEFT JOIN), чтобы вместо NULL получить 0. Это критично для арифметических операций, так как NULL + 5 = NULL.
ВЫБОР КОГДА Сумма(Знаменатель) = 0 ТОГДА 0 ИНАЧЕ Сумма(Числитель) / Сумма(Знаменатель) КОНЕЦ
Используя этот подход, вы получите корректный средневзвешенный коэффициент на любом уровне группировки отчета.