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