При построении аналитических отчетов и выгрузок часто возникает задача: получить остатки по регистру накопления (например, по взаиморасчетам с контрагентами) строго на каждый календарный день интервала, даже если в конкретные даты движений по регистру не было. Стандартное обращение к виртуальной таблице ОстаткиИОбороты с периодичностью День возвращает только те дни, в которых были совершены регистратором хозяйственные операции. Разберем по шагам, как заставить запрос дополнить пропущенные даты, какие ошибки часто совершают разработчики и как корректно получить актуальный остаток на каждый день.
Язык запросов 1С предоставляет механизм автоматического дополнения периодов в блоке расчета итогов. Для этого используется конструкция ПЕРИОДАМИ в секции ИТОГИ ПО. Проанализируем базовый вариант запроса и правильный порядок его обработки во встроенном языке.
Посмотрим на пример запроса с дополнением дат:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Взаиморасчеты.Период КАК Период,
| Взаиморасчеты.СуммаВзаиморасчетовКонечныйОстаток КАК КонечныйОстаток
|ИЗ
| РегистрНакопления.ВзаиморасчетыСКонтрагентамиПоДокументамРасчетов.ОстаткиИОбороты(
| &НачалоПериода,
| &КонецПериода,
| День,
| ДвиженияИГраницыПериода,
| ДокументРасчетовСКонтрагентом = &ДокументРасчетовСКонтрагентом) КАК Взаиморасчеты
|ИТОГИ
| СУММА(КонечныйОстаток)
|ПО
| ОБЩИЕ,
| Период ПЕРИОДАМИ(ДЕНЬ, &НачалоПериода, &КонецПериода)";
Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(ПолеВводаДата.Значение));
Запрос.УстановитьПараметр("КонецПериода", КонецДня(ТекущаяДата()));
Запрос.УстановитьПараметр("ДокументРасчетовСКонтрагентом", ВыбранныйДокумент);
РезультатЗапроса = Запрос.Выполнить();
Обратим особое внимание на ключевые особенности построения этого запроса и типичные ошибки:
ДокументРасчетовСКонтрагентом обязательно должно находиться в 5-м параметре виртуальной таблицы. Если вынести условие в блок ГДЕ, платформа сначала рассчитает остатки по всей таблице регистра за весь период, а затем применит фильтр, что приведет к фатальному падению производительности и ошибкам в расчете остатков.ДвиженияИГраницыПериода. Это гарантирует, что границы интервала будут включены в расчет.&КонецПериода значение через НачалоДня() (00:00:00), движения последнего дня не попадут в выборку. Следует использовать КонецДня().Теперь рассмотрим, как правильно обойти выборку. Если вызвать стандартный метод Выбрать() без параметров, результат вернет плоскую таблицу без дополненных дней. Для вывода дополненной шкалы времени необходимо обращаться к группировке с параметром "Все":
ВыборкаПериод = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Период", "Все");
ТекущийОстаток = 0;
Пока ВыборкаПериод.Следующий() Цикл
// Если на дату движений не было, агрегатная сумма вернет NULL или 0.
// Для сохранения остатка предыдущего дня выполняем проверку значения:
Если ВыборкаПериод.КонечныйОстаток <> NULL И ВыборкаПериод.КонечныйОстаток <> 0 Тогда
ТекущийОстаток = ВыборкаПериод.КонечныйОстаток;
КонецЕсли;
Сообщить("Дата: " + Формат(ВыборкаПериод.Период, "ДФ=dd.MM.yyyy") + " | Остаток: " + ТекущийОстаток);
КонецЦикла;
Выясним причину, почему требуется дополнительная переменная ТекущийОстаток: конструкция ПЕРИОДАМИ генерирует саму сетку дат, но функция СУММА() в итогах вычисляется только по фактически существующим записям. В дни без движений сумма остатка не протягивается автоматически, поэтому перенос значения на пустые дни выполняется программно при обходе.
Если требуется получить готовый результат без обхода итогов (например, для выгрузки в плоскую ТаблицаЗначений), можно сформировать календарную шкалу непосредственно в запросе и рассчитать остаток нарастающим итогом.
Разберем по шагам логику такого запроса:
ГрафикиРаботыПоВидамВремени, производственного календаря или генератором чисел).ОстаткиИОбороты.
ВЫБРАТЬ
Календарь.Дата КАК Период,
&НачальныйОстаток + ЕСТЬNULL(СУММА(ТаблицаОборотов.СуммаОборот), 0) КАК КонечныйОстаток
ИЗ
ВТ_КалендарныеДни КАК Календарь
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Обороты КАК ТаблицаОборотов
ПО ТаблицаОборотов.Период <= Календарь.Дата
СГРУППИРОВАТЬ ПО
Календарь.Дата
УПОРЯДОЧИТЬ ПО
Период
Этот подход исключает необходимость использования конструкции ИТОГИ и позволяет передавать результат напрямую в другие процедуры обработки данных.
Если в коде требуется получить данные без ручного протягивания остатков, оптимально применить Систему компоновки данных программно. В отличие от языка запросов, механизм СКД умеет самостоятельно рассчитывать переходящие остатки на дополненные периоды.
Рассмотрим порядок настройки схемы компоновки:
Период указывается роль Период с порядковым номером.Остаток (с указанием начального и конечного остатка).Период в свойствах выбирается Тип дополнения: День.ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений выгружается в обычную таблицу значений. При этом платформа автоматически заполнит все пропущенные даты корректными протянутыми суммами остатков.