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