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

Программист 1С v8.3 (Обычные формы) Бухгалтерский учет IT и автоматизация бизнеса
← На главную

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

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

Выясним причину проблемы

Попробуем проанализировать простой запрос, в котором календарная таблица соединяется с виртуальной таблицей ОстаткиИОбороты:


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

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

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

Решение 1: Расчет остатка накопительным итогом в запросе

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

Рассмотрим алгоритм решения по шагам:

  1. Получаем начальный остаток по выбранному счету на самую первую дату периода.
  2. Получаем обороты за каждый день периода, когда были движения.
  3. Соединяем таблицу календарных дат с оборотами по нестрогому условию: ДатаКалендаря >= ДатаОборота.
  4. Суммируем обороты за все предшествующие дни и добавляем их к начальному остатку на начало периода.

Обратите внимание: Соединение по нестрогому условию >= создаёт декартово произведение строк. Чтобы запрос не приводил к падению производительности на СУБД, необходимо предварительно отфильтровать данные по конкретному счету и субконто во временных таблицах до соединения с календарем.

Решение 2: Передача даты в параметр через связи наборов данных в СКД

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

Разберем настройку СКД по шагам:

  1. Создаем Первый набор данных (запрос), который выбирает календарные даты периода (например, из регистра сведений РегламентированныйПроизводственныйКалендарь или динамически сгенерированной таблицы дат).
  2. Создаем Второй набор данных (запрос) к виртуальной таблице Остатки регистра бухгалтерии на дату с нужными фильтрами:
    
    ВЫБРАТЬ
        ХозрасчетныйОстатки.Субконто1,
        ХозрасчетныйОстатки.Субконто2,
        ХозрасчетныйОстатки.СуммаОстаток
    ИЗ
        РегистрБухгалтерии.Хозрасчетный.Остатки(&Период, Счет = &Счет, , ) КАК ХозрасчетныйОстатки
    
  3. Переходим на вкладку Связи наборов данных и настраиваем связь от Первого набора ко Второму:
    • Источник связи: НаборДанных1 (таблица дат).
    • Приемник связи: НаборДанных2 (остатки).
    • Условие связи: ДатаКалендаря.
    • Параметры: передаем ДатаКалендаря в параметр &Период второго набора данных, предварительно выразив дату на конец дня: КОНЕЦПЕРИОДА(ДатаКалендаря, "ДЕНЬ").

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

Решение 3: Использование функции ВЫЧИСЛИТЬВЫРАЖЕНИЕ() в СКД

Ещё один вариант реализации в СКД — использование вычисляемых полей и ресурсов с функцией ВЫЧИСЛИТЬВЫРАЖЕНИЕ(). Это позволяет рассчитать накопительный итог по полю оборота прямо в компоновке данных на уровне группировок отчета, не усложняя исходный запрос к базе данных.

← На главную