Как вывести остатки и планы в СКД на каждый день, если не было движений

Программист 1С v8.3 (Обычные формы) Управленческий учет Промышленность, строительство и АПК
← На главную

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

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

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

Рассмотрим ситуацию: виртуальные таблицы ОстаткиИОбороты и Обороты по умолчанию возвращают данные только за те периоды, в которых зафиксированы записи в регистре. Если 5-го числа склад отдыхал, запрос к виртуальной таблице с периодичностью День просто пропустит эту дату. При левом соединении планов и фактических остатков, если по какой-то номенклатуре нет остатка, но есть план (или наоборот), данные могут «выпадать» из итоговой выборки. Наша задача — создать непрерывную временную шкалу.

Решение 1: Использование механизмов дополнения периодов в СКД

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

  1. Настройка виртуальной таблицы: В запросе для таблицы ТоварыНаСкладах.ОстаткиИОбороты необходимо использовать метод дополнения ДвиженияИГраницыПериода. Это гарантирует, что система получит данные на начало и конец выбранного интервала.
  2. Настройка ролей полей: На закладке «Наборы данных» для поля ПериодДень (или аналогичного) нужно настроить роль. В поле «Роль» укажите «Период», порядковый номер и обязательно выберите тип дополнения «День». Здесь же указываются параметры &НачалоПериода и &КонецПериода.
  3. Настройки группировки: В настройках самого отчета, выбрав группировку по периоду, перейдите в «Детальные настройки» и установите флаг «Дополнять периоды». Укажите дату начала и окончания.

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

Решение 2: Формирование календаря через временные таблицы в запросе

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

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


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

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


ВЫБРАТЬ
    ПартииТоваровНаСкладахОбороты.Номенклатура,
    ПартииТоваровНаСкладахОбороты.ПериодДень,
    СУММА(ЕСТЬNULL(ВтПлан.План, 0)) КАК План
ПОМЕСТИТЬ ВтПланПартии
ИЗ
    РегистрНакопления.ПартииТоваровНаСкладах.Обороты(&Начало, &Конец, Авто, ) КАК ПартииТоваровНаСкладахОбороты
ЛЕВОЕ СОЕДИНЕНИЕ ВтПлан КАК ВтПлан
    ПО ПартииТоваровНаСкладахОбороты.Номенклатура = ВтПлан.Номенклатура
    И ПартииТоваровНаСкладахОбороты.ПериодДень >= ВтПлан.PeriodDay
СГРУППИРОВАТЬ ПО
    ПартииТоваровНаСкладахОбороты.ПериодДень,
    ПартииТоваровНаСкладахОбороты.Номенклатура;

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

Решение 3: Декартово произведение для создания полной сетки

Для того чтобы отчет был гарантированно заполнен по всем дням и всей номенклатуре, профессиональным подходом считается создание эталонной таблицы «Дата — Номенклатура». Проанализируем, как это можно реализовать:

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

Рассмотрим пример запроса, который собирает остатки на каждый день, используя такой подход:


// Шаг 1: Получаем все уникальные даты, где были движения хоть у чего-то
ВЫБРАТЬ
    ТоварыНаСкладахОстаткиИОбороты.ПериодДень
ПОМЕСТИТЬ ВтВсеДаты
ИЗ
    РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&Начало, &Конец, Авто, , ) КАК ТоварыНаСкладахОстаткиИОбороты;

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

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

В данном примере мы проанализировали механизм «протягивания» последнего известного остатка на последующие даты. С помощью функции МАКСИМУМ(ВтОстатки.ПериодДень) мы находим дату последнего реального движения товара, которая меньше или равна текущей рассматриваемой дате календаря.

Оптимизация итогового отчета

Выясним причину, по которой отчет может работать медленно. Использование конструкций ЛЕВОЕ СОЕДИНЕНИЕ по условию >= на больших объемах данных (десятки тысяч позиций номенклатуры и сотни дней) создает огромную нагрузку на сервер SQL.

Рекомендации по оптимизации:

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

← На главную