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