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