При разработке сложных отчетов на системе компоновки данных (СКД) часто возникает необходимость использовать внешний набор данных в виде таблицы значений (ТЗ). Типичный сценарий: данные нельзя получить одним запросом, они требуют сложной программной обработки или обращения к специализированным функциям общих модулей (например, получение кадровых данных сотрудников в ЗУП). В этом случае формирование таблицы происходит в модуле объекта отчета. Основная трудность, с которой сталкиваются разработчики, — это передача даты или периода, выбранного пользователем на форме отчета, внутрь алгоритма формирования этой таблицы.
В данной статье мы разберем, как правильно перехватить управление процессом компоновки, извлечь значения параметров и передать их в ваши функции для формирования корректной выборки.
Для того чтобы подсунуть СКД свою таблицу значений, нам необходимо полностью взять управление процессом формирования отчета на себя. Это делается в предопределенной процедуре ПриКомпоновкеРезультата модуля объекта отчета. Рассмотрим базовый алгоритм, который мы будем использовать:
СтандартнаяОбработка в значение Ложь.Рассмотрим подробнее каждый из этих этапов, уделяя особое внимание программной работе с параметрами.
Когда пользователь нажимает кнопку "Сформировать", все изменения, которые он внес в параметры на форме (например, установил дату), попадают в КомпоновщикНастроек. Чтобы извлечь эти данные в коде модуля, мы используем объект НастройкиКомпоновкиДанных. Проанализируем два основных способа получения значения.
Способ 1: Использование метода НайтиЗначениеПараметра
Этот способ считается наиболее надежным, так как он позволяет обратиться к параметру по его идентификатору, определенному в схеме.
Настройки = КомпоновщикНастроек.ПолучитьНастройки();
ИмяПараметра = Новый ПараметрКомпоновкиДанных("ДатаОтчета");
ЗначениеПараметра = Настройки.ПараметрыДанных.НайтиЗначениеПараметра(ИмяПараметра);
Если ЗначениеПараметра <> Неопределено Тогда
ВыбраннаяДата = ЗначениеПараметра.Значение;
КонецЕсли;
Способ 2: Поиск в коллекции элементов
Мы можем напрямую обратиться к коллекции элементов параметров данных. Это удобно, если мы точно знаем имя параметра.
Настройки = КомпоновщикНастроек.ПолучитьНастройки();
ЭлементПараметра = Настройки.ParaметрыДанных.Элементы.Найти("НаДату");
Если ЭлементПараметра <> Неопределено Тогда
ВыбраннаяДата = ЭлементПараметра.Значение;
КонецЕсли;
Часто в СКД вместо простого типа Дата используется системный тип СтандартныйПериод. Это позволяет пользователю выбирать удобные интервалы (например, "Этот месяц" или "Прошлый год"). Если ваш параметр в схеме имеет тип СтандартныйПериод, то простого обращения к .Значение может быть недостаточно для передачи в функцию, ожидающую тип Дата.
Проанализируем ситуацию: если ваша функция (например, КадровыйУчет.КадровыеДанныеСотрудников) требует на вход конкретную дату, а в настройках лежит период, вам нужно решить, какую именно дату из интервала использовать — начало или конец. Рассмотрим пример извлечения даты из периода:
ПараметрПериод = Настройки.ПараметрыДанных.Элементы.Найти("ПериодОтчета").Значение;
// Если тип параметра - СтандартныйПериод
Если ТипЗнч(ПараметрПериод) = Тип("СтандартныйПериод") Тогда
ДатаДляФунции = ПараметрПериод.ДатаОкончания; // Или ДатаНачала
Иначе
ДатаДляФунции = ПараметрПериод; // Если это просто Дата
КонецЕсли;
Внимание: Если в коде вы встретите обращение вида Параметр.Значение.Дата, это может означать, что параметр является структурой или специфическим объектом. Всегда проверяйте типы данных в отладчике.
Соберем все части воедино. Допустим, у нас есть задача вывести список сотрудников и их подразделений на дату, выбранную пользователем. Рассмотрим реализацию процедуры ПриКомпоновкеРезультата:
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь; // Отключаем стандартный вывод
// 1. Получаем настройки пользователя
Настройки = КомпоновщикНастроек.ПолучитьНастройки();
// 2. Извлекаем дату из параметров СКД
ЭлементПараметра = Настройки.ПараметрыДанных.Элементы.Найти("НаДату");
Если ЭлементПараметра = Неопределено Или Не ЗначениеЗаполнено(ЭлементПараметра.Значение) Тогда
ВыбраннаяДата = ТекущаяДата(); // Значение по умолчанию
Иначе
ВыбраннаяДата = ЭлементПараметра.Значение;
// Если это период, берем дату окончания
Если ТипЗнч(ВыбраннаяДата) = Тип("СтандартныйПериод") Тогда
ВыбраннаяДата = ВыбраннаяДата.ДатаОкончания;
КонецЕсли;
КонецЕсли;
// 3. Подготовим таблицу значений (структура должна совпадать с Набором Данных в СКД)
ТЗИтог = Новый ТаблицаЗначений;
ТЗИтог.Колонки.Добавить("Сотрудник", Новый ОписаниеТипов("СправочникСсылка.Сотрудники"));
ТЗИтог.Колонки.Добавить("Подразделение", Новый ОписаниеТипов("СправочникСсылка.ПодразделенияОрганизаций"));
ТЗИтог.Колонки.Добавить("Должность", Новый ОписаниеТипов("СправочникСсылка.Должности"));
// 4. Получаем данные (пример для ЗУП 3.1)
СписокСотрудников = ПолучитьСписокСотрудников(); // Ваша функция получения массива ссылок
ИменаКадровыхДанных = "Подразделение, Должность";
ТаблицаКадровыхДанных = КадровыйУчет.КадровыеДанныеСотрудников(Истина, СписокСотрудников, ИменаКадровыхДанных, ВыбраннаяДата);
Для Каждого СтрокаДанных Из ТаблицаКадровыхДанных Цикл
НоваяСтрока = ТЗИтог.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаДанных);
КонецЦикла;
// 5. Связываем программную таблицу с внешним набором данных СКД
// Ключ "ВнешняяТЗ" должен в точности совпадать с именем объекта в схеме СКД
ВнешниеНаборыДанных = Новый Структура("ВнешняяТЗ", ТЗИтог);
// 6. Выполняем компоновку
ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки, Истина);
ДокументРезультат.Очистить();
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
КонецПроцедуры
При работе с внешними наборами данных важно помнить о типизации колонок таблицы значений. Если вы создадите колонки без указания ОписаниеТипов, СКД может воспринять данные как произвольные объекты. Это приведет к тому, что вы не сможете использовать стандартное оформление (например, формат даты), итоги по числовым полям или корректные группировки по ссылкам.
Разберем еще один важный момент: соответствие имен. Поле "Имя объекта" в наборе данных — это не имя таблицы в запросе, а ключ в структуре ВнешниеНаборыДанных. Если в схеме вы назвали набор данных "ТаблицаСотрудников", то и в коде структура должна выглядеть так: Новый Структура("ТаблицаСотрудников", ТЗИтог).
Также проанализируем ситуацию с производительностью. Если ваш отчет предполагает работу с тысячами сотрудников, старайтесь минимизировать количество вызовов функций в циклах. Как показано в примере выше, лучше получить все данные одним вызовом функции КадровыеДанныеСотрудников, передав ей массив ссылок, чем вызывать её для каждого сотрудника отдельно. Это значительно ускорит формирование отчета, особенно если данные выбираются на историческую дату с учетом среза последних.
Таким образом, мы выяснили, что передача параметров во внешнюю ТЗ сводится к правильному обращению к объекту настроек и внимательному контролю типов данных. Используя этот подход, вы сможете создавать гибкие и производительные отчеты любой сложности.