Предисловие
Недавно у меня возникла необходимость во-первых, формировать отчет программно, во-вторых, добавлять в него колонки по итогам получения данных. Наверняка для многих не составляет сложности сформировать такой отчет, да к тому же многие потребности покрываются возможностью выводить группируемые поля в колонки (мне не подошло). Несмотря на многолетний опыт, с такой задачей столкнулся впервые, поэтому покажу как делал. К сожалению, осмысленного примера не придумал, поэтому будет такой, сверху донизу искусственный.
Если видите ошибки, или как сделать лучше - обязательно пишите комментарии.
Постановка задачи
Написать отчет, который выводит для заданного диапазона целых чисел их степени от 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. В процессе обратил внимание на забавный факт: то, что мы в настройках СКД видим как "ресурсы", называется на самом деле "поля итога", а "поле" - это "путь к данным". Так что традиция запутывать пользователей несоответствием имени и синонима, оказывается, идет еще от разработчиков платформы.
Проверено на следующих конфигурациях и релизах:
- 1С:Библиотека стандартных подсистем, редакция 3.1, релизы 3.1.9.209