Как полностью ограничить список выбора при вводе по строке в управляемой форме 1С?

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

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

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

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

Выясняем причину: почему платформа игнорирует ручной отбор?

Рассмотрим стандартную реализацию переопределения ввода по строке, которую часто используют программисты — её кастомизация удобнее через расширение возможностей автоподбора в полях ввода. В модуле менеджера справочника (в нашем случае — «Должности») размещается следующий код:


Процедура ОбработкаПолученияДанныхВыбора(ДанныеВыбора, Параметры, СтандартнаяОбработка)
    
    ДанныеВыбора = Новый СписокЗначений;
    
    Запрос = Новый Запрос;
    Запрос.Текст = "
    |ВЫБРАТЬ
    |    Должности.Ссылка КАК Ссылка,
    |    Должности.Наименование КАК Наименование
    |ИЗ
    |    Справочник.Должности КАК Должности
    |ГДЕ
    |    Должности.Наименование ПОДОБНО &Наименование + ""%""
    |    И НЕ Должности.Наименование ПОДОБНО ""%(%""
    |    И НЕ Должности.Наименование ПОДОБНО ""%к.%""
    |    И НЕ Должности.ПометкаУдаления
    |{ГДЕ
    |    Должности.Наименование}
    |";
    
    Запрос.УстановитьПараметр("Наименование", Параметры.СтрокаПоиска);
    
    Выборка = Запрос.Выполнить().Выбрать();
    Пока Выборка.Следующий() Цикл
         ДанныеВыбора.Добавить(Выборка.Ссылка, Выборка.Наименование);    
    КонецЦикла; 
    
    СтандартнаяОбработка = Ложь;    
    
КонецПроцедуры

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

Тем не менее, при тестировании поведения в режиме «1С:Предприятие» при вводе, например, букв «Ме», пользователь видит в выпадающем списке не только отфильтрованные данные (например, «Методист»), но и «Менеджер» (который был исключен логикой запроса), причем слово «Менеджер» отображается с подсветкой совпадения «Ме».

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

Решение 1. Отключение истории выбора на уровне элемента формы

Согласно стандартам разработки фирмы 1С (требования ИТС), если для объекта метаданных переопределен обработчик ОбработкаПолученияДанныхВыбора, свойство поля ввода «История выбора при вводе» необходимо принудительно устанавливать в значение «Не использовать». Это позволяет избежать ситуации, когда пользователь выбирает из истории элемент, который более не удовлетворяет условиям отбора (например, был заблокирован, помечен на удаление или скрыт настройками прав доступа RLS).

Рассмотрим, как отключить историю выбора на форме. Сделать это можно двумя путями: интерактивно и программно.

Интерактивный метод в конфигураторе:

  1. Откроем нужную управляемую форму в конфигураторе.
  2. Выберем поле ввода, для которого переопределен автоподбор (например, поле ввода реквизита «Должность»).
  3. Откроем палитру свойств этого элемента (клавиша F4).
  4. Найдем категорию свойств «Использование».
  5. Установим свойство «История выбора при вводе» (ChoiceHistoryOnInput) в значение «Не использовать» (по умолчанию там стоит значение «Авто»).

Программный метод в коде формы:

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


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

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

Решение 2. Корректировка юзабилити при отключении истории

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

Решение 3. Глобальное отключение на уровне метаданных справочника

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

  1. Найдем справочник «Должности» в дереве метаданных.
  2. Откроем окно его свойств.
  3. Перейдем на вкладку «Ввод».
  4. В поле «История выбора при вводе» выберем значение «Не использовать».

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

Решение 4. Принудительная очистка накопленной "мусорной" истории пользователей

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

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


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

Альтернативный вариант: Фильтрация на уровне формы через событие «АвтоПодбор»

Иногда нам требуется ограничить выбор должностей не глобально для всей системы, а только в одной конкретной форме (например, в документе «Прием на работу» показывать только производственные должности, а в документе «Договор ГПХ» — только внештатные). В таких случаях переопределять модуль менеджера справочника не рекомендуется. Вместо этого мы можем использовать локальное событие элемента формы АвтоПодбор.

Разберем этот метод по шагам:


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

&НаСервереБезКонтекста
Функция ПолучитьСписокДолжностейНаСервере(Знач ТекстПоиска)
    
    СписокРезультатов = Новый СписокЗначений;
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    Должности.Ссылка КАК Ссылка,
        |    Должности.Наименование КАК Наименование
        |ИЗ
        |    Справочник.Должности КАК Должности
        |ГДЕ
        |    Должности.Наименование ПОДОБНО &Наименование + ""%""
        |    И НЕ Должности.ПометкаУдаления";
        
    Запрос.УстановитьПараметр("Наименование", ТекстПоиска);
    
    Выборка = Запрос.Выполнить().Выбрать();
    Пока Выборка.Следующий() Цикл
        СписокРезультатов.Добавить(Выборка.Ссылка, Выборка.Наименование);
    КонецЦикла;
    
    Возврат СписокРезультатов;
    
КонецФункции

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

Продвинутый сценарий: Обход рекурсии при использовании метода «ПолучитьДанныеВыбора»

Иногда перед разработчиком стоит задача не полностью переписать алгоритм поиска, а лишь слегка скорректировать («причесать») стандартный результат выдачи платформы. Например, получить список, который платформа нашла по своим сложным алгоритмам (с учетом полнотекстового поиска, синонимов и т.д.), и затем отфильтровать его по дополнительным условиям.

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

Проанализируем пример безопасного вызова с использованием специального флага-маркера в параметрах:


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

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

Резюме и чеклист разработчика

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

  1. Если вы переопределяете поиск в модуле менеджера через ОбработкаПолученияДанныхВыбора, всегда отключайте свойство «История выбора при вводе» для соответствующего поля на форме или в свойствах самого объекта метаданных.
  2. Для улучшения UX-характеристик поля ввода, лишенного истории, убирайте кнопку выпадающего списка (КнопкаСписка = Нет) и оставляйте классическую кнопку выбора (КнопкаВыбора = Да).
  3. Если у пользователей продолжает всплывать старая история, воспользуйтесь программной очисткой хранилища настроек по ключу "ИсторияВыбора".
  4. Если ограничение актуально только для одного рабочего места (одной формы), делайте фильтрацию локально в событии АвтоПодбор на клиенте и сервере формы, не затрагивая глобальный модуль менеджера.
← На главную