При разработке отчетов на Системе компоновки данных (СКД) программисты часто сталкиваются с проблемой «размножения» итогов. Типичная ситуация: мы хотим увидеть реализацию товаров в разрезе позиций (номенклатуры) и одновременно вывести сумму оплаты по этой реализации. Поскольку в одной реализации может быть десять строк товаров, а документ оплаты один, при обычном левом соединении сумма оплаты продублируется десять раз. В итоге общая сумма оплат в отчете окажется завышенной в десятки раз.
В этой статье мы подробно разберем, как бороться с этой проблемой, проанализируем различные подходы — от настройки связей наборов данных до использования сложных выражений СКД.
Прежде чем переходить к решению, выясним причину ошибки. Когда мы связываем таблицу Продажи.Состав с таблицей Оплаты по полю ДокументОснование, СУБД создает строку для каждой комбинации «Товар — Оплата». Если в реализации 5 товаров и 2 платежных поручения, в выборке получится 10 строк. Сумма каждого платежа повторится 5 раз (по количеству товаров). Стандартный ресурс Сумма(СуммаОплаты) просто сложит всё, что видит в выборке, и выдаст неверный результат.
Наиболее правильным и архитектурно верным способом решения задачи в СКД является разделение запроса на два независимых набора данных. Этот метод позволяет системе самостоятельно управлять агрегацией данных на разных уровнях группировки.
Рассмотрим этот метод по шагам:
НаборДанных1 как источник связи, а НаборДанных2 как приемник. Условие связи — поле Ссылка (из реализации) и ДокументОснование (из оплат).Почему это работает? При расчете ресурсов СКД видит, что данные по оплате относятся к уровню документа в целом, а не к каждой строке товара. Система применит функцию Сумма к данным из второго набора только один раз для каждой группы первого набора, что исключит дублирование.
Если по каким-то причинам вы не хотите разделять запрос на разные наборы данных, можно воспользоваться мощным механизмом вычисляемых выражений в ресурсах. Мы можем заставить СКД сначала собрать уникальные значения оплат в массив по конкретному документу, а уже потом просуммировать их.
Проанализируем ситуацию: нам нужно просуммировать оплаты, но только один раз для каждого документа оплаты. В поле «Выражение» для ресурса СуммаОплаты следует прописать следующую конструкцию:
Сумма(ВычислитьВыражениеСГруппировкойМассив("Максимум(СуммаОплаты)", "ДокументОплаты"))
Разберем, что здесь происходит:
"Максимум(СуммаОплаты)" — мы берем сумму конкретного платежа (так как она одинакова во всех задублированных строках, Максимум или Среднее вернет само значение)."ДокументОплаты" — это поле группировки. Система «схлопнет» дубликаты по этому идентификатору.ВычислитьВыражениеСГруппировкойМассив — создает массив из этих уникальных значений.Сумма(...) — складывает элементы полученного массива для итогов более высокого уровня.Еще один профессиональный прием — использование оператора ОБЪЕДИНИТЬ ВСЕ в одном запросе. Вместо того чтобы «растягивать» таблицу оплат вширь (присоединяя её к товарам), мы добавляем её данные вниз.
Рассмотрим структуру такого запроса:
// Часть 1: Данные по товарам
ВЫБРАТЬ
Реализация.Ссылка КАК Документ,
РеализацияТовары.Номенклатура КАК Номенклатура,
РеализацияТовары.Сумма КАК СуммаПродажи,
0 КАК СуммаОплаты
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТовары
ГДЕ ...
ОБЪЕДИНИТЬ ВСЕ
// Часть 2: Данные по оплатам
ВЫБРАТЬ
Оплата.ДокументОснование КАК Документ,
ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка) КАК Номенклатура,
0 КАК СуммаПродажи,
Оплата.Сумма КАК СуммаОплаты
ИЗ
Документ.ПриходныйКассовыйОрдер КАК Оплата
ГДЕ ...
В СКД при такой структуре данные выведутся красиво: в строках с номенклатурой будет заполнена СуммаПродажи, а в строке самого документа (или в отдельной пустой строке номенклатуры) — СуммаОплаты. Общие итоги по вертикали сойдутся идеально, так как физически в таблице нет ни одной записи, где бы эти суммы перемножались.
Если вы работаете в конфигурации «Управление торговлей 11.4» (или ERP/КА), получение данных напрямую из табличных частей документов считается признаком неоптимальной разработки. Программа построена на регистрах накопления, в которых данные уже подготовлены для отчетов.
Для получения корректных итогов рекомендуется:
Обороты регистра накопления ВыручкаИСебестоимостьПродаж.РасчетыСКлиентамиПоДокументам.Работа с регистрами не только ускоряет отчет, но и избавляет от необходимости вручную обрабатывать связи документов, так как аналитика (какой заказ или реализация оплачены) уже заложена в движениях по регистрам.
В процессе разработки вы можете столкнуться с технической ошибкой: «Для объекта метаданных Отчет.ОтчетОТоварах не найден идентификатор...». Это часто происходит в современных конфигурациях на базе БСП, когда новый отчет еще не зарегистрирован в системе.
Для устранения этой проблемы попробуйте выполнить одно из действий:
/С ЗапуститьОбновлениеИнформационнойБазы.Подведем итог: Самым надежным способом избежать дублей является использование связей наборов данных. Если же вы ограничены одним запросом — используйте объединение таблиц или функцию ВычислитьВыражениеСГруппировкойМассив в ресурсах. Помните, что правильная архитектура данных в запросе — залог корректных итогов в отчете.