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

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

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

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

Проанализируем ситуацию на примере классического запроса. Когда мы пытаемся выбрать данные из документа ОтчетОРозничныхПродажах и присоединяем к нему ЧекККМ, чтобы определить, был ли возврат, СУБД создает временный набор записей, где для каждой строки из левой таблицы подбираются все подходящие строки из правой. Если в одном отчете о продажах зафиксировано 5 чеков, из которых один — возвратный, то вместо одной строки с общей суммой продаж мы получим 5 строк. При этом сумма самого документа будет суммироваться пять раз, что приведет к неверным итогам в отчете.

Посмотрим на исходный код запроса, который вызывает проблему:


ВЫБРАТЬ
    ОтчетОРозничныхПродажахОсновной.Ссылка,
    ОтчетОРозничныхПродажахОсновной.СуммаДокумента,
    ВЫБОР
        КОГДА ЧекККМ.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийЧекККМ.Возврат)
            ТОГДА 1
        ИНАЧЕ 0
    КОНЕЦ КАК ЭтоВозврат
ИЗ
    Документ.ОтчетОРозничныхПродажах КАК ОтчетОРозничныхПродажахОсновной
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЧекККМ КАК ЧекККМ
    ПО ОтчетОРозничныхПродажахОсновной.Ссылка = ЧекККМ.ОтчетОРозничныхПродажах

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

Решение №1: Использование вложенного запроса с группировкой

Самый простой способ «схлопнуть» данные по чекам до того, как они соединятся с основной таблицей — использовать вложенный запрос. Мы заранее сгруппируем чеки по полю ОтчетОРозничныхПродажах и посчитаем количество возвратов для каждого отчета.

Рассмотрим обновленный текст запроса:


ВЫБРАТЬ
    ОтчетОРозничныхПродажахОсновной.Ссылка КАК Ссылка,
    ОтчетОРозничныхПродажахОсновной.СуммаДокумента КАК СуммаДокумента,
    ВложенныйЗапросВозвраты.КоличествоВозвратов КАК КоличествоВозвратов
ИЗ
    Документ.ОтчетОРозничныхПродажах КАК ОтчетОРозничныхПродажахОсновной
    ЛЕВОЕ СОЕДИНЕНИЕ (
        ВЫБРАТЬ
            ЧекККМ.ОтчетОРозничныхПродажах КАК ОтчетОРозничныхПродажах,
            СУММА(ВЫБОР
                    КОГДА ЧекККМ.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийЧекККМ.Возврат)
                        ТОГДА 1
                    ИНАЧЕ 0
                КОНЕЦ) КАК КоличествоВозвратов
        ИЗ
            Документ.ЧекККМ КАК ЧекККМ
        СГРУППИРОВАТЬ ПО
            ЧекККМ.ОтчетОРозничныхПродажах
    ) КАК ВложенныйЗапросВозвраты
    ПО ОтчетОРозничныхПродажахОсновной.Ссылка = ВложенныйЗапросВозвраты.ОтчетОРозничныхПродажах

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

Решение №2: Использование временных таблиц (Best Practice)

Для повышения производительности, особенно на больших объемах данных, рекомендуется использовать временные таблицы. Это позволяет платформе 1С и СУБД более эффективно строить план запроса.

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

  1. Создаем временную таблицу ВТ_Возвраты, в которую выбираем только ссылки на отчеты о продажах и количество чеков с видом операции «Возврат».
  2. Соединяем основную таблицу с этой временной таблицей.

ВЫБРАТЬ
    ЧекККМ.ОтчетОРозничныхПродажах КАК ОтчетСсылка,
    КОЛИЧЕСТВО(ЧекККМ.Ссылка) КАК КолВозвратов
ПОМЕСТИТЬ ВТ_Возвраты
ИЗ
    Документ.ЧекККМ КАК ЧекККМ
ГДЕ
    ЧекККМ.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийЧекККМ.Возврат)
СГРУППИРОВАТЬ ПО
    ЧекККМ.ОтчетОРозничныхПродажах
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Продажи.Ссылка,
    Продажи.Магазин,
    Продажи.СуммаДокумента,
    ЕСТЬNULL(ВТ_Возвраты.КолВозвратов, 0) КАК КоличествоВозвратов
ИЗ
    Документ.ОтчетОРозничныхПродажах КАК Продажи
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Возвраты КАК ВТ_Возвраты
    ПО Продажи.Ссылка = ВТ_Возвраты.ОтчетСсылка

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

Решение №3: Разделение на наборы данных в СКД

Система компоновки данных позволяет использовать несколько наборов данных (DataSets). Мы можем создать два независимых запроса:

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

Важные нюансы работы с типами данных и NULL

В процессе обсуждения часто возникает вопрос: как правильно проверять поля на заполненность при соединении. Заметим, что проверка Пользователи.ФизическоеЛицо.Ссылка ЕСТЬ NULL работает корректно при ЛЕВОМ СОЕДИНЕНИИ, если запись в таблице пользователей не найдена. Однако, если запись найдена, но само поле ФизическоеЛицо не заполнено, результатом будет Пустая Ссылка, а не NULL.

Для универсальности рекомендуется использовать следующий подход в ВЫБОР:


ВЫБОР
    КОГДА Отчет.Ответственный.ФизическоеЛицо = ЗНАЧЕНИЕ(Справочник.ФизическиеЛица.ПустаяСсылка)
        ТОГДА Отчет.Ответственный
    ИНАЧЕ Отчет.Ответственный.ФизическоеЛицо
КОНЕЦ

Также обратим внимание, что в СКД нет необходимости явно соединять таблицу со справочником Пользователи, если нужно просто вытянуть ФизическоеЛицо. Использование «обращения через точку» (Ответственный.ФизическоеЛицо) автоматически заставит платформу выполнить неявное левое соединение, что сделает запрос короче и читабельнее.

Настройка ресурсов для подсчета

После того как мы исправили запрос одним из вышеперечисленных методов, перейдем к настройке вкладки «Ресурсы». Для поля КоличествоВозвратов необходимо указать выражение:

Сумма(КоличествоВозвратов)

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

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

← На главную