Как реализовать поиск и отбор в динамическом списке по программно вычисляемым полям?

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

При разработке на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с задачей вывода дополнительных данных в списки документов или справочников. Типичный пример: вывести список ответственных менеджеров из табличной части или текущий этап согласования из внешнего регистра сведений в одну строку через запятую. Однако, реализовав это через популярное событие ПриПолученииДанных, разработчик обнаруживает, что стандартные механизмы поиска (Ctrl+F) и отборы по этой колонке перестают работать. Разберем подробно, почему так происходит и как правильно решить эту задачу.

Почему событие ПриПолученииДанных не подходит для фильтрации?

Для начала проанализируем внутренние механизмы платформы. Динамический список — это объект, который генерирует SQL-запрос к базе данных. Когда мы накладываем отбор или используем строку поиска, платформа добавляет в этот запрос конструкцию WHERE. База данных ищет только по тем полям, которые физически существуют в таблицах SQL или могут быть получены через соединения.

Событие ПриПолученииДанных срабатывает на стороне сервера 1С после того, как основной запрос к базе данных уже выполнен и данные для текущей страницы (обычно 30–45 строк) получены. Мы программно "дорисовываем" значения в ячейки для отображения пользователю. Платформа ничего не знает об этих значениях в контексте всей базы данных. Именно поэтому попытка найти документ по тексту, добавленному в этом событии, всегда будет возвращать пустой результат: в базе данных этого текста просто нет.

Решение 1: Денормализация данных (Оптимальный путь для ERP)

Рассмотрим наиболее производительный и надежный метод, который часто применяется в крупных конфигурациях, таких как 1С:ERP 2.5 или 1С:Комплексная автоматизация. Если нам нужно искать по строке, объединяющей данные из табличной части (например, список менеджеров), лучше всего хранить эту строку в реальном реквизите шапки документа.

Алгоритм реализации:

  1. Добавим в документ реквизит СписокМенеджеровСтрокой (Тип: Строка, Длина: Неограничена или 500+).
  2. В модуле объекта в обработчике ПередЗаписью реализуем формирование этой строки.
  3. Добавим это поле в текст запроса динамического списка.

Пример кода в модуле документа:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;
    
    // Формируем строку менеджеров на основании ТЧ "Товары"
    ТаблицаМенеджеров = Товары.ВыгрузитьКолонку("Менеджер");
    МассивУникальных = Новый Массив;
    Для Каждого Менеджер Из ТаблицаМенеджеров Цикл
        Если ЗначениеЗаполнено(Менеджер) И МассивУникальных.Найти(Менеджер) = Неопределено Тогда
            МассивУникальных.Добавить(Строка(Менеджер));
        КонецЕсли;
    КонецЦикла;
    
    СписокМенеджеровСтрокой = СтрСоединить(МассивУникальных, ", ");
КонецПроцедуры

Преимущества: Поиск и отбор работают мгновенно, так как используется индексированное (если нужно) поле таблицы. Нагрузка на сервер при открытии списка минимальна.

Решение 2: Использование вложенных запросов в динамическом списке

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

В тексте запроса динамического списка (на закладке "Запрос") можно использовать левое соединение с группировкой или вложенный запрос. Чтобы избежать появления нескольких строк для одного документа, используем конструкцию ГДЕ Ссылка В (...) в пользовательских настройках или параметрах запроса.

Если же мы хотим видеть список этапов согласования из Регистра Сведений, проанализируем следующий пример запроса:


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

Важный нюанс: Язык запросов 1С не поддерживает агрегатную функцию STRING_AGG (объединение строк в одну). Поэтому вывести именно "список через запятую" прямо в запросе крайне сложно. В таких случаях лучше использовать Решение 1 или Решение 3.

Решение 3: Программная подмена текста запроса при изменении отбора

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

Пошаговый алгоритм:

  1. Добавим на форму реквизит ОтборПоМенеджеру (тип СправочникСсылка.Пользователи или Строка).
  2. В событии ПриИзменении этого реквизита напишем код, который добавит условие в КомпоновщикНастроек или изменит ТекстЗапроса.

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

Решение 4: Полнотекстовый поиск (ППС)

Малоизвестный, но эффективный метод. Если в свойствах документа включено Использовать полнотекстовый поиск, и реквизит табличной части также включен в индекс, то стандартное поле поиска в заголовке формы (которое ищет "везде") будет находить документ по имени менеджера, даже если его нет в колонках списка. Платформа сама найдет ссылку на документ через индексы табличных частей. Это не поможет сделать строгий отбор "по колонке", но решит задачу "быстро найти документ по менеджеру".

Выводы и рекомендации

Рассмотрев все варианты, мы приходим к следующим выводам:

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

← На главную