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