Как реализовать разбивку продаж по году, полугодию и кварталу в запросе 1С

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

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

Метод 1: Использование конструкции ВЫБОР КОГДА в едином запросе

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

Проанализируем пример кода, где продажи разбиваются на год, два полугодия и четыре квартала:


ВЫБРАТЬ
    ПродажиОбороты.Номенклатура КАК Номенклатура,
    ПродажиОбороты.Характеристика КАК Характеристика,
    СУММА(ПродажиОбороты.КоличествоОборот) КАК ОбщиеПродажи,
    // Расчет среднемесячных продаж за год
    СУММА(ВЫБОР
            КОГДА ПродажиОбороты.Период МЕЖДУ &НачалоПериода И &КонецПериода
                ТОГДА ПродажиОбороты.КоличествоОборот / 12
            ИНАЧЕ 0
        КОНЕЦ) КАК СреднееЗаГод,
    // Расчет за 1-е полугодие
    СУММА(ВЫБОР
            КОГДА ПродажиОбороты.Период МЕЖДУ &НачалоПериода И ДОБАВИТЬКДАТЕ(&НачалоПериода, МЕСЯЦ, 6)
                ТОГДА ПродажиОбороты.КоличествоОборот / 6
            ИНАЧЕ 0
        КОНЕЦ) КАК Полугодие1,
    // Расчет за 1-й квартал
    СУММА(ВЫБОР
            КОГДА ПродажиОбороты.Период МЕЖДУ &НачалоПериода И ДОБАВИТЬКДАТЕ(&НачалоПериода, МЕСЯЦ, 3)
                ТОГДА ПродажиОбороты.КоличествоОборот / 3
            ИНАЧЕ 0
        КОНЕЦ) КАК Квартал1
ИЗ
    РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, День, ) КАК ПродажиОбороты
СГРУППИРОВАТЬ ПО
    ПродажиОбороты.Номенклатура,
    ПродажиОбороты.Характеристика

Важный нюанс: при использовании такого подхода в виртуальной таблице Обороты необходимо указывать периодичность День, Запись или Авто. Если указать периодичность Период, система не сможет корректно проверить условие МЕЖДУ для внутренних интервалов, если они меньше общего периода отчета.

Метод 2: Временные таблицы с разной периодичностью

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

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

  1. Создаем временную таблицу для квартальных продаж.
  2. Создаем временную таблицу для годовых продаж.
  3. Объединяем полученные таблицы с таблицей остатков.

Посмотрим на пример реализации первого шага:


ВЫБРАТЬ
    ПродажиКвартал.Номенклатура КАК Номенклатура,
    ПродажиКвартал.Характеристика КАК Характеристика,
    СРЕДНЕЕ(ПродажиКвартал.КоличествоОборот) КАК СреднееКвартал
ПОМЕСТИТЬ ВТ_ПродажиКвартал
ИЗ
    РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, Квартал, ) КАК ПродажиКвартал
СГРУППИРОВАТЬ ПО
    ПродажиКвартал.Номенклатура,
    ПродажиКвартал.Характеристика

Использование встроенной функции СРЕДНЕЕ в данном контексте само рассчитает среднее значение оборотов по всем кварталам, которые попали в выборку. Если в периоде было 4 квартала, сумма продаж будет разделена на 4.

Проблема объединения данных и потери позиций

Выясним важную причину, по которой данные могут отображаться некорректно. Если мы используем ЛЕВОЕ СОЕДИНЕНИЕ от таблицы продаж к таблице остатков, то товары, которые не продавались в выбранном периоде, но физически лежат на складе, не попадут в отчет. Чтобы этого избежать, мы должны использовать "эталонную" таблицу номенклатуры или начинать соединение от таблицы остатков.

Проанализируем правильную структуру итогового соединения:


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

Совет: Всегда используйте функцию ЕСТЬNULL при соединении с таблицами оборотов, чтобы вместо пустых значений (NULL) в отчете красовались аккуратные нули.

Особенности расчета среднего: делить на 12 или использовать СРЕДНЕЕ?

Рассмотрим ситуацию, которую часто обсуждают на форумах: как правильно считать среднее? Если руководство требует "среднемесячные продажи за год", то мы делим общую сумму за год на 12. Однако, что если отчет формируется за 2,5 года? В этом случае жесткое деление на 12 даст неверный результат.

Для универсальности рекомендуется использовать функцию РАЗНОСТЬДАТ. Это позволит динамически определять количество месяцев в периоде, который выбрал пользователь:

СУММА(КоличествоОборот) / РАЗНОСТЬДАТ(&НачалоПериода, &КонецПериода, МЕСЯЦ)

Настройка отчета в Системе компоновки данных (СКД)

Если вы разрабатываете отчет на СКД, многие из вышеописанных проблем решаются настройками самого отчета без написания сложного SQL-кода. Рассмотрим основные возможности:

  1. Роли полей периодов: В наборе данных запроса укажите для поля Период соответствующую роль. Это позволит пользователю в настройках отчета использовать группировку по периодам (день, неделя, месяц) простым добавлением поля.
  2. Связи наборов данных: Вместо объединения остатков и оборотов в одном запросе, создайте два отдельных набора данных. На вкладке "Связи наборов данных" свяжите их по номенклатуре. СКД сама выполнит оптимальное левое соединение.
  3. Вычисляемые поля: Вы можете вынести расчет средних значений в вычисляемые поля СКД, используя ресурсы. Например, ресурс Среднее(КоличествоОборот) с нужной группировкой по периоду в настройках даст правильный результат без временных таблиц.

В конфигурациях типа Управление нашей фирмой (УНФ) или Управление торговлей, где активно используются характеристики, не забывайте всегда включать Характеристику в условия соединения, иначе данные "схлопнутся" и вы получите завышенные показатели продаж по разным цветам или размерам одного и того же товара.

Подводя итог, выбор метода зависит от ваших целей. Если нужен быстрый и простой отчет с жестко заданными колонками — используйте ВЫБОР КОГДА. Если же требуется гибкость и высокая точность при работе с большими объемами данных — используйте временные таблицы с разной периодичностью и механизмы СКД.

← На главную