Строка поиска по справочникам "Номенклатура" и "Контрагенты"


Поиск элемента справочника набором фрагментов искомых слов в произвольном порядке.

Здравствуйте, друзья!

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

Если организовать поиск, набирая слова или отрывки слов в произвольном порядке, которые помнишь – вот это другое дело. Для этого мы построим запрос по каждому запрашиваемому фрагменту. Фрагменты искомого названия при подборе будем разделять пробелом.

Для дополнительного удобства сделаем транслитерацию латинского алфавита русским. Это позволит не переключаться между раскладкой клавиатуры и всё писать по-русски. Например, вместо буквы "g" будем смело писать "ж" или "г", кто как эту букву слышит. Когда в строке поиска будет более одной буквы, внизу откроется список, в который попадут первые 10 элементов справочника, названия которых (полное или короткое) полностью включают все набранные фрагменты. Если набор будет уникальным, то значение выберется автоматически.

Есть один нюанс с выпадающим списком поля ввода. Этот список ни сам не раскрывается, ни программных средств его открыть не существует. И когда набираешь искомые слова, не видишь подсказки. Для этого мы сделаем маленькую хитрость: под поле ввода и место выпадения списка выбора подложим таблицу, содержание которой будет повторять содержание списка выбора. И затем будем управлять видимостью этой таблицы: есть выбор - видна, нет выбора - нет таблицы. В таблице первый элемент будет всегда пустым, поэтому это место должно находиться под строкой ввода, с небольшим смещением вниз.

Перем ТаблицаРезультата;

//Каждый набранный символ будет вызывать здесь событие
Процедура СтрокаПоискаАвтоПодборТекста(Элемент, Текст, ТекстАвтоПодбора, СтандартнаяОбработка)
   
ТекстПоиска = СокрЛП(Текст);
   
ЭлементыФормы.СтрокаПоиска.ЦветФонаПоля = Новый Цвет(255, 255, 255);
   
//Менее двух символов за запрос не считаем
   
Если СтрДлина(ТекстПоиска) < 2 Тогда
       
ОчиститьСписки();
        Возврат;
    КонецЕсли;
   
//Составим список из набранных фрагментов
   
СписокФрагментов = Новый СписокЗначений;
   
МестоПробела = 1;
    Пока
МестоПробела > 0 Цикл
       
МестоПробела = Найти(ТекстПоиска, " ");
        Если
МестоПробела > 0 Тогда
           
СписокФрагментов.Добавить(Лев(ТекстПоиска, МестоПробела - 1));
           
ТекстПоиска = СокрЛП(Прав(ТекстПоиска, СтрДлина(ТекстПоиска) - МестоПробела));
        Иначе
           
СписокФрагментов.Добавить(СокрЛП(ТекстПоиска));
        КонецЕсли;
    КонецЦикла;
   
СделатьВыбор(СписокФрагментов);
КонецПроцедуры

//Делаем выбор из выпадающего списка с клавиатуры (стрелки вверх, вниз и enter)
Процедура СтрокаПоискаОбработкаВыбора(Элемент, ВыбранноеЗначение, СтандартнаяОбработка)
   
текИндексСпискаВыбора = Элемент.СписокВыбора.Индекс(Элемент.СписокВыбора.НайтиПоЗначению(ВыбранноеЗначение));
   
ЭлементыФормы.СтрокаПоиска.ЦветФонаПоля = Новый Цвет(240, 255, 240);
   
ОчиститьСписки();
   
ПолучитьИнформацию(ТаблицаРезультата[текИндексСпискаВыбора].Ссылка);
КонецПроцедуры

//Кнопка очистки ещё подчистит все списки
Процедура СтрокаПоискаОчистка(Элемент, СтандартнаяОбработка)
   
ЭлементыФормы.СтрокаПоиска.ЦветФонаПоля = Новый Цвет(255, 255, 255);
   
ОчиститьСписки();
КонецПроцедуры

//Делаем выбор из таблицы выбора (при помощи мыши)
Процедура ТаблицаВыбораПриАктивизацииСтроки(Элемент)
    Если НЕ
ЭлементыФормы.ТаблицаВыбора.Видимость Тогда Возврат КонецЕсли;
   
текИндексТаблицыВыбора = ТаблицаВыбора.Индекс(Элемент.ТекущаяСтрока);
    Если
текИндексТаблицыВыбора = 0 Тогда Возврат КонецЕсли;
   
ЭлементыФормы.СтрокаПоиска.Значение = ЭлементыФормы.СтрокаПоиска.СписокВыбора[текИндексТаблицыВыбора - 1];
   
ЭлементыФормы.СтрокаПоиска.ЦветФонаПоля = Новый Цвет(240, 255, 240);
   
ОчиститьСписки();
   
ПолучитьИнформацию(ТаблицаРезультата[текИндексТаблицыВыбора - 1].Ссылка);
КонецПроцедуры

//По результатам запроса при необходимости заполняем список выбора, таблицу выбора
Процедура СделатьВыбор(СписокФрагментов)
   
//Получаем таблицу с найденными элементами
   
ТаблицаРезультата = ЗапросПоиска(СписокФрагментов);
   
ОчиститьСписки();
   
//Имеем три варианта:
    //Элементы не найдены
   
Если ТаблицаРезультата.Количество() = 0 Тогда
       
ЭлементыФормы.СтрокаПоиска.ЦветФонаПоля = Новый Цвет(255, 216, 128);
   
//Найден один элемент, что и требовалось. Теперь уходим отсюда.
   
ИначеЕсли ТаблицаРезультата.Количество() = 1 Тогда
       
ЭлементыФормы.СтрокаПоиска.ЦветФонаПоля = Новый Цвет(240, 255, 240);
       
ЭлементыФормы.СтрокаПоиска.Значение = ТаблицаРезультата[0].Наименование;
       
ПолучитьИнформацию(ТаблицаРезультата[0].Ссылка);
   
//Найдено много элементов
   
Иначе
       
ЭлементыФормы.СтрокаПоиска.ЦветФонаПоля = Новый Цвет(255, 255, 255);
       
КоличествоНайденныхЭлементов = ТаблицаРезультата.Количество();
        Для
текСтрока = 1 По КоличествоНайденныхЭлементов Цикл
           
ТаблицаВыбора.Добавить();
        КонецЦикла;
       
ТаблицаВыбора.ЗагрузитьКолонку(ТаблицаРезультата.ВыгрузитьКолонку("Наименование"), "Наименование");
       
ТаблицаВыбора.Вставить(0);
       
ЭлементыФормы.СтрокаПоиска.СписокВыбора.ЗагрузитьЗначения(ТаблицаРезультата.ВыгрузитьКолонку("Наименование"));
       
ЭлементыФормы.ТаблицаВыбора.ТекущаяСтрока = ТаблицаВыбора[0];
       
ЭлементыФормы.ТаблицаВыбора.Высота = (КоличествоНайденныхЭлементов + 1) * 19 + 2 ;
       
ЭлементыФормы.СтрокаПоиска.КнопкаСпискаВыбора = Истина;
       
ЭлементыФормы.ТаблицаВыбора.Видимость = Истина;
    КонецЕсли;
КонецПроцедуры

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

//Параллельно кириллице будем искать фрагменты на латинице,
//в двух вариантах из-за различия транслитирации некоторых букв
Функция Транслит(Фрагмент)
   
СписокТранслита = Новый СписокЗначений;
   
Кириллица = "абвгдежзийклмнопрстуфхц";
   
Латиница1 = "abvgdegzijklmnoprstufhc";
   
Латиница2 = "abwgdejzijqlmnoprstufhc";
   
КириллицаДиграф = "ч ш ";
   
ЛатиницаДиграф = "chsh";
   
Транслит1 = "";
   
Транслит2 = "";
    Для
позиция = 1 По СтрДлина(Фрагмент) Цикл
       
текБуква = Сред(Фрагмент, позиция, 1);
       
вШаблоне = Найти(Кириллица, текБуква);
        Если
вШаблоне > 0 Тогда
           
Транслит1 = Транслит1 + Сред(Латиница1, вШаблоне, 1);
           
Транслит2 = Транслит2 + Сред(Латиница2, вШаблоне, 1);
            Продолжить;
        КонецЕсли;
       
вШаблоне = Найти(КириллицаДиграф, текБуква);
        Если
вШаблоне > 0 Тогда
           
Транслит1 = Транслит1 + Сред(ЛатиницаДиграф, вШаблоне, 2);
           
Транслит2 = Транслит2 + Сред(ЛатиницаДиграф, вШаблоне, 2);
            Продолжить;
        КонецЕсли;
       
Транслит1 = Транслит1 + текБуква;
       
Транслит2 = Транслит2 + текБуква;
    КонецЦикла;
   
СписокТранслита.Добавить(Транслит1);
   
СписокТранслита.Добавить(Транслит2);
    Возврат
СписокТранслита;
КонецФункции

//Очистка списка выбора и таблицы выбора
Процедура ОчиститьСписки()
   
ЭлементыФормы.ТаблицаВыбора.Видимость = Ложь;
   
ТаблицаВыбора.Очистить();
   
ЭлементыФормы.СтрокаПоиска.СписокВыбора.Очистить();
   
ЭлементыФормы.СтрокаПоиска.КнопкаСпискаВыбора = Ложь;
КонецПроцедуры

//Здесь будет любая наша обработка результата поиска
Процедура ПолучитьИнформацию(текОбъект)
    Если
ТипЗнч(текОбъект) = Тип("СправочникСсылка.Контрагенты") Тогда
       
//Это контрагент
   
Иначе
       
//Это номенклатура
   
КонецЕсли;
   
//Например
   
текОбъект.ПолучитьФорму().Открыть();
КонецПроцедуры

В результате мы получили строку поиска, по своим удобствам похожую на строку поисковых сайтов. Справочники Номенклатура и Контрагенты здесь объединены в качестве примера. Можно искать как в справочнике, так и в документах, слегка подправив запрос.

Также обнаружены некоторые ограничения при использовании:

-При быстром удалении слов из строки клавиатурой событие в процедуре СтрокаПоискаАвтоПодборТекста не вызывается, и таблица выбора остаётся открытой.

-В транслитерации есть три буквы, которые имеют два значения в латинице (в - v, w; ж - g, j; к - k, q). Если их использовать перекрёстно, то в первом варианте, то во втором, тогда такое слово не найдётся.

В будущем, надеюсь, решения для снятия этих ограничений будут найдены.

С уважением, Лёлёсь

Файлы обработки:

-