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