Как в 1С сформировать отчет с динамическим числом колонок на основе произвольной таблицы данных

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

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

Постановка задачи и анализ ситуации

Рассмотрим ситуацию: у нас есть данные о заказах (Курьер, Плановое время, Фактическое время) и есть некоторая таблица штрафов, которая определяет размер взыскания в зависимости от минут опоздания. Сложность заключается в том, что интервалы штрафов (например, "от 0 до 3 мин", "от 3 до 15 мин") не являются фиксированными и могут быть изменены пользователем. Нам необходимо вывести таблицу, где строками будут курьеры, а колонками — сами интервалы штрафов, причем количество этих колонок должно подстраиваться под текущие настройки.

Проанализируем основные проблемы, с которыми сталкивается разработчик:

  1. Отсутствие связи в запросе: Если таблица штрафов хранится не в справочнике, а в ХранилищеОбщихНастроек, ее невозможно просто «соединить» в тексте запроса.
  2. Динамизм колонок: Стандартный список полей в СКД обычно жестко задан на этапе проектирования.
  3. Связь наборов данных: При использовании «Набора данных - объект» возникают сложности с сопоставлением данных по условиям (вхождение числа в интервал), так как стандартные связи СКД работают только по равенству полей.

Метод 1. Программная подготовка «плоской» таблицы значений

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

Разберем алгоритм действий по шагам:

Шаг 1. Получение основных данных. Сначала мы выберем все заказы курьеров за нужный период с помощью обычного запроса. Рассчитаем время опоздания в минутах непосредственно в запросе или при обходе результата.

Шаг 2. Получение настроек штрафов. Извлечем таблицу значений штрафов из ХранилищеОбщихНастроек или другого источника.

Шаг 3. Сопоставление. Создадим новую ТаблицаЗначений, которая станет источником для отчета. В ней должны быть поля: Курьер, ИнтервалНаименование, СуммаШтрафа, КоличествоОпозданий.

Проанализируем пример кода для обработки данных:


// Допустим, ТЗ_Заказы содержит данные о курьерах и их опозданиях
// ТЗ_Штрафы содержит колонки ОпозданиеС, ОпозданиеПо, Штраф, ПредставлениеИнтервала

ТаблицаРезультат = Новый ТаблицаЗначений;
ТаблицаРезультат.Колонки.Добавить("Курьер");
ТаблицаРезультат.Колонки.Добавить("Интервал");
ТаблицаРезультат.Колонки.Добавить("Сумма");

Для Каждого СтрокаЗаказа Из ТЗ_Заказы Цикл
    МинОпоздания = (СтрокаЗаказа.ВремяФакт - СтрокаЗаказа.ВремяПлан) / 60;
    
    Если МинОпоздания <= 0 Тогда
        Продолжить; // Нет опоздания
    КонецЕсли;
    
    // Ищем подходящий штраф
    Для Каждого СтрокаШтрафа Из ТЗ_Штрафы Цикл
        Если МинОпоздания > СтрокаШтрафа.ОпозданиеС И МинОпоздания <= СтрокаШтрафа.ОпозданиеПо Тогда
            НоваяСтрока = ТаблицаРезультат.Добавить();
            НоваяСтрока.Курьер = СтрокаЗаказа.Курьер;
            НоваяСтрока.Интервал = СтрокаШтрафа.ПредставлениеИнтервала;
            НоваяСтрока.Сумма = СтрокаШтрафа.Штраф;
            Прервать;
        КонецЕсли;
    КонецЦикла;
КонецЦикла;

В результате мы получили «плоскую» таблицу. Даже если у курьера было 10 опозданий в разные интервалы, в этой таблице будет 10 строк. Это именно то, что нужно для СКД.

Метод 2. Использование инструмента «Таблица» в настройках СКД

Теперь выясним, как вывести эту таблицу так, чтобы интервалы стали колонками. В СКД для этого существует специальный тип группировки — Таблица.

Выполним следующие настройки в схеме компоновки данных:

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

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

Метод 3. Связь наборов данных через вычисляемые ключи

Рассмотрим ситуацию, когда мы все же хотим использовать Набор данных - запрос для заказов и Набор данных - объект для штрафов внутри СКД. Основная проблема — как их связать, если условие связи не Равно, а Между?

Проанализируем технический прием с использованием «фиктивного ключа»:

В обоих наборах данных можно создать вычисляемое поле (или поле в запросе) с фиксированным значением, например, КлючСвязи = 1. Соединив наборы по этому полю, мы получим декартово произведение строк (каждый заказ соединится со всеми строками штрафов). После этого в СКД можно применить Отбор на уровне детальных записей или группировок: ОпозданиеМин > ОпозданиеС И ОпозданиеМин <= ОпозданиеПо.

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

Особенности хранения настроек в ХранилищеОбщихНастроек

Если вы решили хранить таблицу штрафов в ХранилищеОбщихНастроек, помните о рисках. Настройки в этом хранилище часто привязаны к конкретному пользователю или объекту. Если отчет будет формировать другой сотрудник, он может не увидеть данных, если вы не реализовали механизм «общих» или «системных» настроек.

Рассмотрим рекомендацию: для глобальных бизнес-правил (каковыми являются штрафы) лучше использовать Регистр сведений. Это позволит:

Программный вывод отчета

Поскольку мы используем «Набор данных - объект», отчет необходимо формировать программно. Рассмотрим пример модуля объекта отчета:


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    
    // 1. Получаем данные (наша подготовленная плоская таблица)
    ТаблицаДанных = ПолучитьПодготовленнуюТаблицуШтрафов();
    
    // 2. Формируем структуру внешних наборов данных
    ВнешниеНаборыДанных = Новый Структура;
    ВнешниеНаборыДанных.Вставить("НаборДанныхОбъект", ТаблицаДанных);
    
    // 3. Инициализируем компоновщик макета
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.ПолучитьНастройки(), ДанныеРасшифровки);
    
    // 4. Осуществляем компоновку
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки, Истина);
    
    // 5. Выводим результат в табличный документ
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
    ПроцессорВывода.Вывести(ПроцессорКомпоновки);
    
КонецПроцедуры

Подведение итогов

Для реализации динамических колонок в 1С лучше всего подходит использование Группировки в колонках таблицы СКД. Основная работа разработчика при этом заключается в трансформации исходных данных в «плоский» вид, где для каждой будущей ячейки отчета создается отдельная строка с указанием ее координат (Курьер и Интервал).

Использование Набора данных - объект позволяет гибко подключать любые внешние источники, будь то таблицы значений, файлы Excel или данные из настроек, обходя ограничения языка запросов SQL в части динамического формирования структуры результирующей таблицы.

← На главную