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