При разработке интерфейсов на обычных формах часто возникает задача отобразить в списке документов дополнительное вычисляемое поле, например статус согласования, признак оплаты или состояние отгрузки. Для экономии ресурсов разработчики нередко добавляют в элемент управления ТабличноеПоле (с типом данных ДокументСписок) колонку, не являющуюся реквизитом документа, и заполняют ее программно в событии ПриПолученииДанных или ПриВыводеСтроки.
Сложность возникает тогда, когда пользователь хочет отфильтровать список по этому статусу. Стандартный механизм отбора формы списка отказывается работать с расчетными полями. Разберем, почему возникает эта проблема, как ее обойти штатными средствами платформы и какие альтернативные архитектурные решения существуют.
Проанализируем механику работы объекта ДокументСписок. В платформе 1С объект ДокументСписок представляет собой динамический считыватель данных. Он не хранит все строки таблицы в оперативной памяти клиента, а запрашивает их порциями напрямую из СУБД по мере прокрутки списка пользователем.
Все условия, которые мы задаем через коллекцию Отбор, платформа напрямую транслирует в секцию ГДЕ (или WHERE) SQL-запроса к базе данных. Колонка, добавленная вручную и заполняемая в обработчиках событий:
Попытка обратиться к такой колонке через коллекцию отбора вызовет ошибку, так как платформа не знает о существовании такого поля в структуре СУБД. Кроме того, попытка собрать данные прямо внутри процедуры ПриПолученииДанных приведет к ошибке логики: в отбор попадут только те документы, которые пользователь уже успел прокрутить на экране.
Рассмотрим основной способ решения, когда структуру метаданных менять нельзя. Идея заключается в том, чтобы заранее получить ссылки на все документы, соответствующие выбранному статусу, и наложить на ДокументСписок отбор по стандартному реквизиту Ссылка с видом сравнения ВидСравнения.ВСписке.
Разберем пошаговый алгоритм реализации такого механизма:
Ссылка.Посмотрим на пример программной реализации в модуле формы списка:
Перем м_КэшСтатусов;
Процедура ОбновитьКэшСтатусов()
// Инициализируем соответствие: Ключ - Статус, Значение - СписокЗначений со ссылками
м_КэшСтатусов = Новый Соответствие;
// Формируем запрос для расчета статусов по всем документам за рабочий период
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Док.Ссылка КАК Ссылка,
| ВЫБОР
| КОГДА Док.Проведен ТОГДА ""Проведен""
| КОГДА Док.ПометкаУдаления ТОГДА ""Удален""
| ИНАЧЕ ""Черновик""
| КОНЕЦ КАК Статус
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Док";
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
ТекСписок = м_КэшСтатусов.Получить(Выборка.Статус);
Если ТекСписок = Неопределено Тогда
ТекСписок = Новый СписокЗначений;
м_КэшСтатусов.Вставить(Выборка.Статус, ТекСписок);
КонецЕсли;
ТекСписок.Добавить(Выборка.Ссылка);
КонецЦикла;
КонецПроцедуры
Процедура ПриОткрытии()
ОбновитьКэшСтатусов();
КонецПроцедуры
Процедура ПолеВыбораСтатусПриИзменении(Элемент)
ВыбранныйСтатус = Элемент.Значение;
Если ЗначениеЗаполнено(ВыбранныйСтатус) Тогда
СписокСсылок = м_КэшСтатусов.Получить(ВыбранныйСтатус);
ДокументСписок.Отбор.Ссылка.ВидСравнения = ВидСравнения.ВСписке;
ДокументСписок.Отбор.Ссылка.Значение = ?(СписокСсылок <> Неопределено, СписокСсылок, Новый СписокЗначений);
ДокументСписок.Отбор.Ссылка.Использование = Истина;
Иначе
// Сброс отбора, если пользователь очистил поле выбора
ДокументСписок.Отбор.Ссылка.Использование = Ложь;
КонецЕсли;
КонецПроцедуры
Выясним причину частых затруднений разработчиков при попытке вернуть список в исходное состояние. Если просто очистить значение отбора или передать пустой список, платформа попытается найти документы, входящие в пустой список, и табличное поле окажется полностью пустым.
Для полного отключения фильтрации по ссылке необходимо управлять свойством активности отбора. Это можно сделать двумя способами:
// Способ 1: Прямое отключение активности
ДокументСписок.Отбор.Ссылка.Использование = Ложь;
// Способ 2: Использование метода Установить()
ДокументСписок.Отбор.Ссылка.Установить(Неопределено, Ложь);
Оба варианта деактивируют условие в запросе СУБД, и список документов снова начинает отображаться без ограничений.
Хотя данный метод решает задачу на небольших базах данных, проанализируем риски, с которыми можно столкнуться при росте объема информации:
IN (...). Серверы баз данных (включая MS SQL Server или PostgreSQL) имеют ограничения на размер текста запроса и максимальное число параметров, что приведет к аварийному завершению запроса.Если предполагается работа с большими массивами данных, рассмотрим более надежные архитектурные альтернативы:
ДокументСписок начнет работать нативно и с максимальной скоростью без дополнительных скриптов.ДокументСписок на ТабличноеПоле с типом ТаблицаЗначений. Вся выборка формируется одним запросом сразу с вычислением статуса, а фильтрация по строкам на клиенте выполняется через свойство ОтборСтрок.{ГДЕ ...} на уровне запроса СУБД, вычисляя статус с помощью выражений ВЫБОР КОГДА непосредственно в момент обращения.