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