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