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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке отчетов в 1С:Предприятие на базе Системы компоновки данных (СКД) часто возникает задача сформировать структуру отчета динамически — без использования стандартной формы настроек СКД. К примеру, пользователю предоставляется возможность выбрать произвольный набор реквизитов (например, реквизиты справочника Номенклатура), и на основе этого выбора требуется построить иерархическое дерево группировок вида: Реквизит 1 -> Реквизит 2 -> ... -> Детальные записи.

Разберем подробно, как правильно организовать программную настройку структуры компоновщика данных, обойти выбранные реквизиты в цикле, корректно настроить вложенность группировок и вывести итоговый документ.

Основные нюансы работы со структурой СКД

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

  1. Обеспечение вложенности группировок: Чтобы группировки выстраивались в иерархическое дерево (одна под другой), каждая последующая группировка Тип("ГруппировкаКомпоновкиДанных") должна добавляться не в корень КомпоновщикНастроек.Настройки.Структура, а в коллекцию Структура предыдущей (родительской) группировки. Если добавлять все элементы в корень, они выведутся как параллельные группировки одного уровня.
  2. Тип группировки: Значение ТипГруппировкиКомпоновкиДанных.Иерархия следует использовать только для ссылочных полей с включенной иерархией в метаданных (например, иерархические справочники). Для обычных реквизитов (строки, числа, перечисления) указывается ТипГруппировкиКомпоновкиДанных.Элементы.
  3. Выбираемые поля: Для того чтобы ресурсы (например, КоличествоОстаток) автоматически вычислялись и отображались на каждом уровне группировки, в коллекцию Выбор.Элементы каждой создаваемой группировки рекомендуется добавлять элемент Тип("АвтоВыбранноеПолеКомпоновкиДанных").

Алгоритм программного формирования отчета

Рассмотрим пошагово порядок действий при реализации формирования отчета:

  1. Получаем макет схемы компоновки данных ОсновнаяСхемаКомпоновкиДанных.
  2. Устанавливаем значения параметров данных (например, Организация, Склад, КонецДата).
  3. Очищаем существующую структуру настроек методом Отчет.КомпоновщикНастроек.Настройки.Структура.Очистить().
  4. Обходим в цикле список выбранных пользователем реквизитов и последовательно создаем вложенные элементы структуры.
  5. На самый нижний уровень иерархии добавляем детальные записи с выбранными полями.
  6. Инициализируем КомпоновщикМакетаКомпоновкиДанных, ПроцессорКомпоновкиДанных и ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент.
  7. Выводим результат в табличный документ на форме.

Пример программного кода

Посмотрим на практическую реализацию серверной процедуры формирования отчета. Предполагается, что пользовательские настройки группировок хранятся в табличной части отчета ТаблицаГруппировок со колонками ИмяРеквизита и Использование.


&НаСервере
Процедура СформироватьОтчетНаСервере()
	
	// 1. Получение схемы компоновки данных
	СхемаКомпоновкиДанных = Отчеты.МТ_ОстаткиТоваровНаСкладах.ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
	ОбъектОтчет = РеквизитФормыВЗначение("Отчет");
	
	// 2. Настройка параметров данных
	ПараметрОрганизация = Отчет.КомпоновщикНастроек.Настройки.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("Организация"));
	Если ПараметрОрганизация <> Неопределено Тогда
		ПараметрОрганизация.Значение = ОбъектОтчет.Организация;
		ПараметрОрганизация.Использование = Истина;
	КонецЕсли;

	ПараметрСклад = Отчет.КомпоновщикНастроек.Настройки.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("Склад"));
	Если ПараметрСклад <> Неопределено Тогда
		ПараметрСклад.Значение = ОбъектОтчет.Склад;
		ПараметрСклад.Использование = Истина;
	КонецЕсли;

	ПараметрКонецДата = Отчет.КомпоновщикНастроек.Настройки.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("КонецДата"));
	Если ПараметрКонецДата <> Неопределено Тогда
		ПараметрКонецДата.Значение = Новый Граница(КонецДня(ОбъектОтчет.КонецПериода), ВидГраницы.Включая);
		ПараметрКонецДата.Использование = Истина;
	КонецЕсли;

	// 3. Очистка старой структуры
	ТекНастройкиКомпоновщика = Отчет.КомпоновщикНастроек.Настройки;
	ТекНастройкиКомпоновщика.Структура.Очистить();

	// Указатель на родительскую коллекцию для обеспечения вложенности
	ТекущаяКоллекцияСтруктуры = ТекНастройкиКомпоновщика.Структура;

	// 4. Динамическое создание вложенных группировок по реквизитам
	Для Каждого СтрокаГруппировки Из ОбъектОтчет.ТаблицаГруппировок Цикл
		
		Если Не СтрокаГруппировки.Использование Тогда
			Продолжить;
		КонецЕсли;
		
		// Добавляем группировку в текущую родительскую коллекцию
		НоваяГруппировка = ТекущаяКоллекцияСтруктуры.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
		НоваяГруппировка.Использование = Истина;
		
		// Задаем поле группировки (например, "Номенклатура.ВидНоменклатуры")
		ИмяПоля = "Номенклатура." + СтрокаГруппировки.ИмяРеквизита;
		ПолеГруппировки = НоваяГруппировка.ПоляГруппировки.Элементы.Добавить(Тип("ПолеГруппировкиКомпоновкиДанных"));
		ПолеГруппировки.Использование = Истина;
		ПолеГруппировки.Поле = Новый ПолеКомпоновкиДанных(ИмяПоля);
		ПолеГруппировки.ТипГруппировки = ТипГруппировкиКомпоновкиДанных.Элементы;
		
		// Автовыбор полей и ресурсов для уровня группировки
		АвтоВыбор = НоваяГруппировка.Выбор.Элементы.Добавить(Тип("АвтоВыбранноеПолеКомпоновкиДанных"));
		АвтоВыбор.Использование = Истина;
		
		// Переключаем родительскую коллекцию на структуру только что созданной группировки
		ТекущаяКоллекцияСтруктуры = НоваяГруппировка.Структура;
		
	КонецЦикла;

	// 5. Добавление детальных записей на самый нижний уровень
	ДетальныеЗаписи = ТекущаяКоллекцияСтруктуры.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
	ДетальныеЗаписи.Имя = "ДетальныеЗаписи";
	ДетальныеЗаписи.Использование = Истина;
	
	// Поля детальных записей
	ВыбранноеПолеНоменклатура = ДетальныеЗаписи.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПолеНоменклатура.Поле = Новый ПолеКомпоновкиДанных("Номенклатура");
	ВыбранноеПолеНоменклатура.Использование = Истина;

	ВыбранноеПолеОстаток = ДетальныеЗаписи.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ВыбранноеПолеОстаток.Поле = Новый ПолеКомпоновкиДанных("КоличествоОстаток");
	ВыбранноеПолеОстаток.Использование = Истина;

	// 6. Компоновка макета и выполнение
	ДанныеРасшифровкиОтчета = Новый ДанныеРасшифровкиКомпоновкиДанных;
	КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
	МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, ТекНастройкиКомпоновщика, ДанныеРасшифровкиОтчета);

	ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
	ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровкиОтчета);

	// 7. Вывод результата в табличный документ
	ТаблицаРезультата = Новый ТабличныйДокумент;
	ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
	ПроцессорВывода.УстановитьДокумент(ТаблицаРезультата);
	ПроцессорВывода.Вывести(ПроцессорКомпоновки);

	ЭтаФорма.Результат.Очистить();
	ЭтаФорма.Результат.Вывести(ТаблицаРезультата);
	ЭтаФорма.Результат.ТолькоПросмотр = Истина;
	ЭтаФорма.Результат.ПоказатьУровеньГруппировокСтрок(2);

КонецПроцедуры

Заполнение списка реквизитов через метаданные

Чтобы предоставить пользователю возможность выбора реквизитов справочника Номенклатура, сформируем список доступных реквизитов с использованием объекта Метаданные:


&НаСервере
Процедура ЗаполнитьДоступныеРеквизиты()
	
	ОбъектОтчет = РеквизитФормыВЗначение("Отчет");
	ОбъектОтчет.ТаблицаГруппировок.Очистить();
	
	МетаданныеНоменклатуры = Метаданные.Справочники.Номенклатура;
	
	// Обход обычных реквизитов справочника
	Для Каждого РеквизитИзМетаданных Из МетаданныеНоменклатуры.Реквизиты Цикл
		НоваяСтрока = ОбъектОтчет.ТаблицаГруппировок.Добавить();
		НоваяСтрока.ИмяРеквизита = РеквизитИзМетаданных.Имя;
		НоваяСтрока.Представление = РеквизитИзМетаданных.Синоним;
		НоваяСтрока.Использование = Ложь;
	КонецЦикла;
	
	ЗначениеВРеквизитФормы(ОбъектОтчет, "Отчет");
	
КонецПроцедуры

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

← На главную