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

Программист 1С v8.3 (Управляемые формы) Управленческий учет Промышленность, строительство и АПК
← На главную

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

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

Выясним причину некорректного суммирования

Проанализируем ситуацию на уровне SQL-запроса. Когда мы соединяем таблицу заказов с таблицей рецептур, СУБД формирует плоскую таблицу. Если для одной «Сборки» весом 3 кг в рецептуре указано два узла, в выборке появятся две строки, и в каждой из них в колонке КоличествоКг будет стоять цифра 3. СКД, не обладая встроенной «магией» распознавания смысла данных, просто суммирует все значения в колонке, которые попали в результат запроса. В итоге мы получаем 6 кг вместо положенных 3 кг.

Рассмотрим по шагам, как можно заставить систему считать итоги правильно.

Способ 1. Использование двух наборов данных (Master-Detail)

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

  1. Создадим первый набор данных (НаборДанныхЗаказы): в нем будет простой запрос, который выбирает только уникальные записи по номенклатуре и её общему весу из документов.
    
    ВЫБРАТЬ
        Документы.Номенклатура КАК Номенклатура,
        Документы.КоличествоКг КАК КоличествоКг,
        Документы.Ссылка КАК СсылкаНаЗаказ
    ИЗ
        Документ.Заказ КАК Документы
    
  2. Создадим второй набор данных (НаборДанныхРецептуры): здесь мы выберем данные о составе.
    
    ВЫБРАТЬ
        Рецептуры.Номенклатура КАК Номенклатура,
        Рецептуры.Комплектующее КАК Комплектующее,
        Рецептуры.Доля КАК ДоляСодержания
    ИЗ
        РегистрСведений.Рецептуры КАК Рецептуры
    
  3. Настроим связи наборов данных: перейдем на вкладку «Связи наборов данных» и свяжем их по полю Номенклатура. Источником связи будет «НаборДанныхЗаказы», приемником — «НаборДанныхРецептуры».

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

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

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

Перейдем на закладку «Ресурсы» и для поля КоличествоКг вместо стандартного Сумма(КоличествоКг) пропишем следующее выражение:

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

Разберем, как это работает:

Этот метод считается универсальным, так как он корректно работает даже при наличии многоуровневых группировок в отчете, где простые математические ухищрения (вроде деления на количество строк) обычно «ломаются».

Способ 3. Тонкая настройка расчета ресурсов по группировкам

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

Рассмотрим пример настройки на закладке «Ресурсы»:

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

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

Ошибочные методы, которых стоит избегать

Часто программисты пытаются решить проблему «в лоб» через математические формулы в запросе или ресурсах. Посмотрим на пример такого подхода:

Сумма(КоличествоКг) / Количество(КомплектующееНоменклатура)

Хотя на первый взгляд это кажется логичным (мы делим раздутую сумму на количество повторов), этот метод крайне нестабилен:

Резюме

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

← На главную