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