Как получить остатки на каждый день в запросе 1С, включая дни без оборотов?

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

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

Рассмотрим эту проблему подробно. Если в определенный день не было движений (приходов или расходов), виртуальная таблица ОстаткиИОбороты с периодичностью День просто пропустит эту дату — решить проблему поможет отчет по динамике остатков и потоков денежных средств. В итоге график или таблица получаются "рваными". Разберем, как решить эту задачу и получить полные данные на каждый календарный день.

Суть проблемы и варианты решения

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

Мы проанализируем два основных способа решения:

  1. Метод полного соединения (в коде запроса): создание таблицы всех дат периода и соединение с ней данных регистра — для этого подойдёт выгрузка данных 1С во внешние базы и BI-системы.
  2. Метод СКД (Система Компоновки Данных): использование встроенных механизмов дополнения периодов.

Способ 1: Формирование таблицы дат в запросе

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

  1. Формируем таблицу, содержащую все даты интересующего нас периода.
  2. Используем ЛЕВОЕ СОЕДИНЕНИЕ (LEFT JOIN) этой таблицы дат с таблицей остатков.
  3. Обрабатываем значения NULL для тех дней, когда данных не было.

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


ВЫБРАТЬ
    ДОБАВИТЬКДАТЕ(&НачалоПериода, ДЕНЬ, aa.a * 1000 + bb.b * 100 + cc.c * 10 + dd.d) КАК Период
ПОМЕСТИТЬ ВТ_Даты
ИЗ
    (ВЫБРАТЬ 0 КАК a ОБЪЕДИНИТЬ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВЫБРАТЬ 3) КАК aa
        , (ВЫБРАТЬ 0 КАК b ОБЪЕДИНИТЬ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВЫБРАТЬ 3 ОБЪЕДИНИТЬ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВЫБРАТЬ 5 ОБЪЕДИНИТЬ ВЫБРАТЬ 6 ОБЪЕДИНИТЬ ВЫБРАТЬ 7 ОБЪЕДИНИТЬ ВЫБРАТЬ 8 ОБЪЕДИНИТЬ ВЫБРАТЬ 9) КАК bb
        , (ВЫБРАТЬ 0 КАК c ОБЪЕДИНИТЬ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВЫБРАТЬ 3 ОБЪЕДИНИТЬ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВЫБРАТЬ 5 ОБЪЕДИНИТЬ ВЫБРАТЬ 6 ОБЪЕДИНИТЬ ВЫБРАТЬ 7 ОБЪЕДИНИТЬ ВЫБРАТЬ 8 ОБЪЕДИНИТЬ ВЫБРАТЬ 9) КАК cc
        , (ВЫБРАТЬ 0 КАК d ОБЪЕДИНИТЬ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВЫБРАТЬ 3 ОБЪЕДИНИТЬ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВЫБРАТЬ 5 ОБЪЕДИНИТЬ ВЫБРАТЬ 6 ОБЪЕДИНИТЬ ВЫБРАТЬ 7 ОБЪЕДИНИТЬ ВЫБРАТЬ 8 ОБЪЕДИНИТЬ ВЫБРАТЬ 9) КАК dd
ГДЕ
    ДОБАВИТЬКДАТЕ(&НачалоПериода, ДЕНЬ, aa.a * 1000 + bb.b * 100 + cc.c * 10 + dd.d) <= &КонецПериода
;

В этом блоке мы создали временную таблицу ВТ_Даты. Теперь соединим ее с данными регистра. Важно помнить, что мы должны использовать таблицу ОстаткиИОбороты, чтобы "захватить" изменения.

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

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

Способ 2: Настройка дополнения периодов в СКД

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

Шаг 1. Настройка набора данных

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


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

Шаг 2. Настройка роли поля "Период"

Перейдем на вкладку "Наборы данных". Для поля Период в колонке "Роль" убедитесь, что указано, что это период. Обычно 1С определяет это автоматически (значок календаря с цифрой 1).

Шаг 3. Настройка дополнения данных

Перейдем на вкладку "Настройки" отчета:

  1. В структуре отчета добавьте группировку по полю Период (или сначала по Номенклатуре, а внутри — детальные записи с Периодом, в зависимости от желаемого вида отчета).
  2. Выделите группировку Период.
  3. На вкладке настроек этой группировки найдите раздел "Поля группировки".
  4. Нажмите на свойства поля группировки Период.
  5. В открывшемся окне параметров группировки найдите свойство "Тип дополнения".
  6. Установите значение "День" (или "В пределах периода", если вы хотите заполнить от параметра НачалоПериода до КонецПериода) — для этого подойдёт инструмент для формирования платежного календаря и контроля остатков.

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

Шаг 4. Обработка пустых значений (NULL)

Чтобы вместо пустоты на дату без движений показывался остаток с предыдущего дня, стандартного дополнения СКД недостаточно — оно заполняет нулями, если нет записи. Но ОстаткиИОбороты возвращают КонечныйОстаток именно на момент записи.

Для корректного вывода "протянутого" остатка в СКД рекомендуется использовать специальный синтаксис языка выражений ресурсов или виртуальную таблицу ОстаткиИОбороты с правильно настроенными параметрами:

Но самый надежный способ получить остаток на каждую дату в СКД при использовании дополнения периодов — это не брать поле КонечныйОстаток напрямую, а рассчитывать его через начальный остаток + обороты, либо использовать возможности постобработки.

Важный нюанс при использовании ЛЕВОГО СОЕДИНЕНИЯ

Вернемся к варианту с запросом. Если мы используем ВТ_Даты и соединяем её с регистром, обратим внимание на условие соединения. Если мы соединим просто по дате:


...
ИЗ
    ВТ_Даты КАК Календарь
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Товары.ОстаткиИОбороты(...) КАК Данные
        ПО Календарь.Период = Данные.Период

Такой подход сработает некорректно для получения остатков. Потому что в регистре запись может быть в 12:00, а календарь у нас на начало дня (00:00). Кроме того, если движений не было, соединения не произойдет.

Поэтому для задачи "остаток на каждый день" наиболее эффективным и правильным решением является:

  1. Получить таблицу оборотов за весь период.
  2. Получить остаток на начало всего периода (одна цифра по каждой номенклатуре).
  3. Сформировать календарь.
  4. Соединить Календарь и Номенклатуру (декартово произведение), чтобы получить "сетку" (каждый товар на каждый день).
  5. Присоединить к этой сетке обороты.
  6. Рассчитать остаток нарастающим итогом: ОстатокНачальный + Сумма(Оборот).

В случае с СКД, механизм вычислит это сам, если вы правильно укажете роли полей "Начальный остаток" и "Конечный остаток" и включите дополнение периодов. СКД умеет "протягивать" остатки по периодам дополнения.

Резюме

Если вам нужно просто вывести список дат в отчете — используйте Дополнение периодов в настройках группировки СКД. Это самый быстрый способ ("no-code").
Если вам нужны данные для дальнейшей программной обработки — формируйте таблицу дат через генератор чисел или используйте производственный календарь для отчетов по дням и выполняйте ЛЕВОЕ СОЕДИНЕНИЕ.

← На главную