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

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

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

Причина возникновения проблемы

Прежде чем переходить к решениям, разберем механику процесса. Когда мы используем ЛЕВОЕ СОЕДИНЕНИЕ в тексте запроса между данными заказа и его товарным составом, СУБД формирует плоскую таблицу. Для каждой строки номенклатуры значения из полей заказа будут повторяться. СКД, видя поле СуммаОплаты как ресурс с функцией Сумма, честно складывает все значения, которые видит в итоговой выборке. Наша задача — объяснить системе компоновки, что сумму нужно учитывать только один раз для каждого уникального ключа (заказа или связки заказа и формы оплаты).

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

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

Проанализируем ситуацию: если мы хотим видеть корректную сумму оплаты по партнеру, нам нужно просуммировать оплаты по всем его заказам, но внутри каждого заказа взять сумму только один раз. Для этого в настройках ресурсов (вкладка «Ресурсы») для поля СуммаОплаты вместо стандартного Сумма(СуммаОплаты) мы прописываем специальное выражение.

Посмотрим на пример выражения:


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

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

  1. ВычислитьВыражениеСГруппировкойМассив — эта функция обходит записи в контексте указанной группировки и складывает результаты выражения в массив.
  2. "Максимум(СуммаОплаты)" — это первый параметр. Внутри каждой группы (каждого заказа) мы берем максимальное (или среднее, так как они одинаковы) значение суммы оплаты. Это гарантирует, что из пяти строк одного заказа мы получим только одно число.
  3. "ЗаказКлиента, ФормаОплаты" — второй параметр. Это «ключ» уникальности. Если один заказ может быть оплачен разными способами (например, частично наличными и частично картой), нам важно группировать именно по этой связке, чтобы не потерять часть данных.
  4. Сумма(...) — внешняя функция, которая складывает все полученные из массива уникальные значения для итогов более высокого уровня (Партнер или Общий итог).

Способ 2: Настройка рассчитываемых ресурсов по группировкам

Если вы не хотите использовать сложные формулы, можно воспользоваться механизмом разделения расчета ресурсов по уровням группировок. Рассмотрим подробнее, как настроить вкладку «Ресурсы» в схеме компоновки данных:

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

Способ 3: Использование двух наборов данных (Связи наборов данных)

Проанализируем архитектурно правильный подход, который избавляет от необходимости бороться с дублями в формулах. Вместо одного запроса с соединением мы создаем два независимых набора данных Query (Запрос):

НаборДанныхЗаказы: Выбирает только шапку заказа и сумму оплаты.


ВЫБРАТЬ
    ЗаказКлиента.Ссылка КАК ЗаказКлиента,
    ЗаказКлиента.Партнер КАК Партнер,
    ЗаказКлиента.СуммаОплаты КАК СуммаОплаты,
    ЗаказКлиента.ФормаОплаты КАК ФормаОплаты
ИЗ
    Документ.ЗаказКлиента КАК ЗаказКлиента

НаборДанныхТовары: Выбирает только табличную часть заказа.


ВЫБРАТЬ
    ЗаказКлиента.Ссылка КАК ЗаказКлиента,
    Номенклатура,
    Количество,
    Сумма
ИЗ
    Документ.ЗаказКлиента.Товары КАК Товары

Теперь переходим на вкладку «Связи наборов данных» и связываем их по полю ЗаказКлиента (Источник — Заказы, Приемник — Товары). В этом случае СКД работает по принципу компоновки объектов: она знает, что СуммаОплаты — это атрибут набора «Заказы», и при выводе номенклатуры она не будет «размножать» это значение в памяти для итоговых расчетов. Сумма по партнеру будет рассчитана корректно простым агрегатом Сумма(СуммаОплаты).

Способ 4: Агрегатная функция Сумма(Различные ...)

В некоторых случаях, если данные позволяют, можно использовать ключевое слово Различные прямо в поле ресурса. Однако будьте осторожны: если у двух разных заказов одного партнера суммы оплат случайно совпадут (например, оба на 5000 рублей), то Сумма(Различные СуммаОплаты) посчитает их как одну сумму 5000, что приведет к ошибке.

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


ВЫБРАТЬ
    ЗаказКлиента.Ссылка КАК Заказ,
    ЗаказКлиента.СуммаОплаты КАК СуммаОплаты,
    ВЫРАЗИТЬ(ЗаказКлиента.Ссылка КАК СТРОКА(100)) + "_" + ВЫРАЗИТЬ(ЗаказКлиента.ФормаОплаты КАК СТРОКА(50)) КАК УникальныйКлючОплаты

Но этот метод считается избыточным («костыльным») по сравнению с использованием ВычислитьВыражениеСГруппировкойМассив.

Нюансы работы с частичными оплатами

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

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

Резюме

Для решения проблемы «размножения» сумм при детализации до номенклатуры мы рекомендуем:

  1. Использовать Связи наборов данных, если отчет строится с нуля — это наиболее производительный и логичный путь.
  2. Использовать ВычислитьВыражениеСГруппировкойМассив, если отчет строится на одном запросе и нужно быстро исправить расчет итогов без переписывания структуры.
  3. Всегда проверять уникальность полей в группировке функции, чтобы не потерять разные платежи в рамках одного документа.
← На главную