Как получить остатки на каждый день в 1С:Запросах и СКД, даже если не было движений

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

Одной из наиболее часто встречающихся задач при разработке отчетов в 1С является получение конечных остатков на каждую дату выбранного периода. Стандартная виртуальная таблица ОстаткиИОбороты при выборе периодичности День возвращает данные только за те дни, в которых были совершены какие-либо движения (приход или расход). Если движений не было, день просто выпадает из выборки, что недопустимо для полноценного анализа складских запасов или взаиморасчетов.

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

Причина проблемы: как работают виртуальные таблицы

Прежде всего, выясним причину, по которой обычный запрос не выводит пустые дни. Виртуальная таблица РегистрНакопления.Товары.ОстаткиИОбороты оптимизирована для быстродействия. Она не хранит остатки на каждую секунду времени, а рассчитывает их исходя из итогов и таблицы движений. Если в конкретный день записей в физической таблице не было, виртуальной таблице «не за что зацепиться», чтобы выдать строку. Даже использование ЛЕВОЕ СОЕДИНЕНИЕ с таблицей дат часто не спасает, так как остаток на «пустой» день не будет равен нулю — он должен быть равен остатку на предыдущую дату.

Способ 1. Использование дополнения периода в СКД

Это самый простой и рекомендуемый способ, если вы используете Систему компоновки данных. Рассмотрим его по шагам.

Во-первых, в тексте запроса набора данных необходимо использовать периодичность Авто или День. Пример базового запроса:


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

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

  1. Выделим группировку Период.
  2. В нижней части окна настроек выберем вкладку «Параметры группировки».
  3. Найдем свойство «Тип дополнения» и установим значение День (или другой нужный период).
  4. Укажем «Начало периода» и «Конец периода», связав их с параметрами отчета &НачалоПериода и &КонецПериода.

После этих манипуляций СКД сама добавит недостающие даты в итоговый отчет.

Способ 2. Правильная настройка ролей полей (важно!)

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

Разберем настройку ролей на вкладке «Набор данных»:

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

Важный момент: если вы хотите видеть остатки по всем филиалам или складам, даже если по конкретному складу не было движения в определенный день, в роли поля остатка должны быть правильно указаны Измерения. Это позволит СКД «размножить» строки по комбинации Период + Склад.

Способ 3. Формирование таблицы дат через запрос

Если задача стоит вне рамок СКД (например, нужно получить таблицу значений в коде), рассмотрим вариант с созданием временной таблицы всех дат периода. Посмотрим на пример кода, где мы генерируем периоды:


// Создадим таблицу всех дат месяца
ВЫБРАТЬ
    ДОБАВИТЬКДАТЕ(&НачалоПериода, ДЕНЬ, Номера.Номер - 1) КАК ДатаПериода
ПОМЕСТИТЬ ВТ_ВсеДаты
ИЗ
    (ВЫБРАТЬ 1 КАК Номер ОБЪЕДИНИТЬ ВЫБРАТЬ 2 ... ОБЪЕДИНИТЬ ВЫБРАТЬ 31) КАК Номера
ГДЕ
    ДОБАВИТЬКДАТЕ(&НачалоПериода, ДЕНЬ, Номера.Номер - 1) <= &КонецПериода
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_ВсеДаты.ДатаПериода КАК Период,
    ЕСТЬNULL(Товары.КоличествоКонечныйОстаток, 0) КАК Остаток
ИЗ
    ВТ_ВсеДаты КАК ВТ_ВсеДаты
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Товары.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, День, , ) КАК Товары
        ПО ВТ_ВсеДаты.ДатаПериода = Товары.Период

Проанализируем этот запрос. Здесь кроется главная ловушка: ЕСТЬNULL(Товары.КоличествоКонечныйОстаток, 0) вернет 0 в те дни, когда движения не было. Но остаток-то на складе есть! Он просто не изменился. Для решения этой проблемы в «чистом» запросе (без СКД) обычно используют расчет накопленным итогом.

Способ 4. Расчет накопленным итогом (универсальный метод)

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

Рассмотрим логику построения такого запроса:

  1. Получаем таблицу всех дат (как в предыдущем примере).
  2. Получаем таблицу всех движений (приход, расход) и начальный остаток на дату начала отчета.
  3. Соединяем таблицу дат с таблицей движений по условию Движения.Период <= ВТ_ВсеДаты.ДатаПериода.
  4. Группируем результат по полю ВТ_ВсеДаты.ДатаПериода и суммируем разницу приходов и расходов.

Пример реализации части запроса:


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

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

Выясним, на что стоит обратить внимание при реализации любого из выбранных методов:

Подводя итог, отметим: для отчетов на СКД самым правильным и быстрым решением является использование Ролей полей и Типа дополнения «День» в настройках группировки. Для сложных алгоритмов внутри обработок используйте временную таблицу дат и соединение по условию МЕНЬШЕ ИЛИ РАВНО для расчета актуального остатка на каждую секунду времени.

← На главную