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



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

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

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

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

Для дополнительного удобства сделаем транслитерацию латинского алфавита русским. Это позволит не переключаться между раскладкой клавиатуры и всё писать по-русски. Например, вместо буквы «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). Если их использовать перекрёстно, то в первом варианте, то во втором, тогда такое слово не найдётся.

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

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

18 Comments

  1. ninch

    Молодец! Хорошая задумка и реализация. Чувствуется где-то была задача поставлена под эту разработку. Так то наврядли кто бы стал такое чудо делать:)

    Reply
  2. leles

    (1) Спасибо, друг!

    Делал для себя, теперь и для Вас!

    Reply
  3. cool.vlad4

    вот , как мне кажется, более навороченная и универсальная — http://infostart.ru/public/89892/

    Reply
  4. leles

    (3) Да, спасибо за ссылку, обязательно изучу

    Reply
  5. Smaylukk

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

    1. Передавал в процедуру поиска имя справочника, по которому искать;

    2. Передавал в процедуру поиска набор полей в виде массива, по которым нужно производить поиск;

    И еще по поводу раскрытия списка выбора — пробуйте подергать методы ВыбратьИзСписка() или ВыбратьИзМеню() — если получится прикрутить — будет отлично, а то таблицу неудобно наверное встраивать в табчасть документа, когда нужно будет привязать к полю в ней этот чуджо-поиск 🙂

    Reply
  6. Rembi_999

    что-то чудаковатое.

    Reply
  7. leles

    (5) Да, да точно! ВыбратьИзСписка() я пробовал приладить как и таблицу, но тогда никак не мог придумать, как только при помощи стрелки «вниз» перейти со строки поиска в этот список(таблицу). Чтобы всё делать с клавиатуры, без мыши. Решение нашёл что по стрелке «вниз» мы передвигаемся не в таблицу, а в выпадающий список, который выпадает над как раз над таблицей и полностью её повторяет так, что создаётся впечатление что мы спускаемся в таблицу.

    Ну и конечно, успешно забыл про ВыбратьИзСписка(). А ведь можно всё организовать лучше! Спасибо! Буду переделывать.

    По поводу расширения универсальности и новых возможностей — это здорово. Надо подумать.

    (6) Чудаковато? — Согласен! Мы здесь немного все чудаки. Время ночь, а все у экрана и улучшать, улучшать 🙂

    Reply
  8. leles

    (5) А нет, не получиться. После появления списка из ВыбратьИзСписка() фокус переходит на этот список и следующую букву уже не набрать. А жаль.

    Reply
  9. Smaylukk

    (8) Можно попробовать завязаться на другое событие — ОкончаниеВводаТекста(), правда здесь пока пользователь не введет нужный текст и не нажмет Enter, список или подсказка не появится.

    P.S. Я реализовывал как раз так 🙂

    Reply
  10. ms200999

    (3) Согласен. У себя сделал поиск на основе именно той публикации, добавил поддержку транслита и учел ошибку выбора раскладки. При вводе подстроки «дел» ищем «дел», «del» и «ltk». Здорово помогает как тем, кто забывает переключать язык ввода, так и тем, кто не помнит, какими символами — кириллицей и латиницей — записана исклмая строка.

    Reply
  11. mkostya

    хорошее решение, очень понравилось.

    (6) Чудаковато? Вполне сойдет за решение??

    Не собираетесь и дальше её усовершенствовать???

    Reply
  12. Damian

    Особенно порадовал справочник «Номенклатура» на скринах. Сразу видно — серьезный бизнес 🙂

    Reply
  13. leles

    (9) Да, как вариант. Только хотелось как в поисковике: набрал пару букв — раз, подсказки.

    Добрал ещё — более нужные подсказки, и выбрал из предложенного.

    (10) Ошибка выбора раскладки это плюс! Если можно приведите Ваш кусок кода с транслитом и ошибкой.

    (11) Спасибо, усовершенствовать обязательно буду. На мой взгляд, такой поиск наиболее необходим при подборе товара в документ. Только не знаю, возможно ли создать такую обработку, чтобы она была универсальной для любой конфигурации. Да и решение об изменении конфигурации это дело каждого. Так что пока набираю информацию.

    (12) Да уж, и ведь это только ассортимент нашего ларька на Апрашке:)

    Reply
  14. anig99

    А не лучше ли искать последовательно? Т.е. разбить строку поиска на подстроки и в цикле через временные таблицы ограничивать конечный список. Примерно так.

    нап вент bo 112

    Сначала делается запрос к справочнику номенклатура по подобию «нап». Сохраняется во временной таблице в запросе. Потом вторым запросом и последующими отрезаем лишнее от этой временной таблицы по подобно по 1 слову. Алгоритм будет не такой громоздкий. Да и по скорости может быть выигрыш.

    Reply
  15. Vladimir87

    Извиняюсь если был такой вопрос, но все же, в УТ 10.3 будет работать?

    Reply
  16. mentozavr

    а у меня как раз задача похожая стоит

    Reply
  17. mentozavr

    (5) Smaylukk, Сэр а как вы это сделали может кодом поделитесь. Заранее благодарю.

    Reply
  18. Andromix

    Отличная обработка, теперь думаю как её прикрутить в форму номенклатуры, что бы при создании показывала похожие элементы

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *