Почему в таблице ОстаткиИОбороты сумма детальных записей не совпадает с итогом по периоду и как это исправить?

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

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

Проанализируем исходную ситуацию. В запросе выбираются данные из виртуальной таблицы регистра накопления ПартииТоваровНаСкладах.ОстаткиИОбороты:


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

Запрос выполняется за период с 01.01.2016 по 31.12.2016. При анализе группировки за 01.10.2016 в результирующей выборке детальных записей не хватает нескольких номенклатурных позиций, хотя в итоговую сумму группировки периода их стоимость входит. Выясним причину этой особенности.

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

Такое поведение не является ошибкой в данных или сбоем платформы — это фундаментальный механизм работы виртуальной таблицы ОстаткиИОбороты. Рассмотрим, как именно платформа формирует строки:

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

Рассмотрим рабочие способы получения корректных помесячных данных.

Способ 1. Использование встроенного дополнения периодов в СКД

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

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

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

Способ 2. Получение итогов средствами классического запроса

Если отчет строится без СКД программным обходом через Запрос.Выполнить().Выбрать(), восстановить целостность данных можно с помощью секции ИТОГИ:


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

При наличии явного указания ИТОГИ ПО ПериодМесяц, Номенклатура платформа строит иерархическое дерево, в котором при обходе группировок пересчет остатков выполняется с учетом таблиц итогов регистра, что нивелирует расхождения.

Способ 3. Связка двух наборов данных в СКД через таблицу остатков

В теме также обсуждался альтернативный подход: отказаться от тяжелой таблицы ОстаткиИОбороты и использовать более производительную виртуальную таблицу Остатки. Разберем эту схему подробнее:

  1. Первый набор данных (НаборПериоды): простой запрос, возвращающий список всех первых чисел месяцев года (например, 12 строк дат).
  2. Второй набор данных (НаборОстатки): запрос к таблице РегистрНакопления.ПартииТоваровНаСкладах.Остатки(&ПериодСреза, ...).
  3. Вкладка «Связи наборов данных»: настраиваем связь от набора НаборПериоды к набору НаборОстатки. В поле параметров связи передаем поле периода из первого набора в параметр &ПериодСреза второго набора.

Такой подход читает готовые рассчитанные остатки напрямую из физической таблицы итогов регистра. Однако важно учитывать: связь наборов данных по параметру выполняет параметризованный запрос к СУБД для каждого шага периода, поэтому для коротких интервалов (например, 12 месяцев) этот метод может быть быстрее, а на длинных горизонтах дат проще и надежнее использовать метод дополнения первого способа.

Способ 4. Формирование календарной сетки в чистом SQL-запросе

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


ВЫБРАТЬ
    СеткаПериодов.Период КАК Период,
    ВсеТовары.Номенклатура КАК Номенклатура,
    ЕСТЬNULL(ТаблицаОстатков.СтоимостьОстаток, 0) КАК СтоимостьОстаток
ИЗ
    ВТ_Периоды КАК СеткаПериодов
    ДРОЗД_СОЕДИНЕНИЕ ВТ_Товары КАК ВсеТовары // Декартово произведение дат и номенклатуры
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ОстаткиПоМесяцам КАК ТаблицаОстатков
        ПО СеткаПериодов.Период = ТаблицаОстатков.Период
        И ВсеТовары.Номенклатура = ТаблицаОстатков.Номенклатура

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

← На главную