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