В практике разработки на платформе 1С часто возникает задача, когда структура отчета заранее не определена и зависит от данных, хранящихся в базе или настройках. Типичный пример — отчет по штрафам курьеров, где количество колонок (интервалов опоздания) может меняться. В этой статье мы разберем, как реализовать подобный функционал, используя возможности Системы Компоновки Данных (СКД) и программную подготовку данных.
Рассмотрим ситуацию: у нас есть данные о заказах (Курьер, Плановое время, Фактическое время) и есть некоторая таблица штрафов, которая определяет размер взыскания в зависимости от минут опоздания. Сложность заключается в том, что интервалы штрафов (например, "от 0 до 3 мин", "от 3 до 15 мин") не являются фиксированными и могут быть изменены пользователем. Нам необходимо вывести таблицу, где строками будут курьеры, а колонками — сами интервалы штрафов, причем количество этих колонок должно подстраиваться под текущие настройки.
Проанализируем основные проблемы, с которыми сталкивается разработчик:
ХранилищеОбщихНастроек, ее невозможно просто «соединить» в тексте запроса.Наиболее надежный и гибкий способ — это подготовка данных в коде на языке 1С перед их передачей в СКД. Мы не будем пытаться заставить СКД сопоставлять интервалы, а сделаем это сами в цикле.
Разберем алгоритм действий по шагам:
Шаг 1. Получение основных данных. Сначала мы выберем все заказы курьеров за нужный период с помощью обычного запроса. Рассчитаем время опоздания в минутах непосредственно в запросе или при обходе результата.
Шаг 2. Получение настроек штрафов. Извлечем таблицу значений штрафов из ХранилищеОбщихНастроек или другого источника.
Шаг 3. Сопоставление. Создадим новую ТаблицаЗначений, которая станет источником для отчета. В ней должны быть поля: Курьер, ИнтервалНаименование, СуммаШтрафа, КоличествоОпозданий.
Проанализируем пример кода для обработки данных:
// Допустим, ТЗ_Заказы содержит данные о курьерах и их опозданиях
// ТЗ_Штрафы содержит колонки ОпозданиеС, ОпозданиеПо, Штраф, ПредставлениеИнтервала
ТаблицаРезультат = Новый ТаблицаЗначений;
ТаблицаРезультат.Колонки.Добавить("Курьер");
ТаблицаРезультат.Колонки.Добавить("Интервал");
ТаблицаРезультат.Колонки.Добавить("Сумма");
Для Каждого СтрокаЗаказа Из ТЗ_Заказы Цикл
МинОпоздания = (СтрокаЗаказа.ВремяФакт - СтрокаЗаказа.ВремяПлан) / 60;
Если МинОпоздания <= 0 Тогда
Продолжить; // Нет опоздания
КонецЕсли;
// Ищем подходящий штраф
Для Каждого СтрокаШтрафа Из ТЗ_Штрафы Цикл
Если МинОпоздания > СтрокаШтрафа.ОпозданиеС И МинОпоздания <= СтрокаШтрафа.ОпозданиеПо Тогда
НоваяСтрока = ТаблицаРезультат.Добавить();
НоваяСтрока.Курьер = СтрокаЗаказа.Курьер;
НоваяСтрока.Интервал = СтрокаШтрафа.ПредставлениеИнтервала;
НоваяСтрока.Сумма = СтрокаШтрафа.Штраф;
Прервать;
КонецЕсли;
КонецЦикла;
КонецЦикла;
В результате мы получили «плоскую» таблицу. Даже если у курьера было 10 опозданий в разные интервалы, в этой таблице будет 10 строк. Это именно то, что нужно для СКД.
Теперь выясним, как вывести эту таблицу так, чтобы интервалы стали колонками. В СКД для этого существует специальный тип группировки — Таблица.
Выполним следующие настройки в схеме компоновки данных:
НаборДанныхОбъект и добавим в него поля Курьер, Интервал, Сумма.Курьер.Интервал.Сумма (не забудьте определить его как ресурс на вкладке «Ресурсы» с функцией Сумма(Сумма)).При таком подходе СКД сама проанализирует все уникальные значения в поле Интервал и создаст для каждого из них отдельную колонку. Если завтра в настройках появится новый штраф "свыше 60 минут", он автоматически появится в отчете новой колонкой без изменения программного кода или схемы.
Рассмотрим ситуацию, когда мы все же хотим использовать Набор данных - запрос для заказов и Набор данных - объект для штрафов внутри СКД. Основная проблема — как их связать, если условие связи не Равно, а Между?
Проанализируем технический прием с использованием «фиктивного ключа»:
В обоих наборах данных можно создать вычисляемое поле (или поле в запросе) с фиксированным значением, например, КлючСвязи = 1. Соединив наборы по этому полю, мы получим декартово произведение строк (каждый заказ соединится со всеми строками штрафов). После этого в СКД можно применить Отбор на уровне детальных записей или группировок: ОпозданиеМин > ОпозданиеС И ОпозданиеМин <= ОпозданиеПо.
Важное примечание: Этот метод менее производителен на больших объемах данных, так как требует перебора всех комбинаций строк. Программная подготовка таблицы (Метод 1) обычно работает быстрее.
Если вы решили хранить таблицу штрафов в ХранилищеОбщихНастроек, помните о рисках. Настройки в этом хранилище часто привязаны к конкретному пользователю или объекту. Если отчет будет формировать другой сотрудник, он может не увидеть данных, если вы не реализовали механизм «общих» или «системных» настроек.
Рассмотрим рекомендацию: для глобальных бизнес-правил (каковыми являются штрафы) лучше использовать Регистр сведений. Это позволит:
Поскольку мы используем «Набор данных - объект», отчет необходимо формировать программно. Рассмотрим пример модуля объекта отчета:
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
// 1. Получаем данные (наша подготовленная плоская таблица)
ТаблицаДанных = ПолучитьПодготовленнуюТаблицуШтрафов();
// 2. Формируем структуру внешних наборов данных
ВнешниеНаборыДанных = Новый Структура;
ВнешниеНаборыДанных.Вставить("НаборДанныхОбъект", ТаблицаДанных);
// 3. Инициализируем компоновщик макета
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.ПолучитьНастройки(), ДанныеРасшифровки);
// 4. Осуществляем компоновку
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки, Истина);
// 5. Выводим результат в табличный документ
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
КонецПроцедуры
Для реализации динамических колонок в 1С лучше всего подходит использование Группировки в колонках таблицы СКД. Основная работа разработчика при этом заключается в трансформации исходных данных в «плоский» вид, где для каждой будущей ячейки отчета создается отдельная строка с указанием ее координат (Курьер и Интервал).
Использование Набора данных - объект позволяет гибко подключать любые внешние источники, будь то таблицы значений, файлы Excel или данные из настроек, обходя ограничения языка запросов SQL в части динамического формирования структуры результирующей таблицы.