Довольно часто при разработке отчетов в 1С возникает задача сравнить продажи или другие показатели контрагента по дням за разные периоды (например, декабрь прошлого года и январь текущего). Если мы делаем простой запрос к виртуальной таблице оборотов регистра, мы получаем данные только за те дни, когда совершались хозяйственные операции. Проанализируем ситуацию: в итоговой таблице или диаграмме СКД «выпадают» дни без продаж. Например, если 1 января продаж не было, а 1 декабря были, ось графика смещается или обрывается, и сравнить показатели наглядно становится невозможно.
Разберем по шагам, как сделать так, чтобы в отчете выводились абсолютно все числа месяца, даже если обороты в эти даты отсутствовали. Мы рассмотрим несколько подходов: от прямого соединения с календарем до использования продвинутых встроенных механизмов Системы Компоновки Данных (СКД).
Выясним причину отсутствия дат — виртуальная таблица Обороты возвращает только записи с ненулевыми оборотами. Чтобы получить непрерывную шкалу времени, нам нужна эталонная таблица дат. Чаще всего для этих целей используют регистр сведений ДанныеПроизводственногоКалендаря.
Рассмотрим подробнее логику такого запроса. Сначала мы формируем временную таблицу с продажами, затем временную таблицу со всеми датами из календаря за нужный период. После этого применяем левое соединение (эталонная таблица дат слева, продажи — справа). Посмотрим на пример рабочего кода, к которому пришли в ходе обсуждения:
ВЫБРАТЬ
НАЧАЛОПЕРИОДА(ВыручкаИСебестоимостьПродажОбороты.Период, ДЕНЬ) КАК ПериодДень,
ВыручкаИСебестоимостьПродажОбороты.КоличествоОборот КАК КолДекабрь,
0 КАК КолЯнварь
ПОМЕСТИТЬ ТабПродаж
ИЗ
РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(&Нач1, &Нач2, День, ) КАК ВыручкаИСебестоимостьПродажОбороты
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
НАЧАЛОПЕРИОДА(ВыручкаИСебестоимостьПродажОбороты.Период, ДЕНЬ),
0,
ВыручкаИСебестоимостьПродажОбороты.КоличествоОборот
ИЗ
РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(&Нач3, &Нач4, День, ) КАК ВыручкаИСебестоимостьПродажОбороты
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ДанныеПроизводственногоКалендаря.Дата КАК Дата
ПОМЕСТИТЬ ТабДат
ИЗ
РегистрСведений.ДанныеПроизводственногоКалендаря КАК ДанныеПроизводственногоКалендаря
ГДЕ
ДанныеПроизводственногоКалендаря.Дата >= &НачДекабря
И ДанныеПроизводственногоКалендаря.Дата <= &КонЯнв
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ТабПродаж.КолДекабрь КАК КолДекабрь,
Выразить(ТабПродаж.КолЯнварь КАК Число) КАК КолЯнварь,
ТабДат.Дата КАК НомерДня
ПОМЕСТИТЬ Т1
ИЗ
ТабДат КАК ТабДат
ЛЕВОЕ СОЕДИНЕНИЕ ТабПродаж КАК ТабПродаж
ПО ТабДат.Дата = ТабПродаж.ПериодДень
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
СУММА(Т1.КолДекабрь) КАК КолДекабрь,
СУММА(Т1.КолЯнварь) КАК КолЯнварь,
ДЕНЬ(Т1.НомерДня) КАК НомерДня
ИЗ
Т1 КАК Т1
СГРУППИРОВАТЬ ПО
ДЕНЬ(Т1.НомерДня)
Важное замечание: При левом соединении для дат, в которых не было продаж, значения колонок из таблицы продаж будут равны NULL. В коде выше автор пытался обойти это конструкцией ВЫРАЗИТЬ, но методически правильным подходом является использование функции ЕСТЬNULL. Обязательно оборачивайте числовые показатели, например: ЕСТЬNULL(ТабПродаж.КолДекабрь, 0), иначе при расчете итогов в СКД могут возникнуть пустые значения.
Проанализируем архитектуру первого способа. Использовать жесткое кодирование колонок КолДекабрь и КолЯнварь через ОБЪЕДИНИТЬ ВСЕ — это плохая практика (антипаттерн) при работе с СКД. Отчет становится негибким: если завтра потребуется сравнить не два месяца, а три или целый квартал, запрос придется полностью переписывать.
Разберем по шагам, как решить задачу изящнее инструментами самой Системы Компоновки Данных. Нам не нужно объединение и не нужна таблица календаря. Достаточно получить общие данные за весь сводный период.
Партнер, КоличествоОборот и Период.ПериодДень) откройте параметры колонки Роль и установите настройку Дополнение в значение День (также укажите начальную и конечную дату).Благодаря настройке "Дополнение периода", СКД сама сгенерирует недостающие даты и выведет их на диаграмму или в таблицу, подставив нули в ресурсы. При этом СКД автоматически разделит общую колонку с количеством на две разные линии (декабрь и январь), формируя нужный визуал.
Посмотрим на пример, когда стоит задача сравнить периоды с разным количеством дней (например, февраль и март) или текущий месяц с аналогичным месяцем прошлого года. В таком случае простой группировки по номеру дня месяца может быть недостаточно для точного наложения графиков друг на друга.
Рассмотрим подробнее использование функций 1С для работы с датами в запросе. Чтобы искусственно «сдвинуть» прошлый период на одну ось с текущим, вы можете использовать функцию ДОБАВИТЬКДАТЕ (например, прибавить ровно 1 год к датам прошлого года) или РАЗНОСТЬДАТ для вычисления порядкового номера дня с начала исследуемого интервала. Это позволит вывести обе серии на одной оси координат без "разрывов" на диаграмме.
Используя правильную группировку в СКД и встроенные механизмы дополнения периодов, вы не только сократите объем программного кода в разы, но и создадите универсальный отчет, который будет работать корректно при любых выбранных интервалах.