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