Как в отчете СКД вывести все дни периода с пустыми ячейками при отсутствии движений?

Программист 1С v8.3 (Обычные формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При построении аналитических отчетов в системе компоновки данных (СКД) часто возникает задача вывести непрерывный календарный график: каждый день заданного интервала должен присутствовать в колонках или строках отчета независимо от того, были ли в эту дату хозяйственные операции. Если движений не было, ячейка должна оставаться пустой.

Разберем типичную ситуацию: разработчик связывает регистр сведений РегламентированныйПроизводственныйКалендарь с таблицей движений через левое соединение, однако при формировании отчета дни без движений неожиданно исчезают. Проанализируем, почему это происходит и какими способами можно гарантировать вывод всех дней периода.

Почему исчезают дни без движений: разбор типичной ошибки

Посмотрим на классический запрос, который разработчики пытаются использовать для решения такой задачи:


ВЫБРАТЬ
    РегламентированныйПроизводственныйКалендарь.ДатаКалендаря КАК Период,
    ВзаиморасчетыСКонтрагентамиОстаткиИОбороты.Контрагент КАК Контрагент,
    СУММА(ВзаиморасчетыСКонтрагентамиОстаткиИОбороты.СуммаВзаиморасчетовПриход) КАК СуммаПриход
ИЗ
    РегистрСведений.РегламентированныйПроизводственныйКалендарь КАК РегламентированныйПроизводственныйКалендарь
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ВзаиморасчетыСКонтрагентами.ОстаткиИОбороты(&НП, &КП, День, , ) КАК ВзаиморасчетыСКонтрагентамиОстаткиИОбороты
        ПО РегламентированныйПроизводственныйКалендарь.ДатаКалендаря = ВзаиморасчетыСКонтрагентамиОстаткиИОбороты.Период
ГДЕ
    РегламентированныйПроизводственныйКалендарь.ДатаКалендаря МЕЖДУ &НП И &КП
СГРУППИРОВАТЬ ПО
    ВзаиморасчетыСКонтрагентамиОстаткиИОбороты.Контрагент,
    РегламентированныйПроизводственныйКалендарь.ДатаКалендаря

В теории левое соединение должно сохранить все даты производственного календаря, заполнив поля регистра значениями NULL при отсутствии записей. Однако на практике дни все равно пропадают. Выясним основные причины такого поведения:

  1. Пользовательский отбор по контрагенту. Если в настройках отчета на вкладке «Отбор» пользователь выбирает конкретного контрагента, СКД добавляет условие в общую секцию ГДЕ итогового запроса. Для дней, когда движений не было, поле Контрагент содержит NULL. Условие вида Контрагент = &ЗначениеОтбора отсекает все записи со значением NULL, фактически превращая левое соединение во внутреннее.
  2. Отсутствие аналитики в календаре. В календаре нет измерения Контрагент. При простом соединении для пустых дней строка группировки формируется с пустым контрагентом, а при построении матрицы «Контрагент в строках, Период в колонках» пустые даты не привязываются к конкретному партнеру.

Способ 1. Использование встроенного дополнения периодов в СКД

Самый эффективный и штатный метод в платформе 1С — вообще отказаться от производственного календаря в запросе и использовать возможности самой компоновки данных. СКД умеет автоматически генерировать недостающие даты.

Разберем по шагам, как настроить отчет штатным механизмом:

  1. Формируем простой запрос только к виртуальной таблице регистра накопления без всяких соединений:
    
    ВЫБРАТЬ
        ВзаиморасчетыСКонтрагентамиОстаткиИОбороты.Период КАК Период,
        ВзаиморасчетыСКонтрагентамиОстаткиИОбороты.Контрагент КАК Контрагент,
        ВзаиморасчетыСКонтрагентамиОстаткиИОбороты.СуммаВзаиморасчетовПриход КАК СуммаПриход
    ИЗ
        РегистрНакопления.ВзаиморасчетыСКонтрагентами.ОстаткиИОбороты(&НП, &КП, День, , ) КАК ВзаиморасчетыСКонтрагентамиОстаткиИОбороты
    
  2. Переходим на вкладку Наборы данных. Находим поле Период и нажимаем на колонку Роль.
  3. В открывшемся окне роли поля указываем признак Период, задаем порядковый номер (например, 1) и тип День.
  4. Переходим на вкладку Настройки в структуру отчета. Выбираем группировку колонок по полю Период.
  5. В нижней панели настроек группировки разворачиваем системные свойства и настраиваем параметры дополнения:
    • Тип дополнения: выбираем значение День;
    • Начальная дата: указываем параметр начала периода отчета (&НП);
    • Конечная дата: указываем параметр конца периода отчета (&КП).

При таком подходе СКД сама сгенерирует непрерывную цепочку дней между начальной и конечной датой, даже если за определенные сутки в регистре не было ни одной записи.

Способ 2. Корректное использование производственного календаря

Если специфика задачи требует обязательного использования производственного календаря (например, чтобы учитывать только рабочие дни или исключать праздники), необходимо правильно обработать отборы СКД, чтобы они не разрушали левое соединение.

Рассмотрим, как зафиксировать отбор внутри виртуальной таблицы с помощью расширения языка запросов компоновки данных:


ВЫБРАТЬ
    Календарь.ДатаКалендаря КАК Период,
    Взаиморасчеты.Контрагент КАК Контрагент,
    СУММА(Взаиморасчеты.СуммаВзаиморасчетовПриход) КАК СуммаПриход
ИЗ
    РегистрСведений.РегламентированныйПроизводственныйКалендарь КАК Календарь
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ВзаиморасчетыСКонтрагентами.ОстаткиИОбороты(
            &НП,
            &КП,
            День,
            ,
            {Контрагент.*}
        ) КАК Взаиморасчеты
        ПО Календарь.ДатаКалендаря = Взаиморасчеты.Период
ГДЕ
    Календарь.ДатаКалендаря МЕЖДУ &НП И &КП
СГРУППИРОВАТЬ ПО
    Календарь.ДатаКалендаря,
    Взаиморасчеты.Контрагент

Конструкция в фигурных скобках {Контрагент.*} указывает СКД, что пользовательский фильтр по контрагенту должен транслироваться непосредственно в параметры виртуальной таблицы регистра, а не в результирующее предложение ГДЕ. Благодаря этому левое соединение не ломается, и даты без движений остаются в выборке.

Если в отчете требуется вывести сетку «все выбранные контрагенты на каждый день», сформируем предварительное декартово произведение списка контрагентов и календаря:


ВЫБРАТЬ
    Календарь.ДатаКалендаря КАК Период,
    Контрагенты.Ссылка КАК Контрагент
ПОМЕСТИТЬ ВТ_Сетка
ИЗ
    РегистрСведений.РегламентированныйПроизводственныйКалендарь КАК Календарь
        КРОСС СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
ГДЕ
    Календарь.ДатаКалендаря МЕЖДУ &НП И &КП
    {Контрагенты.Ссылка.* КАК Контрагент}
;

ВЫБРАТЬ
    Сетка.Период КАК Период,
    Сетка.Контрагент КАК Контрагент,
    СУММА(Обороты.СуммаВзаиморасчетовПриход) КАК СуммаПриход
ИЗ
    ВТ_Сетка КАК Сетка
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ВзаиморасчетыСКонтрагентами.ОстаткиИОбороты(&НП, &КП, День, , ) КАК Обороты
        ПО Сетка.Период = Обороты.Период
        И Сетка.Контрагент = Обороты.Контрагент
СГРУППИРОВАТЬ ПО
    Сетка.Период,
    Сетка.Контрагент

Как вывести именно пустые ячейки вместо нулей

Чтобы ячейка дня без движений визуально оставалась пустой, обратим внимание на правила работы с числовыми ресурсами:

  1. Не используем функцию ЕСТЬNULL в запросе. Если написать ЕСТЬNULL(Сумма, 0), платформа выведет число 0. Если оставить исходное поле или применить агрегатную функцию СУММА(Сумма) к отсутствующим записям, вернется значение NULL, которое СКД отображает как пустую ячейку.
  2. Настройка формата поля. Если в отчете все же присутствуют нули, но их необходимо скрыть, перейдем на вкладку Настройки, откроем Условное оформление (или свойства поля во вкладке «Наборы данных») и зададим форматную строку. В параметре формата укажем свойство ЧН= (число, если ноль). Пустое значение после знака равенства обеспечит скрытие нулевых значений в отчете.
← На главную