Как построить план-фактный отчет в 1С, учитывающий пропущенные плановые показатели?

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

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

Проблема первоначального подхода

Изначально для построения план-фактного отчета может показаться логичным просто объединить данные из регистра накопления планов и регистра накопления продаж, как это показано в первом сообщении:


ВЫБРАТЬ
    ПланПродажОбороты.Период КАК Период,
    ПланПродажОбороты.Сценарий КАК Сценарий,
    ПланПродажОбороты.Подразделение КАК Подразделение,
    ПланПродажОбороты.СуммаОборот КАК План,
    0 КАК Продажи
ИЗ
    РегистрНакопления.ПланПродаж.Обороты(
        &НачалоПериода,
        &КонецПериода,
        Неделя,
        Подразделение = &Подразделение
    ) КАК ПланПродажОбороты

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ПродажиОбороты.Период,
    NULL,
    ПродажиОбороты.Подразделение,
    0,
    ПродажиОбороты.СуммаОборот
ИЗ
    РегистрНакопления.Продажи.Обороты(
        &НачалоПериода,
        &КонецПериода,
        День,
        Подразделение = &Подразделение
    ) КАК ПродажиОбороты
УПОРЯДОЧИТЬ ПО Период

Однако такой запрос лишь выводит строки плана и факта, не решая проблему "наследования" плана. Если за какую-то неделю план не был введен, то в отчете для этой недели не будет планового показателя, хотя требуется, чтобы был показан план из предыдущей недели. Нам необходимо получить "срез последних" плановых данных на каждую отчетную дату или неделю.

Решение 1: Построение запроса с использованием временных таблиц и логики "среза последних"

Основной подход к решению этой задачи в запросе 1С заключается в имитации механизма "среза последних" для регистра накопления на каждую дату или период в заданном интервале. Виртуальная таблица СрезПоследних регистра накопления позволяет получить актуальные данные на *одну* конкретную дату. Когда же нам нужен "срез последних" для *каждой* даты или начала недели в заданном диапазоне, мы должны использовать пакетные запросы с временными таблицами. Разберем этот процесс по шагам.

Шаг 1: Формирование временной таблицы с периодами отчета

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


// Временная таблица 1: Все уникальные комбинации "Начало недели" и "Подразделение"
// за отчетный период, по которым может быть план или факт
ВЫБРАТЬ РАЗЛИЧНЫЕ
    НАЧАЛОПЕРИОДА(ПланПродажОбороты.Период, НЕДЕЛЯ) КАК ПериодОтчета,
    ПланПродажОбороты.Подразделение
ПОМЕСТИТЬ ВТ_ПериодыОтчета
ИЗ
    РегистрНакопления.ПланПродаж.Обороты(
        &НачалоПериода,
        &КонецПериода,
        Неделя,
        Подразделение = &Подразделение
    ) КАК ПланПродажОбороты

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ РАЗЛИЧНЫЕ
    НАЧАЛОПЕРИОДА(ПродажиОбороты.Период, НЕДЕЛЯ),
    ПродажиОбороты.Подразделение
ИЗ
    РегистрНакопления.Продажи.Обороты(
        &НачалоПериода,
        &КонецПериода,
        Неделя,
        Подразделение = &Подразделение
    ) КАК ПродажиОбороты;

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

Шаг 2: Получение всех планов за период (включая предшествующие отчетному)

Нам нужны все записи планов, которые могли быть установлены до или внутри нашего отчетного периода. Это позволит найти актуальный план, даже если он был задан задолго до начала текущего отчетного интервала.


// Временная таблица 2: Все планы продаж до конца отчетного периода
ВЫБРАТЬ
    ПланПродаж.Период КАК ПериодПлана,
    ПланПродаж.Подразделение КАК Подразделение,
    ПланПродаж.Сумма КАК План
ПОМЕСТИТЬ ВТ_ПланыИсходные
ИЗ
    РегистрНакопления.ПланПродаж КАК ПланПродаж
ГДЕ
    ПланПродаж.Период <= &КонецПериода // Захватываем планы, действовавшие до или в конце периода
    И ПланПродаж.Сценарий = &Сценарий // Если используется сценарий планирования
;

Важно использовать условие ПланПродаж.Период <= &КонецПериода, чтобы захватить все планы, которые могли быть актуальны в течение отчетного периода, даже если их период установки был до &НачалоПериода.

Шаг 3: Определение актуального периода плана для каждой недели отчета

Этот шаг является ключевым для реализации логики "наследования" плана. Для каждой недели в нашем отчетном периоде мы должны найти самый поздний период плана, который не превышает текущую отчетную неделю.


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

Здесь мы выполняем левое соединение временной таблицы ВТ_ПериодыОтчета с ВТ_ПланыИсходные. Условие соединения ПериодыОтчета.ПериодОтчета >= ПланыИсходные.ПериодПлана находит все планы, которые были установлены до или в текущей отчетной неделе. Затем, с помощью агрегатной функции МАКСИМУМ(ПланыИсходные.ПериодПлана) и группировки по ПериодОтчета и Подразделение, мы выбираем самую позднюю дату установки плана для каждой отчетной недели. Это и будет период, с которого мы возьмем плановые значения.

Шаг 4: Получение актуальных плановых значений

Теперь, когда у нас есть актуальный период плана для каждой недели отчета, мы можем присоединить к нему исходные данные планов, чтобы получить сами суммы планов.


// Временная таблица 4: Актуальные плановые значения
ВЫБРАТЬ
    АктуальныеПериодыПлана.ПериодОтчета,
    АктуальныеПериодыПлана.Подразделение,
    ПланыИсходные.План
ПОМЕСТИТЬ ВТ_АктуальныеПланы
ИЗ
    ВТ_АктуальныеПериодыПлана КАК АктуальныеПериодыПлана
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ПланыИсходные КАК ПланыИсходные
        ПО АктуальныеПериодыПлана.АктуальныйПериодПлана = ПланыИсходные.ПериодПлана
           И АктуальныеПериодыПлана.Подразделение = ПланыИсходные.Подразделение
ГДЕ
    АктуальныеПериодыПлана.АктуальныйПериодПлана ЕСТЬ НЕ NULL // Исключаем недели, для которых вообще не было планов до или в текущем периоде
;

Мы используем внутреннее соединение с ВТ_ПланыИсходные, чтобы связать найденные актуальные периоды с их суммами планов. Условие АктуальныеПериодыПлана.АктуальныйПериодПлана ЕСТЬ НЕ NULL гарантирует, что мы рассматриваем только те недели, для которых удалось найти хотя бы один предшествующий или совпадающий план.

Шаг 5: Получение фактических данных продаж

Фактические данные продаж мы агрегируем по неделям и подразделениям, аналогично тому, как мы формировали периоды отчета.


// Временная таблица 5: Фактические данные продаж
ВЫБРАТЬ
    НАЧАЛОПЕРИОДА(ПродажиОбороты.Период, НЕДЕЛЯ) КАК ПериодОтчета,
    ПродажиОбороты.Подразделение КАК Подразделение,
    СУММА(ПродажиОбороты.СуммаОборот) КАК Факт
ПОМЕСТИТЬ ВТ_Факт
ИЗ
    РегистрНакопления.Продажи.Обороты(
        &НачалоПериода,
        &КонецПериода,
        День,
        Подразделение = &Подразделение
    ) КАК ПродажиОбороты
СГРУППИРОВАТЬ ПО
    НАЧАЛОПЕРИОДА(ПродажиОбороты.Период, НЕДЕЛЯ),
    ПродажиОбороты.Подразделение
;

Шаг 6: Финальное объединение данных плана и факта

На последнем этапе мы объединяем полученные актуальные плановые значения и фактические данные в единый результат. Используем полное соединение, чтобы в отчете присутствовали все недели, по которым есть либо план, либо факт, либо и то, и другое.


// Финальный запрос: Объединение плана и факта
ВЫБРАТЬ
    ЕСТЬNULL(Планы.ПериодОтчета, Факт.ПериодОтчета) КАК Период,
    ЕСТЬNULL(Планы.Подразделение, Факт.Подразделение) КАК Подразделение,
    ЕСТЬNULL(Планы.План, 0) КАК План,
    ЕСТЬNULL(Факт.Факт, 0) КАК Факт
ИЗ
    ВТ_АктуальныеПланы КАК Планы
        ПОЛНОЕ СОЕДИНЕНИЕ ВТ_Факт КАК Факт
        ПО Планы.ПериодОтчета = Факт.ПериодОтчета
           И Планы.Подразделение = Факт.Подразделение
УПОРЯДОЧИТЬ ПО
    Период, Подразделение

Функция ЕСТЬNULL() используется для того, чтобы в случае отсутствия данных по одной из сторон соединения, отображались нули или значения из другой стороны. Такой пакетный запрос позволяет максимально гибко настроить логику получения "среза последних" для плановых показателей и является стандартным, производительным решением в 1С для подобных задач.

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

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

Принцип работы регламентного задания:

  1. Создаем регламентное задание, которое будет запускаться, например, еженедельно или ежедневно.
  2. При каждом запуске задание проверяет, есть ли запись о плане на текущую или следующую неделю (в зависимости от настроек).
  3. Если записи плана нет, задание находит самый последний введенный план (например, за предыдущую неделю) и создает новую запись плана на текущую неделю, копируя в нее данные предыдущего плана.
  4. Таким образом, регистр планов всегда будет содержать актуальный план на каждую неделю, даже если новый план не был явно введен пользователем.

Преимущества регламентного заполнения:

Недостатки и особенности:

Общие рекомендации для план-фактного анализа

Независимо от выбранного подхода к формированию плановых показателей, существуют общие рекомендации, которые помогут вам в построении эффективного план-фактного анализа в 1С:

  1. Четкое определение плановых значений: Убедитесь, что плановые значения реалистичны, обоснованы и учитывают все важные факторы, такие как сезонность, прошлые показатели и текущие рыночные условия.
  2. Единый формат сопоставления: Стремитесь к тому, чтобы данные плана и факта были сопоставимы по всем аналитическим разрезам (номенклатура, подразделение, менеджер, период). Это облегчит анализ и исключит ошибки.
  3. Использование типовых механизмов: В стандартных конфигурациях 1С, таких как 1С:Управление торговлей, 1С:ERP Управление предприятием или 1С:Управление нашей фирмой, часто уже реализованы типовые отчеты и механизмы для план-фактного анализа. Рассмотрите возможность их использования или адаптации.
  4. Оптимизация запросов: При работе с большими объемами данных всегда обращайте внимание на производительность запросов. Используйте индексы, пакетные запросы, временные таблицы и избегайте медленных функций в условиях соединения. Если запрос выполняется слишком долго, возможно, стоит пересмотреть его структуру или перейти к предварительному заполнению данных (как в случае с регламентным заданием).

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

← На главную