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

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

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

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

Решение 1: Создание отдельных форм выбора (простой, но негибкий способ)

Это решение, к которому автор вопроса пришел самостоятельно. Его суть заключается в создании копий форм для разных сценариев использования.

Рассмотрим по шагам:

  1. Создайте для справочника несколько форм выбора. Например, ФормаВыбораДляТЧ1 и ФормаВыбораДляТЧ2.
  2. В форме документа откройте свойства реквизита в первой табличной части. В свойстве ФормаВыбора укажите созданную для нее форму (ФормаВыбораДляТЧ1).
  3. Повторите то же самое для реквизита второй табличной части, указав другую форму (ФормаВыбораДляТЧ2).
  4. Теперь откройте каждую из созданных форм выбора в конфигураторе. Для основного реквизита формы (динамического списка) настройте условное оформление.
  5. В условном оформлении добавьте новый элемент. В качестве условия укажите, что текущая строка относится к запрещенной группе. Например, условие может быть таким: Список.Родитель = &ЗапрещеннаяГруппа1. В качестве оформления установите свойство Видимость в значение Ложь.

Этот метод рабочий и относительно прост в реализации. Однако у него есть существенный недостаток: дублирование. Если у вас много разных сценариев выбора или логика изменится, придется вносить правки в несколько одинаковых форм, что неудобно и может привести к ошибкам.

Решение 2: Одна универсальная форма с передачей параметров (рекомендуемый способ)

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

Проанализируем процесс по шагам.

Шаг 1. Перехват события начала выбора в документе

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

Например, для реквизита Номенклатура в табличной части Товары код будет выглядеть так:


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

    // Создаем список ЗАПРЕЩЕННЫХ групп для этой табличной части
    СписокЗапрещенныхГрупп = Новый СписокЗначений;
    СписокЗапрещенныхГрупп.Добавить(Справочники.Номенклатура.НайтиПоНаименованию("Группа 1"));
    СписокЗапрещенныхГрупп.Добавить(Справочники.Номенклатура.НайтиПоНаименованию("Группа 3"));
    
    // Готовим параметры для передачи в форму выбора
    ПараметрыФормы = Новый Структура;
    ПараметрыФормы.Вставить("ЗапрещенныеГруппы", СписокЗапрещенныхГрупп);
    
    // Открываем нашу универсальную форму выбора
    ОткрытьФорму("Справочник.Номенклатура.Форма.ФормаВыбораУниверсальная", ПараметрыФормы, Элемент);
КонецПроцедуры

Для второй табличной части (например, Услуги) код будет аналогичным, но мы будем передавать другой список запрещенных групп (например, "Группа 2").

Шаг 2. Настройка универсальной формы выбора

Теперь в нашей универсальной форме выбора (ФормаВыбораУниверсальная) нужно принять переданные параметры и на их основе настроить отбор в динамическом списке.

Это делается в серверном событии формы ПриСозданииНаСервере.


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

            // Группируем условия с логическим "И"
            ГруппаОтбора = Список.Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
            ГруппаОтбора.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИли;
            
            // Первое условие: это не группа
            ЭлементГруппы1 = ГруппаОтбора.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
            ЭлементГруппы1.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ЭтоГруппа");
            ЭлементГруппы1.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
            ЭлементГруппы1.ПравоеЗначение = Ложь;

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

Важный момент: Платформа 1С устроена так, что при установке отбора в иерархическом списке она старается сохранить структуру дерева. Если вы отфильтруете элементы внутри группы, сама группа все равно останется видимой. Чтобы скрыть и сами группы, нужно использовать более надежный вид сравнения НеВСписке для поля Ссылка. Мы комбинируем условия, чтобы показать либо элементы (не группы), либо группы, которые не входят в наш "черный список".

Более надежным может быть отбор по списку разрешенных групп. В этом случае вы передаете в параметрах не запрещенные, а разрешенные группы, и устанавливаете отбор с видом сравнения В Группе Из Списка. Этот метод полностью скроет все, что не входит в указанные иерархии.

Альтернативы: Если нужно не скрыть, а предупредить пользователя

Изначально автор хотел выводить сообщение при попытке раскрытия "запрещенной" группы. Стандартными средствами перехватить событие раскрытия узла в динамическом списке нельзя. Но есть другие способы добиться похожего результата.

1. Проверка после выбора

Можно позволить пользователю выбрать любой элемент, но проверить его корректность уже после выбора, в событии ОбработкаВыбора.


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

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

2. Визуальное выделение

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

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

← На главную