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