При работе с обычными формами (ОФ) в среде 1С:Предприятие 8.2 или 8.3 программисты часто сталкиваются с ограничением стандартного механизма отборов. Если в управляемых формах динамический список позволяет написать произвольный запрос с любыми соединениями, то в обычных формах объект ДокументСписок предоставляет лишь плоский перечень доступных полей для фильтрации. Рассмотрим ситуацию, когда нам необходимо скрыть часть документов на основе условий, которые нельзя выразить через простое равенство или вхождение в диапазон (например, проверка статусов связанных объектов или расчетных показателей).
В этой статье мы подробно разберем несколько способов решения этой задачи: от программной установки отбора до изменения архитектуры хранения данных.
Наиболее очевидный и часто предлагаемый на форумах метод — это формирование массива разрешенных ссылок и передача его в стандартный отбор по полю Ссылка. Давайте проанализируем, как это реализовать правильно. Мы должны выполнить запрос, который выберет все подходящие под наше сложное условие документы, а затем передать этот список в форму.
Разберем пример кода, который можно разместить в процедуре ПриОткрытии или при нажатии на кнопку фильтра:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| РеализацияТоваровУслуг.Ссылка КАК Ссылка
|ИЗ
| Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
|ГДЕ
| РеализацияТоваровУслуг.Проведен = ИСТИНА
| И РеализацияТоваровУслуг.СуммаДокумента > 1000
| И РеализацияТоваровУслуг.Контрагент.ВидКонтрагента = &ВидКонтрагента";
Запрос.УстановитьПараметр("ВидКонтрагента", Перечисления.ВидыКонтрагентов.ЮрЛицо);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
СписокРазрешенныхСсылок = РезультатЗапроса.Выгрузить().ВыгрузитьКолонку("Ссылка");
// Устанавливаем отбор в табличном поле списка
ОтборСсылка = ДокументСписок.Отбор.Ссылка;
ОтборСсылка.ВидСравнения = ВидСравнения.ВСписке;
ОтборСсылка.Значение.Очистить();
Для Каждого Ссылка Из СписокРазрешенныхСсылок Цикл
ОтборСсылка.Значение.Добавить(Ссылка);
КонецЦикла;
ОтборСсылка.Использование = Истина;
Иначе
// Если ничего не найдено, можно либо очистить список, либо вывести пустой отбор
ДокументСписок.Отбор.Ссылка.Использование = Ложь;
КонецЕсли;
Важное ограничение: Выясним причину, по которой этот метод не всегда применим. Если количество документов в базе исчисляется десятками тысяч, а под фильтр попадает более 1000–2000 записей, платформа может начать работать медленно. Это связано с тем, что СУБД (SQL Server или PostgreSQL) получает запрос с огромным списком параметров в операторе IN (...), что увеличивает время исполнения и нагрузку на сеть.
Если условие фильтрации «жесткое» и должно применяться ко всей группе пользователей постоянно, самым правильным архитектурным решением будет настройка RLS. Этот механизм работает на уровне платформы и базы данных, полностью исключая документы из выборки еще до того, как они попадут в интерфейс.
Проанализируем шаги настройки:
Документ.РеализацияТоваровУслуг) на закладку «Права».Пример текста ограничения RLS:
ГДЕ (Проведен = ИСТИНА И Ответственный = &ТекущийПользователь)
ИЛИ (СуммаДокумента < 100000)
Преимущество этого метода в том, что документы исчезнут не только из формы списка, но и из всех отчетов и связанных журналов. Пользователь даже не узнает о существовании этих записей, что решает вопрос безопасности данных.
Если «сложное условие» зависит от данных, которые меняются редко (например, статус завершения бизнес-процесса), мы можем оптимизировать работу системы, добавив в документ реквизит типа Булево, например, ОтображатьВРабочемСписке.
Посмотрим на алгоритм реализации:
ОтображатьВРабочемСписке и устанавливаем для него свойство Индексировать.ПередЗаписью прописываем логику расчета этого признака.Пример кода в модуле документа:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Здесь наше сложное условие
Если ЭтоНовый() Или ПроверитьНеобходимостьПересчета() Тогда
ОтображатьВРабочемСписке = (Статус = Перечисления.Статусы.Завершено И Сумма > 0);
КонецЕсли;
КонецПроцедуры
Теперь в форме списка достаточно один раз установить ДокументСписок.Отбор.ОтображатьВРабочемСписке.Установить(Истина). Это работает мгновенно вне зависимости от размера базы данных.
В обычных формах ПостроительЗапроса является мощным инструментом, позволяющим динамически формировать текст запроса. Если стандартный ДокументСписок нас не устраивает, мы можем заменить его на ТабличноеПоле, источником данных для которого будет результат работы построителя.
Рассмотрим, как это работает:
Мы создаем объект ПостроительЗапроса, инициализируем его текстом, включающим все необходимые ЛЕВОЕ СОЕДИНЕНИЕ. Результат запроса выгружается в таблицу значений, которая отображается пользователю. Однако помните, что при таком подходе вы теряете преимущества динамического считывания данных — список будет загружаться целиком, что может быть критично для больших таблиц.
Если скрыть строку физически невозможно (например, из-за требований динамического обновления), можно воспользоваться событием ПриВыводеСтроки. Хотя это не удаляет строку из списка, это позволяет сделать ее «невидимой» или неактивной для пользователя.
Разберем пример оформления:
Процедура СписокПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки)
Если ДанныеСтроки.СуммаДокумента < 100 Тогда
// Делаем строку едва заметной
ОформлениеСтроки.ЦветТекста = ЦветаСтиля.ТекстЗаблокированногоЭлемента;
// Можно запретить выбор этой строки
ОформлениеСтроки.Ячейки.Номер.УстановитьТекст("Скрыто");
КонецЕсли;
КонецПроцедуры
Важно понимать, что в обычных формах 8.2 невозможно установить высоту строки в 0 через оформление так, чтобы список «схлопнулся». Поэтому данный метод является скорее вспомогательным визуальным средством, а не полноценной фильтрацией.
Выбор метода зависит от ваших приоритетов:
ВидСравнения.ВСписке.