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