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