Как заставить выпадающий список поля ввода учитывать отбор в 1С:Предприятии?

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

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

Постановка проблемы: Отбор игнорируется в выпадающем списке, но работает в форме выбора

Представим, что у нас есть поле ввода на управляемой форме, связанное, например, со справочником "Пользователи". Мы хотим, чтобы в выпадающем списке этого поля отображались только действующие пользователи, то есть те, у которых реквизит "Недействителен" установлен в значение Ложь. Для этого мы можем установить отбор в модуле менеджера справочника, используя предопределенную процедуру ОбработкаПолученияДанныхВыбора, или задать его через свойство ПараметрыВыбора элемента формы. Однако, после всех настроек, мы видим, что при наборе текста в поле или нажатии на кнопку выпадающего списка, система показывает всех пользователей, включая недействительных.

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

Для примера, в модуле менеджера справочника "Пользователи" может быть следующий код, который должен устанавливать отбор по реквизиту "Недействителен":


// В МодулеМенеджера Справочника Пользователи
// ...
Процедура ОбработкаПолученияДанныхВыбора(ДанныеВыбора, Параметры, СтандартнаяОбработка)

    Если НЕ Параметры.Отбор.Свойство("Недействителен") Тогда
        Параметры.Отбор.Вставить("Недействителен", Ложь);
    КонецЕсли;

    // Также можно добавить отбор по пометке удаления
    Если НЕ Параметры.Отбор.Свойство("ПометкаУдаления") Тогда
        Параметры.Отбор.Вставить("ПометкаУдаления", Ложь);
    КонецЕсли;

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

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

Различия в механизмах выпадающего списка и формы выбора (F4)

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

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

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

Временное решение: Ручная фильтрация через обработчик "После"

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


// В МодулеМенеджера Справочника Пользователи
// ...
&После("ОбработкаПолученияДанныхВыбора")
Процедура XD_ОбработкаПолученияДанныхВыбора(ДанныеВыбора, Параметры, СтандартнаяОбработка)

    Если НЕ Параметры.Свойство("Рекурсия") Тогда
        Параметры.Вставить("Рекурсия");

        Если Параметры.Свойство("Отбор") Тогда
            // Отменяем стандартную обработку, так как будем формировать список вручную
            СтандартнаяОбработка = Ложь;

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

            Индекс = СтандартныйСписок.Количество - 1;
            Пока Индекс >= 0 Цикл
                Эл = СтандартныйСписок.Получить(Индекс);

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

                Индекс = Индекс - 1;
            КонецЦикла;

            // Передаем отфильтрованный список в ДанныеВыбора
            ДанныеВыбора = СтандартныйСписок;
        КонецЕсли;
    КонецЕсли;

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

Разберем предложенное решение по шагам:

  1. &После("ОбработкаПолученияДанныхВыбора"): Директива &После позволяет нам выполнить наш код после того, как завершится выполнение основной процедуры ОбработкаПолученияДанныхВыбора. Это дает возможность получить уже сформированный (пусть и некорректно отфильтрованный) список.
  2. Флаг Рекурсия: Внутри нашего обработчика мы снова вызываем функцию ПолучитьДанныеВыбора(Параметры). Без флага Рекурсия это привело бы к бесконечному циклу, так как каждый вызов ПолучитьДанныеВыбора снова и снова вызывал бы наш обработчик XD_ОбработкаПолученияДанныхВыбора. Добавление свойства Рекурсия в Параметры позволяет отслеживать, что мы уже находимся в процессе обработки, и избежать повторного вызова.
  3. Ручная фильтрация: После получения "сырого" списка в переменную СтандартныйСписок, мы проходим по нему в обратном порядке (чтобы избежать проблем с изменением индекса при удалении элементов) и вручную проверяем каждый элемент на соответствие условиям, указанным в Параметры.Отбор. Если элемент не соответствует условию, он удаляется из списка.
  4. СтандартнаяОбработка = Ложь: Мы устанавливаем этот флаг в Ложь, чтобы сообщить платформе, что мы полностью взяли на себя формирование списка данных выбора, и ей не нужно выполнять свою стандартную логику.

Критика и недостатки предложенного решения

Хотя предложенное решение является рабочим обходом, оно имеет существенные недостатки, на которые было справедливо указано в обсуждении:

  1. Высокая ресурсоемкость ("жирность"): Главный недостаток — это производительность. Метод ПолучитьДанныеВыбора(Параметры) без корректного отбора может вернуть очень большой список элементов. Мы полностью загружаем этот потенциально огромный список в оперативную память на клиенте или сервере (в зависимости от контекста вызова), а затем перебираем его в цикле, чтобы удалить ненужные элементы. Это создает избыточную нагрузку на систему и может привести к замедлению работы, особенно при больших объемах данных.
  2. Чтение объектов из БД: Строка Эл.Значение[ЭлОтбора.Ключ] означает, что для каждого элемента из списка, который обычно содержит ссылки, мы фактически считываем полный объект из базы данных (или по крайней мере получаем доступ к его полям), чтобы проверить значение реквизита. Это гораздо медленнее, чем применение отбора на уровне запроса к БД.
  3. Некорректная обработка сложных отборов: Исходный код предполагает, что значение отбора — это простое значение. Однако свойство Отбор в 1С может содержать массив значений (например, Отбор.Вставить("Номенклатура", Новый Массив("Ссылка1", "Ссылка2"))). В таком случае проверка НЕ Эл.Значение[ЭлОтбора.Ключ] = ЭлОтбора.Значение будет работать некорректно. Для таких случаев требуется более сложная логика сравнения.
  4. Применимость: Данный код нужно вставлять непосредственно в модуль менеджера каждого справочника, для которого требуется такая фильтрация. Это делает решение не универсальным и сложным в поддержке при большом количестве объектов.

Поиск корневой причины: Отладка и анализ кода

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

Шаги по отладке:

  1. Откройте конфигурацию в режиме отладчика.
  2. Установите точку останова в процедуре ОбработкаПолученияДанныхВыбора модуля менеджера того объекта, где возникает проблема (например, справочника "Пользователи") — это удобно через инструмент для пошаговой отладки кода в режиме Предприятие.
  3. В режиме предприятия попробуйте выбрать значение в проблемном поле ввода так, чтобы сработал выпадающий список. Отладчик остановится на вашей точке останова.
  4. Проверьте содержимое Параметры.Отбор. Убедитесь, что там действительно присутствуют нужные вам условия (например, Недействителен = Ложь).
  5. Самый важный шаг: Шагайте по коду с помощью F11. Начните с вызова ПолучитьДанныеВыбора(Параметры), если он есть в вашей процедуре, или просто позвольте платформе продолжить выполнение после вашей точки останова. В процессе выполнения платформенного кода (который может быть переопределен расширениями или подписками), вам нужно внимательно следить, куда переходит управление.
  6. Ищите места переопределения. Ваша задача — найти, где именно происходит изменение Параметры.Отбор или где формируется список данных *без* учета этих параметров. Часто это происходит в:

    • Расширениях конфигурации: Расширения могут содержать процедуры ОбработкаПолученияДанныхВыбора с директивами &Перед, &Вместо или &После. Особенно подозрительны процедуры с &Вместо, так как они полностью заменяют стандартную логику.
    • Подписках на события: В конфигурации могут быть подписки на событие ОбработкаПолученияДанныхВыбора. Проверьте список подписок в дереве конфигурации.
    • Модулях сторонних подсистем/библиотек: В сложных конфигурациях, таких как "Комплексная автоматизация" с подключенными модулями (например, 1С:CRM), очень часто используются собственные механизмы переопределения стандартного поведения. Именно так, в ходе обсуждения, было выяснено, что модуль 1С:CRM активно вмешивается в процесс формирования списка, возможно, используя свои внутренние параметры (например, "CRM_..."), которые имеют приоритет или заменяют стандартный отбор.
  7. Анализируйте найденный код. Как только вы найдете место, где отбор "теряется", вы сможете понять, почему это происходит и как это исправить. Возможно, CRM-модуль добавляет свой собственный отбор, который конфликтует с вашим, или полностью игнорирует входящие параметры, формируя список по своим правилам.

Архитектурно верный подход: Подписки на событие "ОбработкаПолученияДанныхВыбора"

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

Применение подписки:

  1. Создайте новую подписку на событие. В дереве конфигурации найдите раздел "Подписки на события" и создайте новую.
  2. Укажите имя и модуль-обработчик. Дайте подписке осмысленное имя (например, "УстановкаОтбораДляПользователей") и создайте новый модуль для обработчика (например, "МодульОбработкиВыбора").
  3. Выберите событие: ОбработкаПолученияДанныхВыбора.
  4. Укажите источник: Выберите те объекты-менеджеры, для которых вы хотите применять отбор (например, СправочникМенеджер.Пользователи, СправочникМенеджер.Контрагенты и т.д.). Вы можете выбрать несколько источников, если нужно применять одну и ту же логику отбора.
  5. Реализуйте логику в модуле-обработчике:

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

Преимущества этого подхода:

Управление историей выбора

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

Заключение

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

  1. Временный обход с ручной фильтрацией: Рабочий, но крайне неэффективный метод, который следует использовать только как временное решение или для диагностики.
  2. Системный поиск корневой причины через отладчик (F11): Это наиболее правильный и рекомендуемый подход, позволяющий точно определить, где и почему происходит нарушение стандартной логики отбора.
  3. Архитектурно верное решение с подписками на событие ОбработкаПолученияДанныхВыбора: Этот подход позволяет внести корректировки в Параметры.Отбор до того, как данные будут фактически получены из базы, что обеспечивает оптимальную производительность и удобство поддержки.

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

← На главную