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