Как настроить отбор в поле выбора по реквизиту табличной части в 1С?

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

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

При попытке настроить стандартное свойство поля ПараметрыВыбора через конфигуратор или программно через конструкцию вида Новый ПараметрВыбора("Отбор.ИмяТЧ.ИмяРеквизита", Значение) платформа выдает синтаксическую ошибку либо отбор попросту игнорируется. Разберем причины такого поведения платформы и рассмотрим рабочие способы решения этой задачи.

Почему не работает стандартный механизм «ПараметрыВыбора»?

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

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

Способ 1. Перехват события «НачалоВыбора» с предварительным отбором ссылок

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

Разберем по шагам реализацию данного варианта в модуле управляемой формы:

  1. Создаем обработчик события элемента формы НачалоВыбора.
  2. Устанавливаем параметр СтандартнаяОбработка = Ложь, чтобы отменить открытие стандартного окна выбора.
  3. Выполняем запрос к табличной части справочника на сервере и получаем массив подходящих ссылок.
  4. Передаем полученный массив в параметры открытия формы выбора через структуру отбора с видом сравнения ВСписке.

Посмотрим на пример программного кода для клиентской и серверной части формы:


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

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

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

Способ 2. Произвольный запрос в динамическом списке формы выбора

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

Проанализируем структуру запроса динамического списка формы выбора:


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

При открытии формы значение параметра устанавливается в обработчике ПриСозданииНаСервере формы выбора:


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

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

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

Рассмотрим, как устроен этот механизм:

  1. В поле формы передаем нужный критерий через структуру ПараметрыПолученияДанныхВыбора.
  2. В модуле менеджера справочника перехватываем обработку, анализируем входящие параметры, выполняем запрос к табличной части с учетом введенной пользователем строки поиска и возвращаем сформированный СписокЗначений.
  3. Устанавливаем СтандартнаяОбработка = Ложь.

Посмотрим на пример кода в модуле менеджера справочника:


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

Рекомендации по производительности и безопасности данных

При реализации любого из предложенных вариантов обратите внимание на следующие технические аспекты:

  1. Индексация реквизитов табличной части: соединения и подзапросы по табличным частям (Ссылка В (ВЫБРАТЬ...)) в больших информационных базах вызывают существенную нагрузку на СУБД. Убедитесь, что для реквизита табличной части, по которому выполняется поиск, в свойствах метаданных включено свойство Индексировать (или Индексировать с доп. упорядочиванием).
  2. Контроль целостности: любые интерфейсные отборы ограничивают пользователя только в момент интерактивного выбора. Они не защищают от программной записи неподходящих значений, обменов данными или копирования строк документов. Если правило отбора является строгим бизнес-требованием, обязательно дублируйте проверку в обработчиках ПередЗаписью или ОбработкаПроверкиЗаполнения соответствующего объекта.
← На главную