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