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