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