При построении помесячных отчетов по складским остаткам разработчики часто сталкиваются с ситуацией, когда по номенклатуре в определенные месяцы не было никаких приходов и расходов. Если запросить данные напрямую из виртуальной таблицы РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты с периодичностью Месяц, строки за такие «пустые» периоды просто не попадут в результат. В итоге остаток на начало месяца пропадает, а непрерывная хронология нарушается.
Разберем причины такого поведения платформы 1С и подробно рассмотрим рабочие способы решения этой задачи как в тексте запроса, так и средствами Системы компоновки данных (СКД).
Проанализируем механику работы виртуальной таблицы ОстаткиИОбороты. Платформа 1С оптимизирует запросы к базе данных и формирует срезы только для тех интервалов времени, внутри которых были зафиксированы движения по указанной комбинации измерений (например, Номенклатура, ХарактеристикаНоменклатуры, Склад). Если за весь месяц по товару не проводился ни один документ, запись за этот месяц отсутствует в физических таблицах итогов и движений за период, а значит, виртуальная таблица не будет генерировать пустую строку с перенесенным остатком.
Попытка решить задачу простым условием ВЫБОР КОГДА внутри одного месяца не дает результата, так как самой строки с нужным периодом нет в выборке. Чтобы получить остаток на начало каждого месяца, необходимо сформировать непрерывную сетку периодов и сопоставить ей последний актуальный остаток.
Рассмотрим вариант решения задачи на чистом языке запросов 1С. Для этого сформируем непрерывную шкалу месяцев, получим все имеющиеся движения и свяжем их так, чтобы для каждого месяца определялся последний зафиксированный срез остатков.
Разберем по шагам построение запроса:
РегистрСведений.РегламентированныйПроизводственныйКалендарь).Месяц по нужным отборам.ПериодыМ.ПериодМесяц >= ОстаткиПоМесяцам.ПериодМесяц и с помощью функции МАКСИМУМ находим ближайший предшествующий период с движениями.Посмотрим на готовый пример запроса:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
НАЧАЛОПЕРИОДА(РегламентированныйПроизводственныйКалендарь.ДатаКалендаря, МЕСЯЦ) КАК ПериодМесяц
ПОМЕСТИТЬ ПериодыМ
ИЗ
РегистрСведений.РегламентированныйПроизводственныйКалендарь КАК РегламентированныйПроизводственныйКалендарь
ГДЕ
РегламентированныйПроизводственныйКалендарь.ДатаКалендаря МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
НАЧАЛОПЕРИОДА(РегламентированныйПроизводственныйКалендарь.ДатаКалендаря, МЕСЯЦ)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗРЕШЕННЫЕ
ТоварыНаСкладахОстаткиИОбороты.Период КАК ПериодМесяц,
ТоварыНаСкладахОстаткиИОбороты.Номенклатура КАК Номенклатура,
ТоварыНаСкладахОстаткиИОбороты.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
ТоварыНаСкладахОстаткиИОбороты.КоличествоНачальныйОстаток КАК НачальныйОстатокПоМесяцам,
ТоварыНаСкладахОстаткиИОбороты.КоличествоКонечныйОстаток КАК КонечныйОстатокПоМесяцам
ПОМЕСТИТЬ ОстаткиПоМесяцам
ИЗ
РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(
&НачалоПериода,
&КонецПериода,
Месяц,
,
Номенклатура В (&Номенклатура)
И Склад В (&СкладОстаткиПоМесяцам)) КАК ТоварыНаСкладахОстаткиИОбороты
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ОпределениеПериода.ПериодМесяц КАК ПериодМесяц,
ОпределениеПериода.Номенклатура КАК Номенклатура,
ОпределениеПериода.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
ОпределениеПериода.ПериодМесяцДляСвязи КАК ПериодМесяцДляСвязи,
ВЫБОР
КОГДА ОпределениеПериода.ПериодМесяц = ОпределениеПериода.ПериодМесяцДляСвязи
ТОГДА ОстаткиПоМесяцам.НачальныйОстатокПоМесяцам
ИНАЧЕ ОстаткиПоМесяцам.КонечныйОстатокПоМесяцам
КОНЕЦ КАК Остаток
ИЗ
(ВЫБРАТЬ
ПериодыМ.ПериодМесяц КАК ПериодМесяц,
ОстаткиПоМесяцам.Номенклатура КАК Номенклатура,
ОстаткиПоМесяцам.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры,
МАКСИМУМ(ОстаткиПоМесяцамПериоды.ПериодМесяц) КАК ПериодМесяцДляСвязи
ИЗ
ПериодыМ КАК ПериодыМ
ЛЕВОЕ СОЕДИНЕНИЕ ОстаткиПоМесяцам КАК ОстаткиПоМесяцамПериоды
ЛЕВОЕ СОЕДИНЕНИЕ ОстаткиПоМесяцам КАК ОстаткиПоМесяцам
ПО ОстаткиПоМесяцамПериоды.Номенклатура = ОстаткиПоМесяцам.Номенклатура
И ОстаткиПоМесяцамПериоды.ХарактеристикаНоменклатуры = ОстаткиПоМесяцам.ХарактеристикаНоменклатуры
ПО ПериодыМ.ПериодМесяц >= ОстаткиПоМесяцамПериоды.ПериодМесяц
СГРУППИРОВАТЬ ПО
ПериодыМ.ПериодМесяц,
ОстаткиПоМесяцам.Номенклатура,
ОстаткиПоМесяцам.ХарактеристикаНоменклатуры) КАК ОпределениеПериода
ЛЕВОЕ СОЕДИНЕНИЕ ОстаткиПоМесяцам КАК ОстаткиПоМесяцам
ПО ОпределениеПериода.Номенклатура = ОстаткиПоМесяцам.Номенклатура
И ОпределениеПериода.ХарактеристикаНоменклатуры = ОстаткиПоМесяцам.ХарактеристикаНоменклатуры
И ОпределениеПериода.ПериодМесяцДляСвязи = ОстаткиПоМесяцам.ПериодМесяц
Если задача решается в рамках разработки отчета на Системе компоновки данных, писать сложные многоуровневые соединения в запросе не требуется. Платформа 1С предоставляет встроенный механизм автоматического дополнения периодов и протягивания остатков.
Разберем настройку по шагам:
ОстаткиИОбороты: ПериодМесяц, Номенклатура, КоличествоНачальныйОстаток, КоличествоКонечныйОстаток.НачальныйОстаток, для конечного — КонечныйОстаток, а полю ПериодМесяц присваиваем тип роли Период с порядковым номером.ПериодМесяц. В свойствах группировки находим параметр «Дополнение периода»: устанавливаем флаг, выбираем тип дополнения Месяц, а также указываем начальную дату &НачалоПериода и конечную дату &КонецПериода.При таких настройках СКД самостоятельно создаст строки для месяцев без движений и корректно перенесет остатки с предыдущего периода.
Еще один надежный подход в запросах — расчет начального остатка на любой месяц через сложение остатка на общую дату старта и всех оборотов, совершенных до этого месяца:
&НачалоПериода через таблицу ТоварыНаСкладах.Остатки.ТоварыНаСкладах.Обороты.ОстатокНаСтарт + СУММА(ОборотыЗаПредыдущиеМесяцы).СУММА(КоличествоОборот) OVER (PARTITION BY Номенклатура ORDER BY ПериодМесяц ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING), что значительно ускоряет выполнение запроса на больших базах.При использовании запросов с заполнением периодов обратим внимание на следующие технические особенности:
Заполнение календаря: при получении сетки месяцев из РегистрСведений.РегламентированныйПроизводственныйКалендарь убедимся, что календарь в базе заполнен на все проверяемые годы. Если нужного года в календаре нет, запрос вернет пустой результат. В качестве альтернативы таблицу дат можно передать в запрос через параметр (массив или таблицу значений).
Нагрузка на СУБД: соединения вида ПериодыМ.ПериодМесяц >= Движения.ПериодМесяц выполняются на уровне СУБД как неэквивалентные (вложенные циклы). При большом объеме номенклатуры и широком диапазоне дат такой запрос может создавать высокую нагрузку, поэтому для тяжелых отчетов предпочтительнее использовать штатные механизмы СКД или оконные функции.