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