Разработчики, работающие с обычными формами в 1С 8.2 и 8.3, часто сталкиваются с жесткими ограничениями стандартных интерфейсных механизмов. Типичная задача: при открытии формы выбора справочника необходимо отобразить только те элементы, которые удовлетворяют сложному условию. При этом само условие невозможно наложить простым равенством — его можно получить только с помощью запроса (например, выбрав остатки по регистру, проверив связи с другими справочниками или обратившись к сложным условиям периодических регистров сведений).
В управляемом интерфейсе эта задача решается за пару минут с помощью механизма произвольного запроса в динамическом списке. Но как быть в обычных формах, где основным источником данных выступает стандартный объект СправочникСписок? Давайте подробно разберем рабочий способ решения этой проблемы, обсудим его ограничения, проблемы производительности и рассмотрим жизнеспособные альтернативные варианты.
Классический и наиболее простой в реализации способ основан на двухшаговом алгоритме:
СправочникСписок с видом сравнения В списке.Рассмотрим по шагам, как реализовать этот алгоритм в обработчике события формы выбора или формы списка справочника (например, в событии ПередОткрытием или ПриОткрытии):
Процедура ПередОткрытием(Отказ, СтандартнаяОбработка)
// Шаг 1. Получаем список разрешенных ссылок через запрос
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.Ссылка КАК Ссылка
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.ПометкаУдаления = ЛОЖЬ
| И Контрагенты.Регион В (&РегионыПользователя)"; // Пример сложного условия
// Заполняем параметры запроса
СписокРегионов = Новый Массив;
СписокРегионов.Добавить(Справочники.Регионы.НайтиПоНаименованию("Северный"));
Запрос.УстановитьПараметр("РегионыПользователя", СписокРегионов);
РезультатЗапроса = Запрос.Выполнить();
// Шаг 2. Заполняем список значений для отбора
СписокРазрешенныхСсылок = Новый СписокЗначений;
Если Не РезультатЗапроса.Пустой() Тогда
ТаблицаДанных = РезультатЗапроса.Выгрузить();
МассивСсылок = ТаблицаДанных.ВыгрузитьКолонку("Ссылка");
СписокРазрешенныхСсылок.ЗагрузитьЗначения(МассивСсылок);
КонецЕсли;
// Шаг 3. Устанавливаем отбор в СправочникСписок
// Обратите внимание: имя основной переменной списка на форме обычно совпадает с типом (например, СправочникСписок)
ОтборСсылка = СправочникСписок.Отбор.Ссылка;
ОтборСсылка.Использование = Истина;
ОтборСсылка.ВидСравнения = ВидСравнения.ВСписке;
ОтборСсылка.Значение = СписокРазрешенныхСсылок;
КонецПроцедуры
Этот метод действительно работает и позволяет быстро решить локальную задачу фильтрации данных без переработки интерфейса формы. Однако у этого подхода есть серьезные подводные камни, о которых обязан знать профессиональный разработчик.
Использование конструкции ВидСравнения.ВСписке для динамических списков таит в себе скрытые угрозы для стабильности и быстродействия информационной базы:
IN (Ссылка1, Ссылка2, ... СсылкаN). Для СУБД обработка таких запросов является крайне ресурсоемкой задачей, что может вызвать зависание интерфейса пользователя.Если логика фильтрации слишком сложная, а количество отображаемых строк заведомо невелико (в пределах нескольких сотен), наиболее надежным и управляемым решением будет полный отказ от использования динамического объекта СправочникСписок на форме.
Вместо этого мы можем использовать обычное табличное поле с типом данных ТаблицаЗначений. Давайте проанализируем этот подход по шагам:
Данные) указываем новую реквизит-таблицу значений (например, ТаблицаЭлементов).СоздатьКолонки() для автоматического формирования интерфейса табличного поля.Пример программного кода для инициализации таблицы значений на форме:
Процедура ПриОткрытии()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.Ссылка КАК Ссылка,
| Контрагенты.Код КАК Код,
| Контрагенты.Наименование КАК Наименование,
| Контрагенты.ИНН КАК ИНН
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.Регион = &Регион";
Запрос.УстановитьПараметр("Регион", ВыбранныйРегион);
// Выгружаем данные напрямую в реквизит формы
ТаблицаЭлементов = Запрос.Выполнить().Выгрузить();
// Обновляем визуальное представление табличного поля
ЭлементыФормы.ТабличноеПолеЭлементов.Значение = ТаблицаЭлементов;
ЭлементыФормы.TabличоеПолеЭлементов.СоздатьКолонки();
КонецПроцедуры
Преимущества использования Таблицы значений: полный контроль над составом строк, мгновенное открытие формы (так как СУБД выполняет простой и быстрый запрос без перегрузки параметрами), отсутствие зависимости от системных механизмов динамического отбора.
Недостатки: мы полностью теряем иерархический просмотр справочника (группы и элементы будут выведены плоским списком). Кроме того, динамическое считывание данных порциями больше не работает — все данные загружаются в память клиента сразу. Также придется вручную программировать стандартные действия (добавление новых элементов, их изменение и копирование).
Если вам необходимо сохранить гибкость настройки отборов пользователем, но стандартный функционал СправочникСписок не справляется со сложными условиями, можно задействовать Построитель запроса.
Построитель запроса в обычных формах умеет напрямую выгружать результаты во встроенные элементы интерфейса и даже строить Дерево значений, что позволяет частично вернуть иерархическую структуру отображения групп справочника. Настройка связей табличного поля формы с построителем запроса позволяет интерактивно управлять параметрами виртуальных таблиц и накладывать сложные условия динамически.
Среди начинающих разработчиков распространено опасное заблуждение: они пытаются скрывать или удалять строки в обработчиках событий ПриВыводеСтроки или ПриПолученииДанных табличного поля.
Запомните: динамически скрыть строки через эти события невозможно!
Данные события предназначены исключительно для визуального оформления уже выбранных платформой строк (например, для окрашивания текста, изменения фона ячеек, вывода картинок статусов или отображения расчетных данных из других регистров). Свойство видимости строки внутри этих обработчиков отсутствует.
Более того, попытка выполнять какие-либо запросы к базе данных внутри события ПриВыводеСтроки — это грубейшая архитектурная ошибка. Данное событие срабатывает при каждом шевелении мыши или прокрутке списка. Запрос в цикле внутри отрисовки интерфейса гарантированно приведет к "зависанию" программы у пользователя даже на мощном сервере.
Выясним причину фильтрации строк. Если ваша бизнес-логика требует скрывать строки справочника из соображений безопасности или разграничения прав доступа (например, менеджер по продажам должен видеть только клиентов своей группы), то использование программных отборов на уровне форм является небезопасным решением.
Правильный путь в этом случае — настройка RLS (Row Level Security) на уровне ролей конфигурации:
СправочникСписок).В современной платформе 1С на управляемом интерфейсе разработчики полностью избавлены от подобных проблем благодаря объекту ДинамическийСписок. Если вам нужно реализовать фильтрацию по результатам сложного запроса, достаточно переключить свойство динамического списка в режим Произвольный запрос и написать текст любой сложности с использованием левых соединений, вложенных запросов и виртуальных таблиц:
ВЫБРАТЬ
Контрагенты.Ссылка КАК Ссылка,
Контрагенты.Наименование КАК Наименование,
ЕСТЬNULL(ВзаиморасчетыОстатки.СуммаВзаиморасчетовОстаток, 0) КАК ДогОстаток
ИЗ
Справочник.Контрагенты КАК Контрагенты
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Взаиморасчеты.Остатки КАК ВзаиморасчетыОстатки
ПО ВзаиморасчетыОстатки.Контрагент = Контрагенты.Ссылка
ГДЕ
ЕСТЬNULL(ВзаиморасчетыОстатки.СуммаВзаиморасчетовОстаток, 0) > &МинимальныйПорог
Параметр МинимальныйПорог в управляемых формах передается в компоновщик настроек динамического списка одной строчкой кода, а платформа сама заботится об оптимальном получении данных порциями, не перегружая оперативную память сервера и клиента.