Каталог решений - Динамическое добавление полей и ресурсов в отчет на СКД

Динамическое добавление полей и ресурсов в отчет на СКД

Динамическое добавление полей и ресурсов в отчет на СКД

В наличии

Добавляем поля в отчет на СКД при формировании. Используем объекты системы компоновки данных и схему запроса.

Категория:

Описание

Предисловие

Недавно у меня возникла необходимость во-первых, формировать отчет программно, во-вторых, добавлять в него колонки по итогам получения данных.  Наверняка для многих не составляет сложности сформировать такой отчет, да к тому же многие потребности покрываются возможностью выводить группируемые поля в колонки (мне не подошло). Несмотря на многолетний опыт, с такой задачей столкнулся впервые, поэтому покажу как делал. К сожалению, осмысленного примера не придумал, поэтому будет такой, сверху донизу искусственный.

Если видите ошибки, или как сделать лучше — обязательно пишите комментарии.

 

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

Написать отчет, который выводит для заданного диапазона целых чисел их степени от 2 до указанной.

 

Архитектура решения

Будем делать внешний отчет (для конфигурации на базе БСП) с единственным набором данных — запрос (не потому что это нужно, а потому что это я хочу показать)

 

Реализация

Предварительная настройка

Собственно, создаем внешний отчет, добавляем в него положенные области, чтобы потомки радовались.

Добавляем сведения, чтобы отчет работал в БСП:

#Область ПрограммныйИнтерфейс

Функция СведенияОВнешнейОбработке() Экспорт
	
	ВерсияБСП = СтандартныеПодсистемыСервер.ВерсияБиблиотеки();
	ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке(ВерсияБСП);
	ПараметрыРегистрации.Информация = Метаданные().Синоним;
	ПараметрыРегистрации.Вид = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиДополнительныйОтчет();
	ПараметрыРегистрации.БезопасныйРежим = Ложь;
	ПараметрыРегистрации.Версия = "2025.06.10";
	
	Команда = ПараметрыРегистрации.Команды.Добавить();
	Команда.Представление = ПараметрыРегистрации.Информация;
	Команда.Идентификатор = Метаданные().Имя;
	Команда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыОткрытиеФормы();
	Команда.ПоказыватьОповещение = Истина;
	
	Возврат ПараметрыРегистрации;
	
КонецФункции

Процедура ОпределитьНастройкиФормы(Форма, КлючВарианта, Настройки) Экспорт
КонецПроцедуры
#КонецОбласти

Процедура ОпределитьНастройкиФормы() сразу добавлена для того, чтобы не попадать лишний раз в исключение при отладке — она вызывается общей формой отчета. Но в любом случае она нам понадобится позже.

Теперь создадим макет СКД и вручную добавим параметры, которые будет задавать пользователь — НачальноеЧислоКонечноеЧисло и МаксимальнаяСтепень (тип Число), зададим разумное ограничение размера — чтобы пользователь не баловался — я поставил 3 знака для чисел и 1 для степени. Можем сразу задать пользовательские значения — для отладки.

Можем сразу написать запрос (а кстати, где подсветка запроса 1С?):

ВЫБРАТЬ
	ВЫРАЗИТЬ(Данные.Число КАК Число) КАК Число
ИЗ
	Данные КАК Данные

Поле у нас всего одно, потому что остальные мы как раз и добавим программно.

Добавляем обработчик ПриКомпоновкеРезультата() с какой-то валидацией, пишем простой алгоритм (он нам нужен просто для примера).

#Область ОбработчикиСобытий

Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
	
	СтандартнаяОбработка = Ложь;
	НастройкиКомпоновки = КомпоновщикНастроек.ПолучитьНастройки();
	
	НачальноеЧисло = НастройкиКомпоновки.ПараметрыДанных.Элементы.Найти("НачальноеЧисло").Значение;
	КонечноеЧисло = НастройкиКомпоновки.ПараметрыДанных.Элементы.Найти("КонечноеЧисло").Значение;
	МаксимальнаяСтепень = НастройкиКомпоновки.ПараметрыДанных.Элементы.Найти("МаксимальнаяСтепень").Значение;
	
	Если НачальноеЧисло >= КонечноеЧисло Тогда
		ВызватьИсключение "Конечное число должно быть больше начального!";
	КонецЕсли;
	Если МаксимальнаяСтепень < 2 Тогда
		ВызватьИсключение "Любое число в степени 0 равно 0, а в степени 1 - самому себе!";
	КонецЕсли;
	
	ТипКолонки = ОбщегоНазначения.ОписаниеТипаЧисло(10);
	ИменаКолонок = Новый Соответствие;
	Данные = Новый ТаблицаЗначений;
	Данные.Колонки.Добавить("Число", ОбщегоНазначения.ОписаниеТипаЧисло(3));
	
	Для к = 2 По МаксимальнаяСтепень Цикл
		ИмяКолонки = СтрШаблон("Колонка%1", к);
		ИменаКолонок.Вставить(к, ИмяКолонки);
		
		Данные.Колонки.Добавить(ИмяКолонки, ТипКолонки);
	КонецЦикла;
	
	Для й = 0 По (КонечноеЧисло - НачальноеЧисло) Цикл
		СтрДанные = Данные.Добавить();
		
		СтрДанные.Число = НачальноеЧисло + й;
		Для к = 2 По МаксимальнаяСтепень Цикл
			СтрДанные[ИменаКолонок.Получить(к)] = Pow(СтрДанные.Число, к);
		КонецЦикла;
	КонецЦикла;
	
	Запрос = Новый Запрос;
	Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
	Запрос.Текст =
	"ВЫБРАТЬ
	|*
	|Поместить Данные
	|ИЗ &Данные КАК Данные";
	Запрос.УстановитьПараметр("Данные", Данные);
	Запрос.Выполнить();
	
	ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;
	КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
	
	МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, НастройкиКомпоновки, ДанныеРасшифровки);
	ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
	ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки, , , Запрос.МенеджерВременныхТаблиц);
	
	ДокументРезультат.Очистить();
	
	ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
	ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
	ПроцессорВывода.Вывести(ПроцессорКомпоновки);
		
КонецПроцедуры

#КонецОбласти

Добиваемся работоспособности отчета:

 

 

Сразу добавляем оптимизацию: СтрШаблон() вместо конкатенации кэширование имен колонок.

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

 

Доработка отчета до конечного вида

Отчет у нас есть, данные мы получили, осталось вывести эти данные в этот отчет. Для этого нам понадобится изменить текст запроса набора данных, добавить новые поля в сам макет СКД, а потом вывести эти поля в вариант отчета. Если добавленные поля будут ресурсами, то нужно добавить ещё и ресурсы.

Для изменения текста запроса воспользуемся объектом СхемаЗапроса, для добавления полей в структуру отчета объектом НастройкиКомпоновкиДанных, а собственно текст запроса нужно править в самом объекте СхемаКомпоновкиДанных:

#Область СлужебныеПроцедурыИФункции

Функция НастройкиКомпоновкиСУчетомДанных()
	
	Схема = Новый СхемаЗапроса;
	Схема.УстановитьТекстЗапроса(СхемаКомпоновкиДанных.НаборыДанных.НаборДанных1.Запрос);
	// у нас простой запрос, поэтому просто получаем последний оператор:
	ЗапросСхемы = Схема.ПакетЗапросов[Схема.ПакетЗапросов.Количество() - 1];
	ОператорВыбрать = ЗапросСхемы.Операторы[ЗапросСхемы.Операторы.Количество() - 1];
	Источник = ОператорВыбрать.Источники[0].Источник;
	
	Для к = 2 По МаксимальнаяСтепень Цикл
		ИмяКолонки = ИменаКолонок.Получить(к);
		Источник.ДоступныеПоля.Добавить(ИмяКолонки);
		ОператорВыбрать.ВыбираемыеПоля.Добавить(СтрШаблон("ВЫРАЗИТЬ(%1 КАК ЧИСЛО(10))", ИмяКолонки));
		ЗапросСхемы.Колонки[ЗапросСхемы.Колонки.Количество() - 1].Псевдоним = ИмяКолонки;
		
		// добавим само поле в набор данных СКД:
		ПолеСКД1 = СхемаКомпоновкиДанных.НаборыДанных.НаборДанных1.Поля.Добавить(Тип("ПолеНабораДанныхСхемыКомпоновкиДанных"));
		ПолеСКД1.Поле = ИмяКолонки;
		ПолеСКД1.ПутьКДанным = ИмяКолонки;
		ПолеСКД1.Заголовок = ИмяКолонки; // можно задать красивый заголовок
		
		// и ресурс:
		ПолеИтога1 = СхемаКомпоновкиДанных.ПоляИтога.Добавить();
		ПолеИтога1.Выражение = СтрШаблон("СУММА(%1)", ИмяКолонки);
		ПолеИтога1.ПутьКДанным = ИмяКолонки;
	КонецЦикла;
	
	// вернем доработанный запрос в СКД:
	СхемаКомпоновкиДанных.НаборыДанных.НаборДанных1.Запрос = Схема.ПолучитьТекстЗапроса();
	
	// теперь нужно переинициализировать настройки и добавить наши новые поля:
	КомпоновщикНастроек.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(СхемаКомпоновкиДанных));
	НастройкиКомпоновки = КомпоновщикНастроек.ПолучитьНастройки();
	
	// также для примера добавим для новых полей условное оформление:
	УсловноеОформление = НастройкиКомпоновки.УсловноеОформление.Элементы.Добавить();
	
	Для к = 2 По МаксимальнаяСтепень Цикл
		ИмяКолонки = ИменаКолонок.Получить(к);
		
		ВыбранноеПолеКомпоновкиДанных = НастройкиКомпоновки.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
		ВыбранноеПолеКомпоновкиДанных.Использование = Истина;
		ВыбранноеПолеКомпоновкиДанных.Поле = Новый ПолеКомпоновкиДанных(ИмяКолонки);
		
		ПолеУсловногоОформления = УсловноеОформление.Поля.Элементы.Добавить();
		ПолеУсловногоОформления.Использование = Истина;
		ПолеУсловногоОформления.Поле = Новый ПолеКомпоновкиДанных(ИмяКолонки);
	КонецЦикла;
	
	УсловноеОформление.Оформление.УстановитьЗначениеПараметра(Новый ПараметрКомпоновкиДанных("МинимальнаяШирина"), 10);
	УсловноеОформление.Оформление.УстановитьЗначениеПараметра(Новый ПараметрКомпоновкиДанных("Формат"), "ЧДЦ=2");
	УсловноеОформление.Использование = Истина;
	
	Возврат НастройкиКомпоновки;
	
КонецФункции

#КонецОбласти

Вот и всё, отчет работает, условное оформление тоже, ресурсы считаются:

 

 

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

Вот и всё, надеюсь, кто-нибудь воспользуется этим как шпаргалкой, и потратит чуть меньше времени, чем потратил я)

 

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

has been added to your cart:
Оформление заказа