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