Как программно найти и удалить один элемент отбора СКД в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе разработки отчетов и обработок в системе 1С:Предприятие 8 часто возникает задача динамического управления отборами Системы компоновки данных (СКД). Например, нам может потребоваться программно найти конкретное поле в настройках компоновщика и удалить его или отключить его использование (например, когда пользователь очистил поле периода или контрагента на форме).

Разберем детально, почему стандартные методы поиска не всегда работают очевидным образом, и рассмотрим правильные алгоритмы решения этой задачи.

В чем сложность стандартного поиска элементов отбора?

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


ЭлементПериод = КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти("Период");

Однако для коллекции элементов отбора (Настройки.Отбор.Элементы) метода Найти() по имени поля не существует. Коллекция отборов может содержать не только простые элементы отбора (ЭлементОтбораКомпоновкиДанных), но и целые группы (ГруппаЭлементовОтбораКомпоновкиДанных), такие как "И", "ИЛИ", "НЕ". Из-за этой иерархической структуры прямой поиск по имени поля не реализован платформой «из коробки».

Шаг 1. Как программно найти элемент отбора по имени поля

Чтобы найти нужный элемент, нам необходимо перебрать коллекцию отборов в цикле и сравнить левое значение каждого элемента. Левое значение отбора имеет тип ПолеКомпоновкиДанных, поэтому для сравнения нам также нужно инициализировать объект этого типа.

Рассмотрим пример поиска отбора по полю "ДатаНачалаП":


ИскомоеПоле = Новый ПолеКомпоновкиДанных("ДатаНачалаП");

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

Шаг 2. Безопасное удаление элемента отбора

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

Выясним, как обойти это ограничение платформы. Существует два надежных способа удаления.

Способ А: Удаление одного элемента с прерыванием цикла

Если вам гарантированно нужно найти и удалить только один (первый попавшийся) элемент отбора, мы можем использовать оператор Прервать сразу после удаления. Цикл завершится, и платформа не успеет выдать ошибку изменения коллекции:


ИскомоеПоле = Новый ПолеКомпоновкиДанных("ДатаНачалаП");

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

Способ Б: Удаление нескольких элементов через вспомогательный массив

Если в настройках может присутствовать несколько отборов по одному и тому же полю, и вам нужно удалить их все, сначала соберите элементы в массив, а затем выполните удаление во втором цикле:


ИскомоеПоле = Новый ПолеКомпоновкиДанных("ДатаНачалаП");
МассивНаУдаление = Новый Массив;

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

// Шаг 2: Безопасно удаляем элементы из коллекции СКД
Для Каждого ЭлементИзМассива Из МассивНаУдаление Цикл
    КомпоновщикНастроек.Настройки.Отбор.Элементы.Удалить(ЭлементИзМассива);
КонецЦикла;

Альтернатива: Отключение использования вместо удаления

Зачастую физическое удаление элемента отбора не требуется. Гораздо удобнее просто отключить его влияние на компоновку данных. Для этого достаточно установить свойство Использование в значение Ложь. Преимущество этого метода в том, что если пользователю снова потребуется этот отбор, вам не придется создавать его заново — достаточно будет вернуть свойство в значение Истина.


ИскомоеПоле = Новый ПолеКомпоновкиДанных("ДатаНачалаП");

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

Нюанс работы с Пользовательскими настройками

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

Чтобы программное отключение сработало корректно и отобразилось на форме, нам нужно выполнить поиск и отключение в коллекции ПользовательскиеНастройки.Элементы:


ИскомоеПоле = Новый ПолеКомпоновкиДанных("ДатаНачалаП");

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

Использование готовых функций БСП (Библиотека Стандартных Подсистем)

Если вы работаете в современной конфигурации, построенной на БСП, вам не нужно писать эти алгоритмы вручную. В общем модуле ОбщегоНазначенияКлиентСервер уже содержатся готовые, оптимизированные функции, которые умеют корректно работать со всеми видами настроек, включая рекурсивный поиск внутри групп отборов.

Рассмотрим наиболее полезные методы БСП:

  1. Быстрое удаление отбора по имени поля:

    Процедура выполнит рекурсивный поиск и удалит все элементы с указанным именем поля:

    
    ОбщегоНазначенияКлиентСервер.УдалитьЭлементыГруппыОтбора(КомпоновщикНастроек.Настройки.Отбор, "ДатаНачалаП");
    
  2. Программная установка или изменение отбора:

    Этот метод чрезвычайно удобен: если отбор с таким полем уже существует, он обновит его значение и установит флаг использования. Если отбора еще нет — он создаст его автоматически.

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

    Для динамических списков на управляемых формах есть специализированный метод, который корректно очищает отборы во всех структурах данных формы:

    
    ОбщегоНазначенияКлиентСервер.УдалитьЭлементыГруппыОтбораДинамическогоСписка(Список, "ДатаНачалаП");
    

Использование стандартных функций БСП делает ваш код лаконичным, предотвращает ошибки изменения коллекций и гарантирует корректное поведение интерфейса отчетов на управляемых формах.

← На главную