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