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

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

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

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

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

Представим ситуацию: у нас есть документ, где заполняется реквизит Врач и реквизит Услуга. Нам необходимо, чтобы при нажатии на кнопку выбора в поле Услуга открывался справочник, в котором видны только те позиции, которые этот конкретный врач имеет право оказывать согласно своей должности. Данные о том, какая должность какие услуги оказывает, хранятся в регистре сведений УслугиПоДолжностям, а текущая должность врача — в регистре КадроваяИстория.

Рассмотрим пошаговый алгоритм реализации этой задачи:

  1. Перехват события начала выбора в поле ввода.
  2. Получение списка разрешенных услуг на сервере с помощью запроса.
  3. Программное открытие формы выбора справочника Услуги.
  4. Установка фильтра в динамическом списке открытой формы.
  5. Обработка результата выбора и запись его в документ.

Шаг 1. Получение списка разрешенных услуг на сервере

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

Проанализируем пример кода серверной функции:


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

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

Шаг 2. Реализация события НачалоВыбора

Теперь перейдем к клиентской части. Нам нужно создать обработчик события НачалоВыбора для элемента формы Услуга. Здесь мы должны остановить стандартную обработку 1С, чтобы форма выбора не открылась сама по себе с настройками по умолчанию.

Рассмотрим подробнее логику процедуры:


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

Здесь ключевым моментом является использование ФиксированныеНастройки. В отличие от обычных настроек компоновщика, фиксированные настройки не могут быть изменены пользователем вручную через меню "Настроить список", что гарантирует соблюдение бизнес-логики.

Шаг 3. Обработка результата выбора

Поскольку мы использовали ОткрытьФорму с указанием владельца (параметр Элемент), система сама может подставить выбранное значение в поле. Однако хорошим тоном считается использование процедуры обратного вызова (Callback), которую мы указали в ОписаниеОповещения.


&НаКлиенте
Процедура ПослеВыбораУслуги(РезультатВыбора, Параметры) Экспорт
    
    Если РезультатВыбора <> Неопределено Тогда
        Объект.Услуга = РезультатВыбора;
    КонецЕсли;
    
КонецПроцедуры

Альтернативные способы реализации

Выясним, существуют ли другие способы решения, которые могут быть удобнее в определенных сценариях:

  1. Связи параметров выбора (ChoiceParameterLinks): В конфигураторе для реквизита Услуга можно настроить связь по полю Врач. Однако это работает только если в справочнике Услуги есть прямой реквизит Врач. В нашей ситуации, когда связь идет через регистр, этот метод напрямую не применим.
  2. Параметры выбора (ChoiceParameters): Можно передать фиксированный отбор прямо в структуру ПараметрыФормы при вызове ОткрытьФорму. Это выглядит так:
    
    ОтборСписка = Новый Структура("Ссылка", СписокРазрешенныхУслуг);
    ПараметрыФормы = Новый Структура("Отбор", ОтборСписка);
    ОткрытьФорму("Справочник.Услуги.ФормаВыбора", ПараметрыФормы, Элемент);
    
    Это более лаконичный код, но он работает только на простое равенство или вхождение в массив (в зависимости от версии платформы).
  3. Событие ОбработкаПолученияДанныхВыбора: Если вы хотите, чтобы отбор работал и при вводе названия услуги строкой (автоподбор), логику получения списка лучше вынести в Модуль менеджера справочника Услуги в предопределенную процедуру ОбработкаПолученияДанныхВыбора.

Важные рекомендации по оптимизации

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

1. Очистка зависимого поля. Если пользователь сначала выбрал Врача А и Услугу А, а затем изменил врача на Врача Б, которому Услуга А недоступна — необходимо предусмотреть очистку поля Услуга в событии ПриИзменении поля Врач.

2. Списки выбора. Если услуг у врача обычно немного (5-10 штук), вместо открытия целой формы справочника можно использовать Список выбора. В этом случае в событии НачалоВыбора вы заполняете массив ДанныеВыбора, и пользователю выпадает компактный список прямо под полем ввода.

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

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

← На главную