При разработке отчетов по продажам в системе компоновки данных (СКД) часто возникает задача: получить данные о реализации товаров, но при этом корректно учесть возвраты от покупателей. Особенно важно сохранить аналитику, например, привязать возврат к конкретному менеджеру, который совершил первоначальную продажу. В этой статье мы подробно разберем, как построить такой запрос, используя временные таблицы и механизмы объединения.
Рассмотрим ситуацию: у нас есть документ РеализацияТоваровУслуг и документ ВозвратТоваровОтПокупателя. В реализации указан менеджер. В возврате менеджер может не указываться или быть другим (тем, кто принял возврат). Наша цель — увидеть «чистые» продажи по менеджеру, который продал товар. Если мы просто соединим таблицы левым соединением, мы рискуем получить дубли строк или некорректные суммы, если возвратов по одной реализации было несколько. Поэтому проанализируем наиболее эффективный подход — использование временных таблиц и объединения.
Первым делом нам необходимо выбрать все данные по продажам за нужный период. Мы поместим их во временную таблицу ВТ_ДанныеРеализаций. На этом этапе мы сразу фильтруем данные по периоду и исключаем пустые ссылки на менеджеров, чтобы отчет был чистым.
Разберем пример кода для первой временной таблицы:
ВЫБРАТЬ
РеализацияТоваровУслугТовары.Ссылка КАК Ссылка,
РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
РеализацияТоваровУслугТовары.Количество КАК Количество,
РеализацияТоваровУслугТовары.Сумма КАК Сумма,
РеализацияТоваровУслугТовары.Менеджер КАК Менеджер
ПОМЕСТИТЬ ВТ_ДанныеРеализаций
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
ГДЕ
РеализацияТоваровУслугТовары.Ссылка.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
И РеализацияТоваровУслугТовары.Менеджер <> ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка)
;
Важный момент: Обратите внимание, что мы обращаемся к табличной части документа. В реальных задачах на больших базах лучше использовать регистры накопления (например, Продажи), но для понимания логики работы с документами этот пример идеален.
Теперь нам нужно получить данные о возвратах. Главная сложность здесь — «достать» менеджера из реализации, на основании которой сделан возврат. Для этого мы используем поле Сделка (или ДокументОснование в зависимости от конфигурации) в документе возврата.
Выполним левое соединение возвратов с реализациями, чтобы подтянуть аналитику менеджера:
ВЫБРАТЬ
ВозвратТоваровОтПокупателяТовары.Ссылка КАК Ссылка,
ВозвратТоваровОтПокупателяТовары.Номенклатура КАК Номенклатура,
-1 * ВозвратТоваровОтПокупателяТовары.Количество КАК Количество,
-1 * ВозвратТоваровОтПокупателяТовары.Сумма КАК Сумма,
ЕСТЬNULL(РеализацияТоваровУслугТовары.Менеджер, ВозвратТоваровОтПокупателяТовары.Ссылка.Сделка.Менеджер) КАК Менеджер
ПОМЕСТИТЬ ВТ_ДанныеВозвратов
ИЗ
Документ.ВозвратТоваровОтПокупателя.Товары КАК ВозвратТоваровОтПокупателяТовары
ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
ПО ВозвратТоваровОтПокупателяТовары.Ссылка.Сделка = РеализацияТоваровУслугТовары.Ссылка
И ВозвратТоваровОтПокупателяТовары.Номенклатура = РеализацияТоваровУслугТовары.Номенклатура
;
Проанализируем этот участок кода. Мы умножаем Количество и Сумму на -1. Это критически важно для последующего объединения: когда мы сложим 10 проданных товаров и -1 возвращенный, мы получим правильный итог — 9 единиц.
Теперь, когда у нас есть две подготовленные таблицы с одинаковой структурой, мы можем их объединить. Использование ОБЪЕДИНИТЬ ВСЕ позволяет собрать все строки в один массив данных для последующей группировки.
ВЫБРАТЬ
ВТ_ДанныеРеализаций.Номенклатура,
ВТ_ДанныеРеализаций.Количество,
ВТ_ДанныеРеализаций.Сумма,
ВТ_ДанныеРеализаций.Менеджер
ПОМЕСТИТЬ ВТ_ИтоговыеДанные
ИЗ
ВТ_ДанныеРеализаций КАК ВТ_ДанныеРеализаций
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ВТ_ДанныеВозвратов.Номенклатура,
ВТ_ДанныеВозвратов.Количество,
ВТ_ДанныеВозвратов.Сумма,
ВТ_ДанныеВозвратов.Менеджер
ИЗ
ВТ_ДанныеВозвратов КАК ВТ_ДанныеВозвратов
;
Совет: Если в итоговом отчете вам нужно видеть и документ реализации, и документ возврата, добавьте поле Ссылка в выборку. Если же цель — просто получить общие цифры по товарам, ссылку можно опустить.
На последнем этапе мы выбираем данные из нашей итоговой временной таблицы и производим агрегацию (суммирование). Именно здесь «минусовые» возвраты вычтутся из «плюсовых» реализаций.
ВЫБРАТЬ
ВТ_ИтоговыеДанные.Менеджер КАК Менеджер,
ВТ_ИтоговыеДанные.Номенклатура КАК Номенклатура,
СУММА(ВТ_ИтоговыеДанные.Количество) КАК КоличествоИтог,
СУММА(ВТ_ИтоговыеДанные.Сумма) КАК СуммаИтог
ИЗ
ВТ_ИтоговыеДанные КАК ВТ_ИтоговыеДанные
ГРУППИРОВАТЬ ПО
ВТ_ИтоговыеДанные.Менеджер,
ВТ_ИтоговыеДанные.Номенклатура
ХАВИНГ
СУММА(ВТ_ИтоговыеДанные.Количество) <> 0
Использование конструкции ХАВИНГ (ИМЕЮЩИЕ) позволяет нам исключить из отчета те товары, которые были возвращены полностью. Если сумма количеств равна нулю, значит, товар был продан и полностью возвращен — такие строки зачастую только загромождают отчет.
Рассмотрим подробнее преимущества описанного метода:
UNION ALL лишено этой проблемы.Менеджера и Номенклатуру в настройки группировок, а КоличествоИтог и СуммаИтог назначить ресурсами.Если вы работаете в крупной компании с большим объемом документов, выполнение запросов напрямую к табличным частям Документ.РеализацияТоваровУслуг.Товары может быть медленным. Профессиональным стандартом считается использование виртуальных таблиц оборотов регистров накопления. Например, РегистрНакопления.Продажи.Обороты уже содержит данные и о продажах, и о возвратах (обычно возвраты там записываются с отрицательным знаком). Если ваша конфигурация позволяет, всегда отдавайте предпочтение регистрам.
Однако, если задача стоит именно в анализе документов (например, нужно проверить заполнение конкретных полей ТЧ), то предложенная выше схема с временными таблицами является наиболее надежной и понятной для реализации.