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

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

При разработке и доработке форм подбора номенклатуры в типовых конфигурациях (например, в «1С:Управление торговлей 11», «1С:Комплексная автоматизация» или «1С:ERP») разработчики часто сталкиваются с задачей организации быстрого поиска товаров по наименованиям их характеристик прямо в основном динамическом списке номенклатуры.

Пользователю удобно ввести часть названия характеристики (например, цвет краски или размер обуви) в единую строку поиска и сразу увидеть все подходящие позиции номенклатуры. Разберем, с какими платформенными ограничениями мы сталкиваемся и как правильно реализовать такое решение.

Анализ ограничений платформы 1С:Предприятие

Прежде чем перейти к архитектурным вариантам решения, рассмотрим, почему стандартные подходы не работают напрямую:

  1. Отсутствие агрегации строк в языке запросов 1С: В платформе 1С отсутствует встроенный аналог SQL-функций STRING_AGG или GROUP_CONCAT. Мы не можем одним запросом сгруппировать строки подчиненного справочника ХарактеристикиНоменклатуры и объединить их в одну текстовую строку через запятую.
  2. Ограничения события ПриПолученииДанныхНаСервере: Обработчик динамического списка позволяет заполнить ячейку для отображения на клиенте, однако платформа не строит индексы по таким виртуальным колонкам и не может применить к ним условие ГДЕ. Соответственно, штатный поиск по такой вычисляемой колонке работать не будет.
  3. Особенности расширенного (полнотекстового) поиска: Полнотекстовый поиск в 1С опирается на морфологический словарь и ищет целые слова. Если пользователь вводит часть слова (например, «зелен» вместо «зеленый»), поиск без спецсимвола маски (зелен*) не найдет совпадений.

Способ 1. Денормализация данных через служебный регистр сведений (Рекомендуемый)

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

Шаг 1. Создание структуры регистра сведений

Создадим непериодический независимый регистр сведений, например, ХарактеристикиНоменклатурыСтрокой:

  1. Измерение: Номенклатура (Тип: СправочникСсылка.Номенклатура, Ведущее: Истина).
  2. Ресурс: СписокХарактеристик (Тип: Строка неограниченной длины или фиксированной, например, 1000 символов).

Шаг 2. Обновление данных через подписки на события

Чтобы не блокировать и не перезаписывать сам элемент справочника Номенклатура, используем подписки на события справочника ХарактеристикиНоменклатуры — ПриЗаписи и ПередУдалением. При изменении любой характеристики формируется актуальная строка со всеми характеристиками товара и записывается в регистр.

Посмотрим на пример процедуры обновления данных в регистре:


Процедура ОбновитьСписокХарактеристикНоменклатуры(СсылкаНаНоменклатуру) Экспорт

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

КонецПроцедуры

Важный нюанс для УТ 11, КА и ERP: В типовых конфигурациях характеристики могут быть привязаны не только к конкретной номенклатуре, но и к объекту ВидНоменклатуры (общие характеристики). Если в базе используются общие характеристики, алгоритм при изменении характеристики должен обновлять записи регистра для всех позиций номенклатуры соответствующего вида.

Шаг 3. Доработка запроса динамического списка

В тексте запроса динамического списка связываем основную таблицу номенклатуры с созданным регистром сведений через ЛЕВОЕ СОЕДИНЕНИЕ:


ВЫБРАТЬ
	СпрНоменклатура.Ссылка КАК Ссылка,
	СпрНоменклатура.Наименование КАК Наименование,
	СпрНоменклатура.Артикул КАК Артикул,
	ЕСТЬNULL(РегХарактеристики.СписокХарактеристик, "") КАК ХарактеристикиСтрокой
ИЗ
	Справочник.Номенклатура КАК СпрНоменклатура
	ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ХарактеристикиНоменклатурыСтрокой КАК РегХарактеристики
		ПО СпрНоменклатура.Ссылка = РегХарактеристики.Номенклатура

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

Способ 2. Программная фильтрация по массиву ссылок

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

  1. В обработчике изменения строки поиска перехватываем пользовательский ввод.
  2. Выполняем отдельный быстрый запрос к справочнику ХарактеристикиНоменклатуры с условием Наименование ПОДОБНО &СтрокаПоиска.
  3. Получаем массив владельцев найденных характеристик (номенклатуру).
  4. Устанавливаем в динамический список элемент отбора вида Ссылка В (&МассивСсылок) или объединяем отбор по номенклатуре с отбором по ее собственному наименованию.
  5. При очистке строки поиска отключаем данный отбор.

Способ 3. Программная маска для полнотекстового поиска

Если используется механизм полнотекстового поиска, пользователям часто неудобно вводить спецсимволы масок (*, !). Чтобы решить эту проблему, перехватим строку перед отправкой в поиск и модифицируем её программно:


// Разбиваем введенный пользователем текст на отдельные слова
МассивСлов = СтрРазделить(ВведенныйТекст, " ", Ложь);
Для Индекс = 0 По МассивСлов.ВГраница() Цикл
    МассивСлов[Индекс] = МассивСлов[Индекс] + "*";
КонецЦикла;

// Получаем строку вида: "крас* И зелен*"
РезультирующаяСтрокаПоиска = СтрСоединить(МассивСлов, " И ");

Такой прием позволяет пользователям искать совпадения по неполным словам без необходимости изучения синтаксиса поисковых запросов 1С.

← На главную