В процессе разработки отчетов на базе Системы компоновки данных (СКД) часто возникает задача динамического изменения структуры отчета или текста запроса в зависимости от условий или выбора пользователя. Это особенно актуально, когда необходимо сохранить типовую конфигурацию без изменений, но при этом внедрить сложную логику, которую невозможно реализовать только через пользовательские настройки. В данной статье мы разберем, как программно модифицировать схему, изменять запросы и управлять вариантами отчета.
Самый очевидный и быстрый способ изменить поведение отчета — это прямая модификация текста запроса в наборе данных. Это полезно, когда нам нужно заменить одно поле на сложное выражение ВЫБОР или изменить таблицу-источник. Рассмотрим, как это сделать в модуле отчета внутри процедуры ПриКомпоновкеРезультата.
Проанализируем пример, в котором нам нужно заменить выбор склада на выбор его вышестоящего родителя (второго уровня иерархии), если таковой имеется. Для этого мы обратимся к схеме компоновки и программно заменим подстроку в запросе:
Процедура ПриКомпоновкеРезультата(ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь; // Отключаем стандартный вывод
// Получаем схему из макета
СхемаСКД = ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
// Находим нужный набор данных (запрос)
НаборДанных = СхемаСКД.НаборыДанных.НаборДанных1;
// Текст замены со сложной логикой ВЫБОР
НовоеВыражение = "ВЫБОР
| КОГДА РеализацияТоваровУслуг.Склад.Родитель.Родитель <> ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка)
| ТОГДА РеализацияТоваровУслуг.Склад.Родитель.Родитель
| ИНАЧЕ ВЫБОР
| КОГДА РеализацияТоваровУслуг.Склад.Родитель <> ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка)
| ТОГДА РеализацияТоваровУслуг.Склад.Родитель
| ИНАЧЕ РеализацияТоваровУслуг.Склад
| КОНЕЦ
|КОНЕЦ";
// Заменяем стандартное поле в тексте запроса
НаборДанных.Запрос = СтрЗаменить(НаборДанных.Запрос, "РеализацияТоваровУслуг.Склад", НовоеВыражение);
// Далее выполняем стандартную компоновку с новой схемой
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаСКД, КомпоновщикНастроек.ПолучитьНастройки(), ДанныеРасшифровки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(Результат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
КонецПроцедуры
Если стоит задача не трогать типовую СКД, чтобы не нарушать поддержку конфигурации, мы должны использовать метод создания копии объекта схемы. Выясним причину, почему это важно: любые изменения объекта СхемаКомпоновкиДанных, полученного напрямую из метаданных, могут сохраниться в сеансе, что приведет к непредсказуемому поведению отчета при последующих открытиях.
Посмотрим на алгоритм безопасной модификации:
ПолучитьМакет.КомпоновщикМакета.Выполнить().Важный нюанс: при таком подходе КомпоновщикНастроек продолжает работать с оригинальной схемой (которая в метаданных). Чтобы настройки пользователя (отборы, группировки) корректно наложились на измененный запрос, имена полей и их псевдонимы в новом запросе должны строго совпадать с именами в исходной схеме.
Вместо того чтобы «кромсать» текст запроса функциями СтрЗаменить, гораздо элегантнее использовать параметры СКД. Это позволяет заложить всю логику выбора данных непосредственно в текст запроса, переключая режимы через параметры, которые могут быть скрыты от пользователя.
Разберем пример запроса, который поддерживает два режима работы:
ВЫБРАТЬ
ВЫБОР
КОГДА &ИспользоватьИерархиюСкладов ТОГДА
ВЫБОР
КОГДА Склады.Родитель.Родитель <> ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка) ТОГДА Склады.Родитель.Родитель
ИНАЧЕ Склады.Родитель
КОНЕЦ
ИНАЧЕ Склады.Ссылка
КОНЕЦ КАК Склад
ИЗ
Справочник.Склады КАК Склады
В коде ПриКомпоновкеРезультата нам остается только установить значение параметра &ИспользоватьИерархиюСкладов в зависимости от внешних условий или текущего варианта отчета.
Часто программисту нужно знать, какой именно вариант настроек выбрал пользователь, чтобы применить специфическую логику изменения запроса. Выясним, как это определить программно.
В управляемом приложении ключ текущего варианта можно получить через свойство КлючТекущегоВарианта. Однако в процедуре ПриКомпоновкеРезультата на сервере этот ключ напрямую недоступен. Чтобы обойти это ограничение, рассмотрим следующий прием:
В форме отчета, перед формированием, мы можем поместить имя варианта в ДополнительныеСвойства компоновщика настроек:
// В форме отчета
&НаКлиенте
Процедура СформироватьПосле(Результат)
КомпоновщикНастроек.Настройки.ДополнительныеСвойства.Вставить("ИмяВарианта", ЭтаФорма.Отчет.ТекущийВариант);
КонецПроцедуры
Затем в модуле объекта мы легко считываем это значение:
ИмяВарианта = "";
Если КомпоновщикНастроек.Настройки.ДополнительныеСвойства.Свойство("ИмяВарианта", ИмяВарианта) Тогда
Если ИмяВарианта = "ВариантСИерархией" Тогда
// Применяем логику изменения структуры
КонецЕсли;
КонецЕсли;
Если логика преобразования данных (например, получение родителя склада) не требует изменения секции ГДЕ или СОЕДИНЕНИЯ, то лучшим решением будет использование вычисляемых полей. Это позволяет оставить текст SQL-запроса максимально простым и читаемым.
Рассмотрим, как программно добавить вычисляемое поле в схему:
НовоеПоле = СхемаСКД.ВычисляемыеПоля.Добавить();
НовоеПоле.ПутьКДанным = "СкладВерхнегоУровня";
НовоеПоле.Выражение = "ВЫБОР КОГДА Склад.Родитель.Родитель.Ссылка ЕСТЬ NULL ТОГДА Склад.Родитель ИНАЧЕ Склад.Родитель.Родитель КОНЕЦ";
НовоеПоле.Заголовок = "Склад (укрупненно)";
После добавления такого поля программно, мы можем использовать его в группировках или выбранных полях, просто обращаясь к нему по имени СкладВерхнегоУровня. Это избавляет от необходимости манипулировать текстом запроса и снижает риск возникновения синтаксических ошибок.
В особо сложных случаях может потребоваться не просто изменить текст запроса, а полностью перестроить состав доступных полей набора данных. Например, если в новом запросе появились поля, которых не было в оригинале.
Проанализируем ситуацию: вы изменили текст запроса через СтрЗаменить и добавили туда новое поле РеализацияТоваровУслуг.Контрагент. Если этого поля не было в схеме в режиме конфигуратора, СКД о нем «не узнает». Вам необходимо программно добавить его в коллекцию Поля набора данных:
ПолеКонтрагент = НаборДанных.Поля.Добавить(Тип("ПолеНабораДанныхСхемыКомпоновкиДанных"));
ПолеКонтрагент.Поле = "Контрагент";
ПолеКонтрагент.ПутьКДанным = "Контрагент";
ПолеКонтрагент.Заголовок = "Клиент";
Помните: после ручного изменения полей набора данных крайне желательно проверить роли полей (например, является ли поле измерением или периодом), иначе итоги в отчете могут рассчитываться некорректно.
Объект СхемаКомпоновкиДанных доступен только на сервере. Если вам требуется изменить схему на основе данных, которые есть только на клиенте, вы должны передать их на сервер (например, через параметры процедуры), выполнить модификацию и там же произвести компоновку результата. Напрямую вернуть измененный объект схемы с сервера на клиент без сериализации в XML невозможно, поэтому старайтесь выполнять всю цепочку действий (от модификации до вывода) в одном серверном вызове в контексте ПриКомпоновкеРезультата.
Используя предложенные методы, вы сможете создавать гибкие и динамичные отчеты, сохраняя при этом чистоту кода и типовую структуру конфигурации.