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