Как ограничить выбор студентов в поле ввода только теми, кто указан в конкретном документе

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

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

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

Постановка задачи и выбор события

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

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

Решение через событие НачалоВыбора

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

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


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

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

Важное замечание по запросу: обратите внимание на условие ПосещениеСтуденты.Студент <> ЗНАЧЕНИЕ(Справочник.Студенты.ПустаяСсылка). В языке запросов 1С для ссылочных типов проверка на наличие значения выполняется именно так. Использование ЕСТЬ НЕ NULL корректно только при левых соединениях, когда записи во второй таблице может не существовать физически. В табличной части пустая строка — это именно пустая ссылка, а не NULL.

Улучшение интерфейса: Использование АвтоПодбора

Часто пользователи жалуются, что список обновляется только при нажатии на кнопку «Показать все». Чтобы фильтрация работала «на лету» (при наборе первых букв имени студента), следует использовать событие АвтоПодбор.

Логика работы в АвтоПодбор идентична НачалоВыбора, но это позволяет системе предлагать варианты из вашего списка сразу в выпадающем меню под полем ввода. Рассмотрим особенности реализации:


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

Режим выбора из списка: борьба со «лишними» значениями

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

  1. Установите в свойствах поля ввода РежимВыбораИзСписка = Истина.
  2. При изменении реквизита Документ на форме, вызывайте процедуру обновления списка.

&НаСервере
Процедура ОбновитьСписокВыбораНаСервере()
    
    Список = ПолучитьСписокСтудентовИзДокумента(Объект.Документ);
    
    // Очищаем текущий список выбора элемента формы и заполняем заново
    ЭлементСтудент = Элементы.Студент;
    ЭлементСтудент.СписокВыбора.Очистить();
    Для Каждого ТекЗначение Из Список Цикл
        ЭлементСтудент.СписокВыбора.Добавить(ТекЗначение.Значение);
    КонецЦикла;
    
КонецПроцедуры

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

Архитектурный совет: Почему запросы к документам — это не всегда хорошо

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

Рекомендуемый подход: Если такая проверка требуется часто, правильнее будет при проведении документа «Посещение» записывать данные о составе студентов в Регистр сведений (например, «СоставПосещений»). Тогда в функции ПолучитьСписокСтудентовИзДокумента мы будем делать запрос не к табличной части документа, а к виртуальной таблице или физической таблице регистра сведений. Это обеспечит стабильную скорость работы системы даже при огромных объемах данных.

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

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

Разберем алгоритм:

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

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

Такой подход считается наиболее «чистым» с точки зрения методологии 1С, так как логика выбора данных отделена от логики отображения конкретной формы.

Подведем итог: для быстрого решения используйте связку НачалоВыбора и АвтоПодбор с корректным запросом (проверка на ПустаяСсылка). Для создания максимально жесткого и понятного интерфейса используйте СписокВыбора и РежимВыбораИзСписка. А для крупных систем всегда рассматривайте вариант с хранением данных в регистрах сведений.

← На главную