Как в СКД развернуть данные регистра по дням месяца и вывести ответственных лиц

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке отчетов в 1С часто возникает задача представить данные в виде шахматки (матрицы или кросс-таблицы): по строкам выводятся аналитические разрезы (задачи, объекты, документы), а по колонкам — дни выбранного периода (с 1 по 31 число). В ячейках на пересечении требуется отобразить ответственное лицо, статус или отметку о выполнении. Задача усложняется, когда данные в регистре задаются не единичной датой, а интервалом (дата начала и дата окончания), либо когда в один и тот же день за одну задачу отвечает несколько сотрудников.

Разберем по шагам, как решить эту задачу средствами системы компоновки данных (СКД) без сложного программного кода.

Шаг 1. Формирование таблицы дат календаря

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

Способ 1. Использование производственного календаря конфигурации

В большинстве типовых конфигураций 1С (ERP, КА, УТ, ЗУП, БП) уже присутствует справочник или регистр сведений календаря. Самый простой и производительный вариант — выбрать даты из существующего регистра:


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

Способ 2. Генерация дат декартовым произведением цифр

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


ВЫБРАТЬ
    ДОБАВИТЬКДАТЕ(&НачалоПериода, ДЕНЬ, Числа.НомерДня) КАК ПериодДень
ПОМЕСТИТЬ ВТ_Календарь
ИЗ
    (ВЫБРАТЬ
        aa.a * 1000 + bb.b * 100 + cc.c * 10 + dd.d КАК НомерДня
    ИЗ
        (ВЫБРАТЬ 0 КАК a ОБЪЕДИНИТЬ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВЫБРАТЬ 3 ОБЪЕДИНИТЬ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВЫБРАТЬ 5 ОБЪЕДИНИТЬ ВЫБРАТЬ 6 ОБЪЕДИНИТЬ ВЫБРАТЬ 7 ОБЪЕДИНИТЬ ВЫБРАТЬ 8 ОБЪЕДИНИТЬ ВЫБРАТЬ 9) КАК aa
        ПОЛНОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ 0 КАК b ОБЪЕДИНИТЬ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВЫБРАТЬ 3 ОБЪЕДИНИТЬ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВЫБРАТЬ 5 ОБЪЕДИНИТЬ ВЫБРАТЬ 6 ОБЪЕДИНИТЬ ВЫБРАТЬ 7 ОБЪЕДИНИТЬ ВЫБРАТЬ 8 ОБЪЕДИНИТЬ ВЫБРАТЬ 9) КАК bb ПО (ИСТИНА)
        ПОЛНОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ 0 КАК c ОБЪЕДИНИТЬ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВЫБРАТЬ 3 ОБЪЕДИНИТЬ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВЫБРАТЬ 5 ОБЪЕДИНИТЬ ВЫБРАТЬ 6 ОБЪЕДИНИТЬ ВЫБРАТЬ 7 ОБЪЕДИНИТЬ ВЫБРАТЬ 8 ОБЪЕДИНИТЬ ВЫБРАТЬ 9) КАК cc ПО (ИСТИНА)
        ПОЛНОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ 0 КАК d ОБЪЕДИНИТЬ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВЫБРАТЬ 3 ОБЪЕДИНИТЬ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВЫБРАТЬ 5 ОБЪЕДИНИТЬ ВЫБРАТЬ 6 ОБЪЕДИНИТЬ ВЫБРАТЬ 7 ОБЪЕДИНИТЬ ВЫБРАТЬ 8 ОБЪЕДИНИТЬ ВЫБРАТЬ 9) КАК dd ПО (ИСТИНА)) КАК Числа
ГДЕ
    Числа.НомерДня <= РАЗНОСТЬДАТ(&НачалоПериода, &КонецПериода, ДЕНЬ)
;

Шаг 2. Соединение данных регистра с календарем

Рассмотрим случай, когда период ответственности задан двумя полями: ДатаНачала и ДатаОкончания. Нам нужно, чтобы запись регистра отображалась в каждой колонке дня, попадающего в указанный диапазон.

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


ВЫБРАТЬ
    Регистр.Измерение1 КАК Измерение1,
    Регистр.Измерение2 КАК Измерение2,
    Регистр.Ответственный КАК Ответственный,
    НАЧАЛОПЕРИОДА(Регистр.ДатаНачала, ДЕНЬ) КАК ДатаНачала,
    ВЫБОР
        КОГДА Регистр.ДатаОкончания = ДАТАВРЕМЯ(1, 1, 1) ИЛИ Регистр.ДатаОкончания ЕСТЬ NULL
            ТОГДА &КонецПериода
        ИНАЧЕ НАЧАЛОПЕРИОДА(Регистр.ДатаОкончания, ДЕНЬ)
    КОНЕЦ КАК ДатаОкончания
ПОМЕСТИТЬ ВТ_ДанныеРегистра
ИЗ
    РегистрСведений.ОтветственныеПоЗадачам КАК Регистр
ГДЕ
    Регистр.ДатаНачала <= &КонецПериода
    И (Регистр.ДатаОкончания >= &НачалоПериода ИЛИ Регистр.ДатаОкончания = ДАТАВРЕМЯ(1, 1, 1))
;

ВЫБРАТЬ
    Календарь.ПериодДень КАК ПериодДень,
    Данные.Измерение1 КАК Измерение1,
    Данные.Измерение2 КАК Измерение2,
    Данные.Ответственный КАК Ответственный
ИЗ
    ВТ_Календарь КАК Календарь
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ДанныеРегистра КАК Данные
            ПО Календарь.ПериодДень >= Данные.ДатаНачала
            И Календарь.ПериодДень <= Данные.ДатаОкончания

Важные нюансы запроса:

  1. Функция НАЧАЛОПЕРИОДА(..., ДЕНЬ) отсекает время, чтобы сравнение дат производилось корректно по суткам.
  2. Пустая дата окончания (ДАТАВРЕМЯ(1, 1, 1)) обрабатывается выражением ВЫБОР КОГДА. Это гарантирует, что продолжающиеся задачи не потеряются при формировании отчета.

Шаг 3. Агрегация нескольких ответственных в ресурсах СКД

Если на одну дату и одну задачу приходится несколько ответственных сотрудников, стандартные функции МАКСИМУМ() или МИНИМУМ() не подходят, так как они выведут только одного человека по алфавиту.

В СКД есть встроенная функция для объединения строк. Перейдем на вкладку «Ресурсы» схемы компоновки данных и добавим ресурс Ответственный со следующим выражением:

СоединитьСтроки(Массив(Различные Ответственный), Символы.ПС)

Разберем, как работает это выражение:

  1. Конструкция Массив(Различные Ответственный) собирает всех ответственных в текущей группировке в единую коллекцию, автоматически исключая повторяющиеся записи.
  2. Функция СоединитьСтроки(...) объединяет элементы массива через заданный разделитель. В качестве разделителя можно указать Символы.ПС (перенос строки, чтобы сотрудники выводились друг под другом) или обычную запятую с пробелом: ", ".

Шаг 4. Настройка структуры отчета и форматирование колонок

Для вывода сетки настроим структуру отчета на вкладке «Настройки»:

  1. Добавим группировку верхнего уровня типа «Таблица».
  2. В раздел «Строки» добавим группировки по аналитическим разрезам (например, Измерение1, затем Измерение2).
  3. В раздел «Колонки» добавим группировку по полю ПериодДень.
  4. В «Выбранные поля» таблицы добавим наш ресурс Ответственный.

Чтобы в шапке отчета вместо полного представления даты (например, «01.09.2023 00:00:00») выводился только номер дня месяца или короткая дата, перейдем на вкладку «Наборы данных» (или вкладку «Условное оформление») и для поля ПериодДень укажем в параметре «Оформление» строку формата: ДФ=dd (только число) или ДФ=dd.MM (число и месяц).

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

← На главную