Как удалить или изменить конкретное поле в отборе СКД, не затрагивая остальные настройки?

Программист 1С v8.3 (Обычные формы)
← На главную

При разработке отчетов на базе Системы компоновки данных (СКД) часто возникает задача программного управления фильтрами. Типичная ситуация: на форме отчета выведены отдельные поля ввода (например, «Подразделение» и «Номенклатура»), которые должны управлять отбором. Распространенная ошибка начинающих разработчиков заключается в использовании метода Очистить() для коллекции элементов отбора. Это приводит к тому, что при изменении одного значения «слетают» все остальные настройки, установленные пользователем. Разберем подробно, как правильно идентифицировать и удалять только нужные элементы отбора.

Проблема использования метода Очистить()

Рассмотрим ситуацию, которую описал автор темы. В процедуре при изменении значения поля выполняется следующий код:

КомпоновщикНастроек.Настройки.Отбор.Элементы.Очистить();

Этот метод полностью удаляет все строки из таблицы отборов. Если пользователь уже выбрал «Номенклатуру», а затем решил изменить «Подразделение», метод Очистить() удалит и номенклатуру, и подразделение. Наша задача — научиться находить в коллекции Элементы только ту строку, которая отвечает за конкретное поле, и работать именно с ней.

Решение 1: Перебор коллекции и удаление конкретного элемента

Чтобы удалить конкретное поле, нам нужно проанализировать состав коллекции КомпоновщикНастроек.Настройки.Отбор.Элементы. Важно помнить: если мы планируем удалять элементы из коллекции в цикле, обход нужно выполнять с конца к началу. Если идти с начала (от 0 до Максимум), то при удалении элемента индексы оставшихся сместятся, и цикл пропустит следующую строку или вызовет ошибку обращения.

Проанализируем правильный алгоритм удаления поля «Подразделение»:


// Определяем поле, которое хотим найти
ИскомоеПоле = Новый ПолеКомпоновкиДанных("Подразделение");

// Получаем количество элементов в отборе
КоллекцияЭлементов = КомпоновщикНастроек.Настройки.Отбор.Elements;
К-во = КоллекцияЭлементов.Количество();

// Цикл с конца к началу
Для Индекс = 1 По К-во Цикл
    ТекущийИндекс = К-во - Индекс;
    Элемент = КоллекцияЭлементов.Получить(ТекущийИндекс);
    
    // Проверяем, является ли элемент отбором (а не группой) и совпадает ли поле
    Если ТипЗнч(Элемент) = Тип("ЭлементОтбораКомпоновкиДанных") Тогда
        Если Элемент.ЛевоеЗначение = ИскомоеПоле Тогда
            КоллекцияЭлементов.Удалить(Элемент);
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

В этом примере мы используем объект Новый ПолеКомпоновкиДанных для сравнения. Это принципиально важный момент, так как ЛевоеЗначение в СКД — это не строка, а специальный объект.

Решение 2: Поиск и обновление вместо удаления

Профессиональный подход заключается в том, чтобы не удалять и добавлять элементы заново, а искать существующий элемент и обновлять его значение. Это работает быстрее и сохраняет настройки пользователя (например, если он вручную изменил вид сравнения на «В списке»).

Выясним, как реализовать этот метод для поля «Подразделение»:


Процедура ПодразделениеПриИзменении(ЭлементФормы)
    ИскомоеПоле = Новый ПолеКомпоновкиДанных("Подразделение");
    ЭлементОтбора = Неопределено;

    // Ищем, есть ли уже такое поле в отборах
    Для Каждого Элемент Из КомпоновщикНастроек.Настройки.Отбор.Элементы Цикл
        Если ТипЗнч(Элемент) = Тип("ЭлементОтбораКомпоновкиДанных") 
            И Элемент.ЛевоеЗначение = ИскомоеПоле Тогда
            ЭлементОтбора = Элемент;
            Прервать;
        КонецЕсли;
    КонецЦикла;

    // Если не нашли — создаем новый
    Если ЭлементОтбора = Неопределено Тогда
        ЭлементОтбора = КомпоновщикНастроек.Настройки.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
        ЭлементОтбора.ЛевоеЗначение = ИскомоеПоле;
        ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
    КонецЕсли;

    // Устанавливаем значение и активность
    Если ЗначениеЗаполнено(Подразделение) Тогда
        ЭлементОтбора.ПравоеЗначение = Подразделение;
        ЭлементОтбора.Использование = Истина;
    Иначе
        // Если поле на форме очищено — просто выключаем отбор, не удаляя строку
        ЭлементОтбора.Использование = Ложь;
    КонецЕсли;
КонецПроцедуры

Решение 3: Работа через параметры (альтернативный подход)

Как справедливо замечено в обсуждении, иногда проще управлять не коллекцией отборов, а параметрами СКД. Этот метод особенно хорош для фиксированных полей интерфейса. В тексте запроса СКД можно прописать условие, которое будет автоматически игнорироваться, если параметр не заполнен.

Рассмотрим пример условия в запросе:


ГДЕ (&Подразделение = ЗНАЧЕНИЕ(Справочник.Подразделения.ПустаяСсылка) ИЛИ Подразделение = &Подразделение)
  И (&Номенклатура = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка) ИЛИ Номенклатура = &Номенклатура)

В этом случае в процедуре ПриИзменении вам достаточно просто устанавливать значение параметра:


ПараметрСКД = КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти("Подразделение");
Если ПараметрСКД <> Неопределено Тогда
    ПараметрСКД.Значение = Подразделение;
    ПараметрСКД.Использование = Истина;
КонецЕсли;

Важные нюансы при работе с СКД

  1. Пользовательские настройки: В современных конфигурациях (даже на обычных формах при использовании определенных шаблонов) отборы могут находиться не в КомпоновщикНастроек.Настройки, а в КомпоновщикНастроек.ПользовательскиеНастройки. Если программные изменения не отображаются на форме, проверьте именно эту коллекцию.
  2. Иерархия отборов: Если пользователь объединил отборы в группы (например, группа «ИЛИ»), простой перебор коллекции Элементы не найдет поля внутри групп. Для полной уверенности требуется рекурсивный обход всех вложенных групп элементов.
  3. Идентификатор пользовательской настройки: Чтобы быстро находить нужный элемент без циклов, в конфигураторе можно задать элементу отбора конкретный «Идентификатор пользовательской настройки». Тогда к нему можно обращаться напрямую через метод Найти() в коллекции пользовательских настроек.

Подведем итог: для корректного удаления поля необходимо отказаться от метода Очистить() в пользу точечного поиска элемента по объекту ПолеКомпоновкиДанных. Это обеспечит стабильную работу отчета и сохранит удобство интерфейса для пользователя.

← На главную