Как ограничить выбор номенклатуры определенной группой в управляемых формах 1С 8.3?

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

При разработке документов и справочников часто возникает типовая прикладная задача: разрешить пользователю выбирать номенклатуру только из определенной папки (группы справочника) или конкретного вида. В обычном приложении для этого использовался метод ПолучитьФормуВыбора() и прямое обращение к элементам формы. Однако при переходе на управляемые формы старый код перестает работать, вызывая ошибки на клиенте и сервере. Разберем по шагам, как правильно решить эту задачу на платформе 1С:Предприятие 8.3, проанализируем частые ошибки разработчиков и реализуем комплексную защиту от неверного ввода.

Типичные ошибки при переводе кода на управляемые формы

Рассмотрим частые проблемы, с которыми сталкиваются разработчики при попытке настроить отбор:

  1. Попытка получения формы выбора на сервере: Контекст управляемых форм строго разделен. Методы открытия и получения форм принадлежат клиентскому контексту, а прямое обращение к элементам формы через серверный вызов недопустимо.
  2. Использование несуществующего поля Группа: В структуре метаданных иерархического справочника нет реквизита с именем «Группа». Иерархия строится на системном реквизите Родитель и флаге ЭтоГруппа. Попытка наложить отбор по полю Группа приводит к ошибке dynamic list: «Поле не найдено "Группа"».
  3. Использование поля Владелец: Поле Владелец присутствует только у подчиненных справочников (например, договоры контрагентов подчинены контрагенту) и не имеет отношения к иерархическим папкам одного каталога.
  4. Отбор только первого уровня: Стандартный отбор через структуру параметров Отбор.Вставить("Родитель", Группа) использует строгое равенство. Если внутри выбранной группы есть подпапки, элементы из этих подпапок отображаться не будут.

Способ 1. Открытие формы выбора с простым отбором по Родителю

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

Проанализируем правильный клиент-серверный код:


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

КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьПараметрыВыбораНаСервере()
    
    // Получаем ссылку на требуемую группу
    Группа = Справочники.Номенклатура.НайтиПоКоду("U0000137672");
    
    Отбор = Новый Структура;
    Отбор.Вставить("Родитель", Группа);
    
    ПараметрыФормы = Новый Структура;
    ПараметрыФормы.Вставить("Отбор", Отбор);
    
    Возврат ПараметрыФормы;

КонецФункции

Обратите внимание на третий параметр метода ОткрытьФорму() — мы передаем Элемент (элемент формы, для которого вызван выбор). Это необходимо для того, чтобы платформа знала, куда вернуть выбранное значение и вызвала стандартное оповещение об изменении реквизита.

Способ 2. Отбор с учетом всей иерархии (включая вложенные подпапки)

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


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

КонецПроцедуры

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

КонецФункции

Способ 3. Отбор без программирования (по реквизиту или виду)

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

  1. Откроем форму документа в конфигураторе и выделим поле ввода номенклатуры.
  2. В палитре свойств найдем свойство Параметры выбора.
  3. Добавим строку: имя параметра — Отбор.ВидНоменклатуры, а в качестве значения выберем нужный элемент справочника видов номенклатуры.

Данная настройка платформы автоматически передает отбор в форму выбора и, в отличие от события НачалоВыбора, накладывает ограничения на стандартный ввод по строке.

Защита от обхода отбора: ввод по строке и проверка при записи

Переопределение обработчика НачалоВыбора решает задачу только при интерактивном нажатии на кнопку с троеточием или клавишу F4. Однако пользователь может:

  1. Набрать наименование или артикул вручную с клавиатуры (сработает автоподбор по строке).
  2. Скопировать запрещенную номенклатуру через буфер обмена.
  3. Выбрать значение из истории недавнего ввода.

Для создания надежного решения применим комплексный подход.

Шаг 1. Ограничение быстрого ввода на форме. В палитре свойств элемента формы выключим КнопкаВыпадающегоСписка, а для свойства ИсторияВыбораПриВводе установим значение Не использовать.

Шаг 2. Контроль в модуле менеджера. Для фильтрации всплывающих подсказок при вводе текста используем экспортную процедуру ОбработкаПолученияДанныхВыбора в модуле менеджера справочника Номенклатура.

Шаг 3. Обязательный контроль целостности данных при записи. Никакие интерфейсные ограничения не защищают от записи документа программным путем или через групповую обработку. Добавим обязательную проверку в модуле объекта документа в событии ОбработкаПроверкиЗаполнения:


Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
    
    РазрешеннаяГруппа = Справочники.Номенклатура.НайтиПоКоду("U0000137672");
    
    Для Каждого Стр Из Товары Цикл
        
        Если ЗначениеЗаполнено(Стр.Номенклатура) Тогда
            
            // Метод ПринадлежитЭлементу проверяет всю цепочку иерархии без лишних запросов
            Если Не Стр.Номенклатура.ПринадлежитЭлементу(РазрешеннаяГруппа) Тогда
                
                Сообщение = Новый СообщениеПользователю;
                Сообщение.Текст = "Номенклатура """ + Стр.Номенклатура + """ не входит в разрешенную группу!";
                Сообщение.Поле = "Товары[" + Формат(Товары.Индекс(Стр), "ЧГ=0") + "].Номенклатура";
                Сообщение.УстановитьДанные(ЭтотОбъект);
                Сообщение.Сообщить();
                
                Отказ = Истина;
                
            КонецЕсли;
            
        КонецЕсли;
        
    КонецЦикла;

КонецПроцедуры

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

← На главную