При разработке отчетов на базе Системы компоновки данных (СКД) программисты часто сталкиваются с ситуацией, когда числовые показатели (ресурсы) начинают принимать пугающие значения: суммы увеличиваются вдвое, втрое, а итоги по группировкам не поддаются логике. Разберем детально, почему это происходит и как правильно спроектировать запрос, чтобы отчет выводил верные данные.
Рассмотрим ситуацию, описанную в обращении. Автор использует виртуальную таблицу ВзаиморасчетыСКонтрагентамиПоДокументамРасчетов.ОстаткиИОбороты и присоединяет к ней несколько регистров сведений, таких как ПараметрыПродажАгентов, КонтактнаяИнформация и другие. Основная проблема — задвоение сумм — возникает в момент выполнения ЛЕВОЕ СОЕДИНЕНИЕ.
Проанализируем механизм возникновения ошибки. Если в основной таблице (регистре накопления) есть одна запись по документу, а в присоединяемой таблице (регистре сведений) по условию связи находится две и более записи, то в результирующей выборке строка основной таблицы продублируется столько раз, сколько соответствий найдено в правой таблице. Поскольку СКД рассчитывает ресурсы как СУММА(Поле), все дублированные значения суммируются, что приводит к некорректному итогу.
Посмотрим на фрагмент исходного запроса, вызывающий подозрение:
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ПараметрыПродажАгентов КАК ПараметрыПродажАгентов
ПО ВзаиморасчетыСКонтрагентамиПоДокументамРасчетовОстаткиИОбороты.ДокументРасчетовСКонтрагентом.ТорговыйПредставитель = ПараметрыПродажАгентов.Агент
И ВзаиморасчетыСКонтрагентамиПоДокументамРасчетовОстаткиИОбороты.Период <= ПараметрыПродажАгентов.Период
В данном случае условие <= по периоду практически гарантирует, что на одну дату документа из регистра накопления система найдет множество записей в регистре сведений за все последующие даты. Это классическая причина «раздувания» выборки.
Для регистров сведений, хранящих периодические настройки (цены, ответственные, параметры), необходимо получать только одну актуальную запись на конкретный момент времени. Использование простого соединения с физической таблицей регистра — ошибка. Выясним, как сделать это правильно.
Вместо соединения с РегистрСведений.ПараметрыПродажАгентов следует использовать виртуальную таблицу СрезПоследних. Если же требуется получить срез на разные даты для каждой строки (например, на дату документа), то срез последних в самом запросе (в секции ИЗ) может не подойти. В таких случаях мы применяем подзапрос или коррелированное соединение, но самым эффективным методом в СКД является вынос получения данных в отдельный набор данных и использование Связей наборов данных.
Если же мы остаемся в рамках одного запроса, попробуем ограничить выборку:
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ПараметрыПродажАгентов.СрезПоследних(&КонецПериода, ) КАК ПараметрыПродажАгентов
ПО ВзаиморасчетыСКонтрагентамиПоДокументамРасчетовОстаткиИОбороты.ДокументРасчетовСКонтрагентом.ТорговыйПредставитель = ПараметрыПродажАгентов.Агент
Как подтвердил автор темы, использование среза последних помогло устранить дублирование строк и привело суммы в порядок.
Проанализируем поведение виртуальной таблицы ОстаткиИОбороты. У нее есть важный параметр — Периодичность. Рассмотрим два варианта:
Если при смене периодичности с «Регистратор» на «Период» суммы остатков выровнялись, но «поломались» обороты (приход/расход), это сигнализирует о том, что в ресурсах СКД неправильно настроены Роли полей. Выясним, как это исправить.
В СКД недостаточно просто поставить галочку в колонке «Ресурс». Для полей остатков необходимо на закладке «Наборы данных» в колонке Роль указать специфические настройки:
СуммаНачальныйОстаток: Тип — Начальный остаток, Имя группы — «Сумма», Поле — «СуммаКонечныйОстаток» (взаимосвязь).СуммаКонечныйОстаток: Тип — Конечный остаток, Имя группы — «Сумма», Поле — «СуммаНачальныйОстаток».Если роли настроены верно, СКД перестанет просто суммировать остатки по иерархии группировок. Для группировки «Контрагент» система возьмет остаток на начало самого раннего периода внутри этой группы и остаток на конец самого позднего, игнорируя промежуточные суммы, которые могли бы «наложиться» друг на друга при обычном суммировании.
Особый случай — поле СуммаДокумента. Если документ попал в несколько строк отчета (например, из-за разных номенклатурных позиций или аналитик), его общая сумма будет суммироваться многократно. Чтобы в итогах по документу или контрагенту сумма документа отображалась корректно (без учета дублей), можно использовать следующее выражение в ресурсах:
Сумма(ВычислитьВыражениеСГруппировкойМассив("Максимум(СуммаДокумента)", "ДокументРасчетовСКонтрагентом"))
Это выражение сначала определит максимальную (единственную) сумму для каждого уникального документа, а затем сложит эти значения в итогах вышестоящей группировки.
Часто обороты считаются неверно, если не учитывать границы времени. При передаче параметров в виртуальную таблицу убедимся, что &КонецПериода включает последнюю секунду дня. В коде 1С это решается использованием объекта Граница, а внутри СКД — стандартной функцией КОНЕЦПЕРИОДА(&КонецПериода, "ДЕНЬ").
Проанализируем типичный пример установки параметров:
// В настройках СКД для параметров лучше использовать:
&КонецПериода = КонецПериода(&КонецПериода, "ДЕНЬ")
Подведем итог нашему исследованию. Если ваш отчет «врет» в цифрах, выполните следующие шаги:
ЛЕВОЕ СОЕДИНЕНИЕ.Соблюдение этих правил позволит вам создавать надежные и точные отчеты, которые будут корректно отображать как текущие остатки, так и обороты за произвольный период.