Как установить отбор по дополнительному реквизиту в динамическом списке и избежать ошибки пересечения отборов

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

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

Параметры Отбора, Сортировки, Группировки или список отображаемых полей заданы неверно. по причине: Невозможно применить фиксированные настройки. Пересекаются элементы отбора.

В этой статье мы подробно разберем, почему возникает данная коллизия, как устроен механизм характеристик «под капотом» платформы 1С, и изучим несколько надежных, проверенных на практике способов решения этой задачи.

Выясняем причину ошибки пересечения отборов

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

  1. Фиксированные настройки (Список.ФиксированныеНастройки): Эти настройки задаются строго программным путем и не могут быть изменены пользователем интерактивно в режиме предприятия. Они жестко ограничивают выборку данных.
  2. Пользовательские настройки (Список.КомпоновщикНастроек.ПользовательскиеНастройки): Эти отборы вынесены на форму для удобства пользователя. Пользователь может включать, выключать их или менять их значения. Более того, платформа по умолчанию автоматически сохраняет эти настройки при закрытии формы и восстанавливает их при следующем открытии.

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

Способ 1. Программное управление пользовательскими настройками

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


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

КонецПроцедуры

Этот способ хорош тем, что он не вызывает исключений пересечения настроек, а гармонично встраивается в пользовательский интерфейс. Пользователь увидит установленный фильтр на форме и сможет при необходимости его отключить. При необходимости глубже разобраться в тонкостях системы компоновки динамического списка пригодится руководство по настройке динамических списков.

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

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

Для программной установки отбора воспользуемся методом УстановитьЭлементОтбораДинамическогоСписка из общего модуля ОбщегоНазначенияКлиентСервер. Этот метод берет на себя все рутинные операции по проверке структуры динамического списка:


// Вызываем в обработчике ПриСозданииНаСервере или при нажатии команды на форме
ИмяЭлементаОтбора = "Ссылка.[ИмяДополнительногоРеквизита]";
ЗначениеФильтра = Истина;

ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(
    Список, 
    ИмяЭлементаОтбора, 
    ЗначениеФильтра, 
    ВидСравненияКомпоновкиДанных.Равно, 
    "Запрещено к продаже", // Представление отбора на форме
    Истина,               // Использование отбора
    РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный
);

Метод БСП автоматически анализирует наличие существующих отборов и предотвращает возникновение коллизий, делая код более лаконичным и безопасным.

Способ 3. Полное программное очищение пользовательских настроек перед установкой

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

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


&НаСервере
Процедура ОчиститьИУстановитьФиксированныйОтбор()
    
    ПолеОтбора = Новый ПолеКомпоновкиДанных("Ссылка.[Запрет (Доп. свойства справочника ""Номенклатура"" (Общие))]");
    
    // Шаг 1. Отключаем пересекающиеся пользовательские отборы
    Для Каждого ЭлементНастройки Из Список.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы Цикл
        Если ТипЗнч(ЭлементНастройки) = Тип("ОтборКомпоновкиДанных") Тогда
            Для Каждого ЭлементОтбора Из ЭлементНастройки.Элементы Цикл
                Если ЭлементОтбора.ЛевоеЗначение = ПолеОтбора Тогда
                    ЭлементОтбора.Использование = Ложь; // Отключаем использование старого отбора
                КонецЕсли;
            КонецЦикла;
        КонецЕсли;
    КонецЦикла;
    
    // Шаг 2. Добавляем новый чистый фиксированный отбор
    Список.Отбор.Элементы.Очистить(); // Дополнительно можем очистить коллекцию фиксированных отборов
    
    НовыйФиксированныйОтбор = Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
    НовыйФиксированныйОтбор.ЛевоеЗначение = ПолеОтбора;
    НовыйФиксированныйОтбор.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
    НовыйФиксированныйОтбор.ПравоеЗначение = Истина;
    НовыйФиксированныйОтбор.Использование = Истина;
    НовыйФиксированныйОтбор.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный;
    
КонецПроцедуры

Такой подход обеспечивает 100% срабатывание фильтра без вызова платформенной ошибки пересечения настроек.

Способ 4. Альтернатива через произвольный запрос (Рекомендуется для производительности)

Важно помнить, что обращение к характеристикам и дополнительным реквизитам через конструкцию Ссылка.[ИмяРеквизита] заставляет платформу генерировать сложные неявные соединения (LEFT JOIN) ко всей таблице дополнительных реквизитов СУБД на лету. На крупных базах данных с большим объемом информации это может привести к критическому падению производительности динамического списка.

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

Выполним следующие шаги:

  1. Откроем свойства динамического списка и установим флаг Произвольный запрос.
  2. Напишем оптимизированный запрос, где вручную свяжем таблицу номенклатуры с таблицей ее дополнительных реквизитов:

ВЫБРАТЬ
    СправочникНоменклатура.Ссылка КАК Ссылка,
    СправочникНоменклатура.Код КАК Код,
    СправочникНоменклатура.Наименование КАК Наименование,
    ЕСТЬNULL(ТаблицаДопРеквизитов.Значение, ЛОЖЬ) КАК ЗапретПродажи
ИЗ
    Справочник.Номенклатура КАК СправочникНоменклатура
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура.ДополнительныеРеквизиты КАК ТаблицаДопРеквизитов
        ПО ТаблицаДопРеквизитов.Ссылка = СправочникНоменклатура.Ссылка
        И ТаблицаДопРеквизитов.Свойство = &СвойствоЗапрет

Теперь в модуле формы при ее создании нам достаточно один раз установить значение параметра СвойствоЗапрет, указав конкретную ссылку на элемент Плана видов характеристик, и наложить легкий и быстрый отбор по полю ЗапретПродажи:


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

Такой подход полностью исключает появление ошибки пересечения настроек и работает в разы быстрее, поскольку СУБД производит соединение по индексированному полю Свойство. Для изучения других вариантов добавления полей в список можно использовать статью о практических методах доработки динамических списков.

Способ 5. Передача отборов при открытии формы через параметры

Рассмотрим ситуацию, когда форму списка необходимо открыть из другого места конфигурации (например, из документа) уже с примененным отбором. Чтобы не вызывать ошибку пересечения настроек, правильно передавать параметры отбора в структуру ФиксированныеНастройки при вызове метода открытия формы:


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

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

← На главную