При построении отчетов по регистрам накопления за длительный интервал разработчики часто сталкиваются с неожиданным поведением: сумма по детальным строкам за конкретный месяц оказывается меньше, чем итоговая сумма в заголовке этого же месяца. При этом, если выполнить тот же самый запрос не за целый год, а только за один проблемный месяц, все сходится идеально. Разберем детально, почему так происходит на уровне платформы «1С:Предприятие», и рассмотрим несколько проверенных способов решения этой задачи.
Проанализируем исходную ситуацию. В запросе выбираются данные из виртуальной таблицы регистра накопления ПартииТоваровНаСкладах.ОстаткиИОбороты:
ВЫБРАТЬ
ПартииТоваровНаСкладахОстаткиИОбороты.ПериодМесяц КАК ПериодМесяц,
ПартииТоваровНаСкладахОстаткиИОбороты.Номенклатура КАК Номенклатура,
ПартииТоваровНаСкладахОстаткиИОбороты.РУ_СтоимостьНачальныйОстаток КАК РУ_СтоимостьНачальныйОстаток
ИЗ
РегистрНакопления.ПартииТоваровНаСкладах.ОстаткиИОбороты(
&Нач,
&Кон,
Авто,
,
Склад = &Склад И СерияНоменклатуры.Организация = &Организация
) КАК ПартииТоваровНаСкладахОстаткиИОбороты
Запрос выполняется за период с 01.01.2016 по 31.12.2016. При анализе группировки за 01.10.2016 в результирующей выборке детальных записей не хватает нескольких номенклатурных позиций, хотя в итоговую сумму группировки периода их стоимость входит. Выясним причину этой особенности.
Такое поведение не является ошибкой в данных или сбоем платформы — это фундаментальный механизм работы виртуальной таблицы ОстаткиИОбороты. Рассмотрим, как именно платформа формирует строки:
ДвиженияИГраницыПериода. Платформа рассчитывает и выводит остатки только на крайние границы всего запрошенного интервала (дата начала &Нач и дата окончания &Кон), а также на моменты движений. Промежуточные месяцы не дополняются строками с нулевыми движениями. Когда запрос формируется строго за октябрь (с 01.10 по 31.10), начало октября становится граничной точкой всего запроса, поэтому товар попадает в выборку. В годовом же запросе октябрь является промежуточным шагом, и «неподвижные» товары просто отсекаются.Авто в параметрах виртуальной таблицы рассчитано строго на работу совместно с инструкцией ИТОГИ ПО ... или механизмом СКД. Если формируется плоская выборка, периодичность следует задавать явно — Месяц.Рассмотрим рабочие способы получения корректных помесячных данных.
Если задача решается в рамках Системы компоновки данных, наиболее надежным и штатным подходом является использование встроенного механизма дополнения периодов. Разберем настройку по шагам:
СтоимостьНачальныйОстаток, так и СтоимостьКонечныйОстаток. Платформе требуются оба поля для правильного расчета переходящих итогов.НачальныйОстаток, для конечного — КонечныйОстаток, связывая их с полем ПериодМесяц и задавая одинаковое имя группы остатков.ПериодМесяц. В нижней панели свойств группировки переходим в раздел Дополнение периодов.Месяц, а в полях начальной и конечной даты указываем параметры отчета &Нач и &Кон.В этом случае СКД автоматически построит полную сетку периодов для каждой номенклатуры и рассчитает правильный начальный остаток на каждый месяц, даже если оборотов по позиции не было.
Если отчет строится без СКД программным обходом через Запрос.Выполнить().Выбрать(), восстановить целостность данных можно с помощью секции ИТОГИ:
ВЫБРАТЬ
ПартииТоваровНаСкладахОстаткиИОбороты.ПериодМесяц КАК ПериодМесяц,
ПартииТоваровНаСкладахОстаткиИОбороты.Номенклатура КАК Номенклатура,
ПартииТоваровНаСкладахОстаткиИОбороты.РУ_СтоимостьНачальныйОстаток КАК РУ_СтоимостьНачальныйОстаток
ИЗ
РегистрНакопления.ПартииТоваровНаСкладах.ОстаткиИОбороты(
&Нач,
&Кон,
Месяц,
,
Склад = &Склад И СерияНоменклатуры.Организация = &Организация
) КАК ПартииТоваровНаСкладахОстаткиИОбороты
ИТОГИ
СУММА(РУ_СтоимостьНачальныйОстаток)
ПО
ПериодМесяц,
Номенклатура
При наличии явного указания ИТОГИ ПО ПериодМесяц, Номенклатура платформа строит иерархическое дерево, в котором при обходе группировок пересчет остатков выполняется с учетом таблиц итогов регистра, что нивелирует расхождения.
В теме также обсуждался альтернативный подход: отказаться от тяжелой таблицы ОстаткиИОбороты и использовать более производительную виртуальную таблицу Остатки. Разберем эту схему подробнее:
РегистрНакопления.ПартииТоваровНаСкладах.Остатки(&ПериодСреза, ...).НаборПериоды к набору НаборОстатки. В поле параметров связи передаем поле периода из первого набора в параметр &ПериодСреза второго набора.Такой подход читает готовые рассчитанные остатки напрямую из физической таблицы итогов регистра. Однако важно учитывать: связь наборов данных по параметру выполняет параметризованный запрос к СУБД для каждого шага периода, поэтому для коротких интервалов (например, 12 месяцев) этот метод может быть быстрее, а на длинных горизонтах дат проще и надежнее использовать метод дополнения первого способа.
Если требуется получить плоскую таблицу без использования СКД и без иерархического обхода итогов, разберем подход на основе декартова произведения сетки периодов и номенклатуры:
ВЫБРАТЬ
СеткаПериодов.Период КАК Период,
ВсеТовары.Номенклатура КАК Номенклатура,
ЕСТЬNULL(ТаблицаОстатков.СтоимостьОстаток, 0) КАК СтоимостьОстаток
ИЗ
ВТ_Периоды КАК СеткаПериодов
ДРОЗД_СОЕДИНЕНИЕ ВТ_Товары КАК ВсеТовары // Декартово произведение дат и номенклатуры
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ОстаткиПоМесяцам КАК ТаблицаОстатков
ПО СеткаПериодов.Период = ТаблицаОстатков.Период
И ВсеТовары.Номенклатура = ТаблицаОстатков.Номенклатура
Сетка дат может формироваться выбором уникальных дат из производственного календаря или регистра сведений периодических курсов валют. Подсоединяя остатки к полной сетке, мы гарантируем, что для каждого месяца будут присутствовать строки по всем номенклатурным позициям.