Почему не совпадают начальные остатки при группировке по месяцам в таблице ОстаткиИОбороты в 1С?

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

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

Проанализируем детально, почему так происходит, как устроена работа виртуальной таблицы ОстаткиИОбороты платформы «1С:Предприятие» и какими способами можно получить правильные остатки на начало каждого периода.

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

Рассмотрим типовой запрос, который приводит к неверным результатам:


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

Разберем по шагам, что происходит внутри СУБД при выполнении такого запроса:

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

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

Способ 1. Использование конструкции языка запросов ИТОГИ ПЕРИОДАМИ

Если отчет или обработка строятся с помощью прямого выполнения запроса в коде без СКД, категорически нельзя использовать СГРУППИРОВАТЬ ПО для полей начальных и конечных остатков с периодичностью. Вместо этого необходимо использовать специальную секцию ИТОГИ ... ПО ... ПЕРИОДАМИ.

Посмотрим на пример правильного запроса:


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

Выясним важные правила работы этого метода:

  1. Конструкция ПЕРИОДАМИ(Месяц, &НП, &КП) заставляет платформу автоматически дополнить все недостающие периоды и корректно рассчитать остатки методом нарастающего итога.
  2. Для корректной работы алгоритма расчета итогов по периодам в списке выборки обязательно должны присутствовать оба парных ресурса: и СтоимостьНачальныйОстаток, и СтоимостьКонечныйОстаток.
  3. Обход результатов такого запроса в коде выполняется через выборку с типом ТипОбхода.ПоГруппировкам.

Способ 2. Настройка отчета в Системе компоновки данных (СКД)

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

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

  1. В тексте запроса набора данных СКД выбираем детальные поля: Период, измерения, ресурсы (СтоимостьНачальныйОстаток, СтоимостьКонечныйОстаток, СтоимостьОборот). Конструкцию СГРУППИРОВАТЬ ПО в запросе не используем.
  2. На вкладке «Ресурсы» указываем выражения для расчета итогов по полям остатков. СКД автоматически определяет роли полей остатков.
  3. На вкладке «Настройки» в структуре отчета добавляем группировку по полю Период.
  4. В свойствах группировки Период включаем флаг «Дополнение периодов». Указываем тип дополнения — Месяц, а в качестве начальной и конечной даты задаем параметры &НачалоПериода и &КонецПериода.

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

Способ 3. Расчет нарастающим итогом через временные таблицы

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

Рассмотрим алгоритм реализации:

  1. В первой временной таблице получаем срез начальных остатков на единую дату &НП через виртуальную таблицу РегистрНакопления.УчетЗатратРегл.Остатки(&НП, ...).
  2. Во второй временной таблице получаем помесячные обороты через таблицу РегистрНакопления.УчетЗатратРегл.Обороты(&НП, &КП, Месяц, ...).
  3. Объединяем начальный остаток и обороты предшествующих месяцев с помощью соединения по условию ПериодОборотов < ТекущийПериод, получая точный математический остаток на начало каждого месяца.

Способ 4. Использование виртуальной таблицы Остатки для фиксированного списка дат

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

Для этого во временную таблицу помещается список интересующих дат (например, 01.04, 01.05, 01.06), после чего с помощью соединения или через выгрузку в цикле остатки получаются непосредственно на каждую точку времени. Таблица Остатки всегда возвращает полный срез данных регистра независимо от наличия движений в выбранном месяце.

← На главную