При разработке документов в управляемом интерфейсе 1С 8.3 часто возникает задача ограничить список выбора в табличной части в зависимости от реквизита шапки. Например, при заполнении колонки Сотрудник необходимо показывать только тех работников, которые числятся в выбранной в документе Организации. Попытка переопределить это поведение вручную через событие АвтоПодбор нередко приводит к неожиданным ошибкам: в выпадающем списке продолжают появляться сотрудники из других организаций либо список вовсе перестает открываться.
Разберем детально, почему так происходит, какие скрытые механизмы платформы влияют на состав выпадающего списка и как правильно решить эту задачу несколькими способами.
Рассмотрим код, с которого начинаются подобные проблемы:
&НаКлиенте
Процедура СотрудникиСотрудникАвтоПодбор(Элемент, Текст, ДанныеВыбора, ПараметрыПолученияДанных, Ожидание, СтандартнаяОбработка)
Если НЕ Объект.Организация.Пустая() Тогда
СтандартнаяОбработка = Ложь;
ДанныеВыбора = ПолучитьДанныеВыбораСотрудников(Текст, Объект.Организация);
Иначе
СтандартнаяОбработка = Истина;
КонецЕсли;
КонецПроцедуры
При реализации серверной функции получения данных разработчики часто допускают ряд критических ошибок:
СправочникСсылка.Сотрудники), то коллекция ДанныеВыбора типа СписокЗначений должна содержать в свойстве Значение именно ссылку на элемент справочника, а не его текстовое наименование. Если поместить туда строку, платформа не сможет сопоставить результат с типом поля и либо задействует стандартный механизм поиска, игнорируя отбор, либо очистит поле при завершении ввода.РегистрСведений.КадроваяИсторияСотрудников.СрезПоследних без передачи даты среза приводит к получению данных на текущую дату, а не на дату документа. Кроме того, последнее событие может оказаться увольнением из указанной организации. Некорректное соединение с интервальным регистром без наложения фильтра по периодам также размножает строки.АвтоПодбор генерируется при наборе каждого символа. Выполнение тяжелых запросов к базе данных при каждом нажатии клавиши создает неоправданную нагрузку на сервер.В большинстве типовых и нетиповых конфигураций в справочнике сотрудников уже присутствует реквизит ГоловнаяОрганизация или Организация. Платформа 1С позволяет настроить фильтрацию вообще без написания программного кода.
Разберем настройку по шагам:
СотрудникиСотрудник.Отбор.ГоловнаяОрганизация (или Отбор.Организация), а в качестве реквизита укажем Объект.Организация.При такой настройке платформа автоматически применяет отбор и при интерактивном открытии формы выбора, и при вводе текста по строке.
Если в справочнике сотрудника нет прямой привязки к организации (актуальное место работы определяется только движениями по регистрам сведений), сформируем отбор программно при изменении реквизита шапки.
Посмотрим на пример реализации в модуле формы документа:
&НаСервере
Процедура УстановитьОтборСотрудниковПоОрганизации()
Если Объект.Организация.Пустая() Тогда
Элементы.СотрудникиСотрудник.ПараметрыВыбора = Новый ФиксированныйМассив(Новый Массив);
Возврат;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| КадроваяИстория.Сотрудник КАК Сотрудник
|ИЗ
| РегистрСведений.КадроваяИсторияСотрудников.СрезПоследних(&Период, Организация = &Организация) КАК КадроваяИстория
|ГДЕ
| НЕ КадроваяИстория.Сотрудник.ПометкаУдаления
| И КадроваяИстория.ВидСобытия <> ЗНАЧЕНИЕ(Перечисление.ВидыКадровыхСобытий.Увольнение)";
Запрос.УстановитьПараметр("Организация", Объект.Организация);
Запрос.УстановитьПараметр("Период", Объект.Дата);
МассивСотрудников = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Сотрудник");
НовыйПараметр = Новый ПараметрВыбора("Отбор.Ссылка", Новый ФиксированныйМассив(МассивСотрудников));
МассивПараметров = Новый Массив;
МассивПараметров.Добавить(НовыйПараметр);
Элементы.СотрудникиСотрудник.ПараметрыВыбора = Новый ФиксированныйМассив(МассивПараметров);
КонецПроцедуры
&НаКлиенте
Процедура ОрганизацияПриИзменении(Элемент)
УстановитьОтборСотрудниковПоОрганизации();
КонецПроцедуры
&НаКлиенте
Процедура ПриОткрытии(Отказ)
УстановитьОтборСотрудниковПоОрганизации();
КонецПроцедуры
Преимущество этого подхода заключается в том, что запрос к регистру выполняется всего один раз — при смене организации или при открытии документа, а не при каждом вводе буквы с клавиатуры. Ввод по строке и стандартная форма выбора будут работать быстро и с учетом заданного списка.
Если требуется динамический поиск с нестандартным сопоставлением текста, логику фильтрации следует переносить не в форму, а в модуль менеджера соответствующего объекта с помощью обработчика ОбработкаПолученияДанныхВыбора.
Рассмотрим структуру реализации в модуле менеджера справочника Сотрудники:
Процедура ОбработкаПолученияДанныхВыбора(ДанныеВыбора, Параметры, СтандартнаяОбработка)
// Проверяем, передан ли отбор по организации через параметры формы
Если Параметры.Отбор.Свойство("Организация") И ЗначениеЗаполнено(Параметры.Отбор.Организация) Тогда
СтандартнаяОбработка = Ложь;
ДанныеВыбора = Новый СписокЗначений;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| КадроваяИстория.Сотрудник КАК Сотрудник,
| КадроваяИстория.Сотрудник.Наименование КАК Представление
|ИЗ
| РегистрСведений.КадроваяИсторияСотрудников.СрезПоследних(&Период, Организация = &Организация) КАК КадроваяИстория
|ГДЕ
| НЕ КадроваяИстория.Сотрудник.ПометкаУдаления
| И КадроваяИстория.Сотрудник.Наименование ПОДОБНО &СтрокаПоиска + ""%""";
Запрос.УстановитьПараметр("Организация", Параметры.Отбор.Организация);
Запрос.УстановитьПараметр("Период", ТекущаяДатаСеанса());
Запрос.УстановитьПараметр("СтрокаПоиска", Параметры.СтрокаПоиска);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Важно: в Значение передаем ссылку, а в Представление — строку
ДанныеВыбора.Добавить(Выборка.Сотрудник, Выборка.Представление);
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Если алгоритм требует обязательного использования события АвтоПодбор непосредственно в форме, устраним допущенные ошибки: корректно заполним СписокЗначений ссылками и отключим системную историю выбора.
Посмотрим на правильный рабочий код формы:
&НаКлиенте
Процедура ПриОткрытии(Отказ)
// Отключаем подмешивание ранее выбираемых пользователей
Элементы.СотрудникиСотрудник.ИсторияВыбораПриВводе = ИсторияВыбораПриВводе.НеИспользовать;
КонецПроцедуры
&НаКлиенте
Процедура СотрудникиСотрудникАвтоПодбор(Элемент, Текст, ДанныеВыбора, ПараметрыПолученияДанных, Ожидание, СтандартнаяОбработка)
Если НЕ Объект.Организация.Пустая() И СтрДлина(СокрЛП(Текст)) >= 2 Тогда
СтандартнаяОбработка = Ложь;
ДанныеВыбора = ПолучитьДанныеВыбораСотрудниковНаСервере(Текст, Объект.Организация, Объект.Дата);
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьДанныеВыбораСотрудниковНаСервере(Текст, Организация, ДатаДокумента)
Результат = Новый СписокЗначений;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| КадроваяИстория.Сотрудник КАК Сотрудник,
| КадроваяИстория.Сотрудник.Наименование КАК Наименование
|ИЗ
| РегистрСведений.КадроваяИсторияСотрудников.СрезПоследних(&Период, Организация = &Организация) КАК КадроваяИстория
|ГДЕ
| НЕ КадроваяИстория.Сотрудник.ПометкаУдаления
| И КадроваяИстория.Сотрудник.Наименование ПОДОБНО &Наименование + ""%""";
Запрос.УстановитьПараметр("Наименование", Текст);
Запрос.УстановитьПараметр("Организация", Организация);
Запрос.УстановитьПараметр("Период", ?(ЗначениеЗаполнено(ДатаДокумента), ДатаДокумента, ТекущаяДатаСеанса()));
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Передаем ссылку в значение, а текст в представление элемента списка
Результат.Добавить(Выборка.Сотрудник, Выборка.Наименование);
КонецЦикла;
Возврат Результат;
КонецФункции
Обратите внимание на вызов процедуры с директивой &НаСервереБезКонтекста: это снижает объем передаваемых данных между клиентом и сервером при быстром вводе текста, а проверка минимальной длины строки (от 2 символов) исключает выполнение запроса при случайных нажатиях клавиш.