Как ограничить выбор групп справочника конкретным родителем при вводе текста в 1С?

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

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

В этой статье мы разберем, как программно ограничить подбор групп, чтобы 1С предлагала варианты только из определенной ветки дерева справочника, и проанализируем различные способы реализации этой задачи в зависимости от типа форм и требований к производительности.

Способ 1. Использование события «Окончание ввода текста»

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

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


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

Функция ПринадлежитРодителю(ПроверяемаяСсылка, ЦелевойРодитель)
    ТекущийОбъект = ПроверяемаяСсылка;
    Пока НЕ ТекущийОбъект.Родитель.Пустая() Цикл
        Если ТекущийОбъект.Родитель = ЦелевойРодитель Тогда
            Возврат Истина;
        КонецЕсли;
        ТекущийОбъект = ТекущийОбъект.Родитель;    
    КонецЦикла;
    Возврат Ложь;
КонецФункции

Важное замечание по производительности: Данный метод (с циклом и функцией ПринадлежитРодителю) может работать медленно, если по запросу ПОДОБНО находится много групп. Это связано с тем, что обращение к Родитель через точку внутри цикла порождает отдельные запросы к базе данных (проблема N+1). Если товаров и групп десятки тысяч, интерфейс может «зависать».

Способ 2. Оптимизированный запрос с использованием «В ИЕРАРХИИ»

Чтобы ускорить процесс, мы можем перенести всю логику фильтрации на сторону сервера базы данных, используя оператор В ИЕРАРХИИ. Это позволит получить уже отфильтрованный список за один подход.

Разберем, как изменить запрос для повышения эффективности:


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

Проанализируем этот подход: оператор В ИЕРАРХИИ в 1С работает достаточно быстро на платформе 8.3, так как система оптимизирует такие запросы. Использование конструкции ПОДОБНО &Текст + "%" (без знака процента в начале) позволяет системе использовать индекс по наименованию, что крайне важно для скорости поиска.

Способ 3. Использование события «ОбработкаПолученияДанныхВыбора» (для Управляемых форм)

Если вы работаете в современной конфигурации на управляемых формах (например, «Розница 8.3»), наиболее правильным и системным решением будет использование обработчика в модуле менеджера справочника Номенклатура.

Рассмотрим преимущества этого метода: он работает глобально для всех полей ввода, где выбирается номенклатура, и позволяет централизованно управлять логикой подбора.

  1. Откройте модуль менеджера справочника Номенклатура.
  2. Найдите или создайте процедуру ОбработкаПолученияДанныхВыбора.
  3. Реализуйте фильтрацию, используя параметры выбора.

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

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


НовыйПараметр = Новый ПараметрВыбора("ОтборПоРодителюИгрушки", Истина);
МассивПараметров = Новый Массив;
МассивПараметров.Добавить(НовыйПараметр);
Элементы.ПолеРодитель.ПараметрыВыбора = Новый ФиксированныйМассив(МассивПараметров);

Рекомендации по оптимизации

Выясним, что еще можно сделать, если справочник содержит сотни тысяч записей и поиск все равно кажется медленным:

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

← На главную