Толкового полного ответа на этот вопрос мне не встречалось: кто-то советует создавать элементы для настроек на форме вручную и программно их устанавливать, кто-то предлагает программно модифицировать и генерить сами схемы... Создается впечатление, что это очень сложная и громоздкая задача, а между тем, если разобраться, выяснится, что все на самом деле не так страшно и сложно. Правильный ответ - да, можно! И прочитав эту статью, вы узнаете как.
Для написания примеров отчета и обработки я воспользовался типовой конфигурацией "Библиотека Стандартных Подсистем (демо)" версии 3.0.3.341 (платформа 8.3.13.1472). Итак, поехали! Начнем с простого.
1. Подмена схемы компоновки данных в отчете.
Создадим новый внешний отчет, назовем его, скажем "_СКДОтчетЗаменаСхемы". Добавим в макеты две простеньких схемы компоновки данных (для примера этого вполне достаточно): Номенклатура и ДоговорыКонтрагентов.
Для номенклатуры настроим группировки и добавим отбор по реквизиту "Родитель", который включим в пользовательские настройки:
Для договоров настроим группировки и добавим отборы по реквизитам "Контрагент" и "Организация", которые также включим в пользовательские настройки:
Теперь одну из схем установим основной (это нужно нам, чтобы сгенерилась начальная форма отчета) и добавим форму отчета, после чего очистим реквизит "Основная схема компоновки данных".
Добавим также реквизит "АдресСхемы" типа Строка неограниченной длины (он нам пригодится позже).
Открываем форму, и переходим к ее настройке. Добавим реквизит формы "ИмяОтчета" типа Строка, вынесем его на форму и включим свойство КнопкаВыпадающегоСписка:
Добавляем для реквизита "ИмяОтчета" событие "ПриИзменении" и переходим в модуль формы. Здесь добавляем событие "ПриСозданииНаСервере" из которого вызываем процедуру ЗаполнитьСписокОтчетов():
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
ЗаполнитьСписокОтчетов();
КонецПроцедуры
&НаСервере
Процедура ЗаполнитьСписокОтчетов()
СписокМакетов = Новый Структура("Номенклатура, ДоговорыКонтрагентов");
Для Каждого Макет Из СписокМакетов Цикл
Элементы.ИмяОтчета.СписокВыбора.Добавить(Макет.Ключ, Макет.Ключ);
КонецЦикла;
КонецПроцедуры
А из события "ИмяОтчетаПриИзменении" вызовем процедуру УстановитьМакетОтчета()
&НаКлиенте
Процедура ИмяОтчетаПриИзменении(Элемент)
УстановитьМакетОтчета()
КонецПроцедуры
&НаСервере
Процедура УстановитьМакетОтчета()
ОтчетОбъект = РеквизитФормыВЗначение("Отчет");
СхемаКомпоновкиДанных = ОтчетОбъект.ПолучитьМакет(ИмяОтчета);
Отчет.АдресСхемы = ПоместитьВоВременноеХранилище(СхемаКомпоновкиДанных, УникальныйИдентификатор);
Отчет.КомпоновщикНастроек.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(Отчет.АдресСхемы));
Отчет.КомпоновщикНастроек.ЗагрузитьНастройки(СхемаКомпоновкиДанных.НастройкиПоУмолчанию);
Отчет.КомпоновщикНастроек.Восстановить();
КонецПроцедуры
Процедура ЗаполнитьСписокОтчетов() принципиальной роли не играет, а вот на процедуре УстановитьМакетОтчета() остановимся подробнее:
//Здесь мы получаем схему компоновки данных из макетов нашего отчета по выбранному имени отчета:
ОтчетОбъект = РеквизитФормыВЗначение("Отчет");
СхемаКомпоновкиДанных = ОтчетОбъект.ПолучитьМакет(ИмяОтчета);
// помещаем схему во временное хранилище и сохраняем адрес в реквизите "АдресСхемы":
Отчет.АдресСхемы = ПоместитьВоВременноеХранилище(СхемаКомпоновкиДанных, УникальныйИдентификатор);
//инициализируем компоновщик настроек
Отчет.КомпоновщикНастроек.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(Отчет.АдресСхемы));
// загружаем настройки
Отчет.КомпоновщикНастроек.ЗагрузитьНастройки(СхемаКомпоновкиДанных.НастройкиПоУмолчанию);
// вызываем метод компоновщика настроек восстановить.
//Согласно описанию, метод "Восстанавливает работоспособность настроек.
//В случае, если был изменен источник доступных настроек, часть полей,
//использованных в настройках, может потерять связь с доступными полями.",
//а источник данных у нас был изменен еще как.
Отчет.КомпоновщикНастроек.Восстановить();
Сохраняем, запускаем обработку и смотрим, что получилось. Меняем имя отчета и смотрим, как соответственно меняются пользовательские настройки:
То что нужно! Нажимаем "Сформировать" иииии.... получаем ошибку:
Да, мы загрузили настройки в компоновщик, но отчет все равно не знает, какую схему использовать. Если бы мы оставили схему по умолчанию, то использовалась бы она, и в случае использования ее настроек отчет бы сформировался, а вот случае использования настроек другой схемы мы получили бы ошибку "Такое-то поле не найдено".
Не беда! Идем в модуль отчета и создаем обработчик события "ПриКомпоновкеРезультата" где и присваиваем нужную схему(вот для чего нужен был реквизит "АдресСхемы"):
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
СхемаКомпоновкиДанных = ПолучитьИзВременногоХранилища(АдресСхемы);
КонецПроцедуры
Запускаем отчет и наслаждаемся результатом:
Как видим, схемы меняются, отчеты строятся, отборы работают. И всего-то потребовалось 5 ключевых строчек кода.
Теперь давайте немного усложним задачу.
2. Использование нескольких схем компоновки данных в обработках.
Допустим, у нас есть какая-нибудь навороченная обработка, типа панели функций или монитора руководителя в которую нам, кроме всего, также нужно выводить несколько отчетов. При реализации подмены СКД в отчете нам помог предопределенный компоновщик настроек и наличие события "ПриКомпоновкеРезультата" в модуле объекта. В обработке всего этого нет - и придётся генерить наши отчеты вручную, но как быть с настройками? И здесь на помощь нам спешит тип данных "КомпоновщикНастроекКомпоновкиДанных".
Создадим новую внешнюю обработку "_СКДЗаменаСхемы". Чтобы не повторятся, закинем в нее схемы компоновки из нашего предыдущего отчета.
Создадим форму отчета и добавим следующие реквизиты формы:
"ИмяОтчета" - тип Строка, аналогично одноименному реквизиту в отчете.
"АдресСхемы" - тип Строка, как и в отчете, но нам больше не нужно будет использовать его в модуле объекта, поэтому из реквизитов объекта он перекочевал в реквизиты формы.
"Результат" - тип ТабличныйДокумент, в который мы будем выводить наши отчеты.
"КомпоновщикНастроек" – тип КомпоновщикНастроекКомпоновкиДанных.
Также добавим команду "СформироватьОтчет" и обработчики для нее на клиенте и на сервере.
А теперь, внимание, очень важный момент, делаем "КомпоновщикНастроек" основным реквизитом формы:
Это откроет нам доступ к расширению управляемой формы для компоновщика настроек и позволит использовать метод СоздатьЭлементыФормыПользовательскихНастроек().
Теперь, добавим группу без отображения "ГруппаПользовательскиеНастройкиОтчеты" и переходим в модуль формы.
По аналогии с отчетом, добавим события "ИмяОтчетаПриИзменении" и "ПриСозданииНаСервере", с соответствующими вызовами процедур ЗаполнитьСписокОтчетов() и УстановитьМакетОтчета(). Только теперь в процедуре УстановитьМакетОтчета() добавляется вызов метода СоздатьЭлементыФормыПользовательскихНастроек():
&НаСервере
Процедура УстановитьМакетОтчета()
Обработка = РеквизитФормыВЗначение("Объект");
СКД = Обработка.ПолучитьМакет(ИмяОтчета);
АдресСхемы = ПоместитьВоВременноеХранилище(СКД, УникальныйИдентификатор);
КомпоновщикНастроек.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(АдресСхемы));
КомпоновщикНастроек.ЗагрузитьНастройки(СКД.НастройкиПоУмолчанию);
КомпоновщикНастроек.Восстановить();
СоздатьЭлементыФормыПользовательскихНастроек(Элементы.ГруппаПользовательскиеНастройкиОтчеты, РежимОтображенияНастроекКомпоновкиДанных.БыстрыйДоступ, 4);
КонецПроцедуры
В качестве первого параметра метода СоздатьЭлементыФормыПользовательскихНастроек() мы указываем элемент формы, в котором будут генерироваться пользовательские настройки. Второй параметр указывает, какие настройки будут добавлены: все или только быстрые. И третий параметр показывает количество настроек выводимых в одном ряду.
Теперь осталось добавить обработчики команды "Сформировать" и механизм динамической замены СКД в обработке готов:
&НаСервере
Процедура СформироватьОтчетНаСервере()
Результат.Очистить();
Схема = ПолучитьИзВременногоХранилища(АдресСхемы);
Настройки = КомпоновщикНастроек.ПолучитьНастройки();
ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(Схема, Настройки, ДанныеРасшифровки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент();
ПроцессорВывода.УстановитьДокумент(Результат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
КонецПроцедуры
&НаКлиенте
Процедура СформироватьОтчет(Команда)
СформироватьОтчетНаСервере();
КонецПроцедуры
Сохраняем, запускаем, наслаждаемся:
Как видим, задача оказалась не намного сложнее, чем подмена СКД в отчете. Ключевым моментом возможности использования автоматической генерации пользовательских настроек является установка реквизита "КомпоновщикНастроек" основным реквизитом формы, что открывает доступ к соответствующему расширению формы.
Ну и чтоб совсем все было красиво, осталось добавить обработчик расшифровки.
Добавим реквизит формы "АдресДанныхРасшифровки" тип Строка.
В конце процедуры СформироватьОтчетНаСервере() добавим строку:
АдресДанныхРасшифровки = ПоместитьВоВременноеХранилище(ДанныеРасшифровки, Новый УникальныйИдентификатор);
Добавим обработчик "ОбработкаРасшифровки" для элемента формы Результат:
&НаКлиенте
Процедура ОбработкаРасшифровки(Элемент, Расшифровка, СтандартнаяОбработка, ДополнительныеПараметры)
Данные = ОбработатьРасшифровки(Элемент.Имя, Расшифровка, СтандартнаяОбработка);
ОткрытьЗначение(Данные);
КонецПроцедуры
и функцию:
&НаСервере
Функция ОбработатьРасшифровки(Инд, Расшифровка, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
ДанныеРасшифровок = ПолучитьИзВременногоХранилища(АдресДанныхРасшифровки);
Если ТипЗнч(Расшифровка) = Тип("ИдентификаторРасшифровкиКомпоновкиДанных") тогда
ЭлементРасшифровки = ДанныеРасшифровок.Элементы[Расшифровка];
Если ТипЗнч(ЭлементРасшифровки) = Тип("ЭлементРасшифровкиКомпоновкиДанныхПоля") Тогда
Для Каждого Поле Из ЭлементРасшифровки.ПолучитьПоля() Цикл
Возврат Поле.Значение;
КонецЦикла;
КонецЕсли;
КонецЕсли;
КонецФункции
Теперь при клике на ячейку нашего отчета будет открываться не окошко с непонятной цифрой, а соответствующий объект.
Как видим, все оказалось достаточно просто.
P.S. И немного о практическом применении. Например, если реализовать добавление и хранение СКД в базе, допустим, в регистре сведений в реквизите с типом "ХранилищеЗначения", то подобную обработку можно использовать для просмотра динамически добавляемых/обновляемых отчетов, что очень выручает в случаях, когда нет возможности обновить конфигурацию в данный момент, а срочно нужно подправить отчет или создать новый "на вчера".
СКД является невероятно мощным механизмом, и я постоянно открываю для себя все новые и новые его возможности и нахожу новые, неочевидные и нестандартные способы применения. Чего и вам желаю :)
Enjoy!