Как сделать отбор по расчетной колонке в ДокументСписок на обычных формах

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

При разработке интерфейсов на обычных формах часто возникает задача отобразить в списке документов дополнительное вычисляемое поле, например статус согласования, признак оплаты или состояние отгрузки. Для экономии ресурсов разработчики нередко добавляют в элемент управления ТабличноеПоле (с типом данных ДокументСписок) колонку, не являющуюся реквизитом документа, и заполняют ее программно в событии ПриПолученииДанных или ПриВыводеСтроки.

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

Почему стандартный отбор не работает для расчетных колонок

Проанализируем механику работы объекта ДокументСписок. В платформе 1С объект ДокументСписок представляет собой динамический считыватель данных. Он не хранит все строки таблицы в оперативной памяти клиента, а запрашивает их порциями напрямую из СУБД по мере прокрутки списка пользователем.

Все условия, которые мы задаем через коллекцию Отбор, платформа напрямую транслирует в секцию ГДЕ (или WHERE) SQL-запроса к базе данных. Колонка, добавленная вручную и заполняемая в обработчиках событий:

  1. Существует исключительно в клиентской памяти интерфейса.
  2. Заполняется только для тех строк, которые физически видны на экране в текущий момент.
  3. Полностью отсутствует в физической таблице базы данных.

Попытка обратиться к такой колонке через коллекцию отбора вызовет ошибку, так как платформа не знает о существовании такого поля в структуре СУБД. Кроме того, попытка собрать данные прямо внутри процедуры ПриПолученииДанных приведет к ошибке логики: в отбор попадут только те документы, которые пользователь уже успел прокрутить на экране.

Решение через программный сбор ссылок и отбор «В списке»

Рассмотрим основной способ решения, когда структуру метаданных менять нельзя. Идея заключается в том, чтобы заранее получить ссылки на все документы, соответствующие выбранному статусу, и наложить на ДокументСписок отбор по стандартному реквизиту Ссылка с видом сравнения ВидСравнения.ВСписке.

Разберем пошаговый алгоритм реализации такого механизма:

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

Посмотрим на пример программной реализации в модуле формы списка:


Перем м_КэшСтатусов;

Процедура ОбновитьКэшСтатусов()
    // Инициализируем соответствие: Ключ - Статус, Значение - СписокЗначений со ссылками
    м_КэшСтатусов = Новый Соответствие;
    
    // Формируем запрос для расчета статусов по всем документам за рабочий период
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    Док.Ссылка КАК Ссылка,
        |    ВЫБОР
        |        КОГДА Док.Проведен ТОГДА ""Проведен""
        |        КОГДА Док.ПометкаУдаления ТОГДА ""Удален""
        |        ИНАЧЕ ""Черновик""
        |    КОНЕЦ КАК Статус
        |ИЗ
        |    Документ.РеализацияТоваровУслуг КАК Док";
    
    Выборка = Запрос.Выполнить().Выбрать();
    Пока Выборка.Следующий() Цикл
        ТекСписок = м_КэшСтатусов.Получить(Выборка.Статус);
        Если ТекСписок = Неопределено Тогда
            ТекСписок = Новый СписокЗначений;
            м_КэшСтатусов.Вставить(Выборка.Статус, ТекСписок);
        КонецЕсли;
        ТекСписок.Добавить(Выборка.Ссылка);
    КонецЦикла;
КонецПроцедуры

Процедура ПриОткрытии()
    ОбновитьКэшСтатусов();
КонецПроцедуры

Процедура ПолеВыбораСтатусПриИзменении(Элемент)
    ВыбранныйСтатус = Элемент.Значение;
    
    Если ЗначениеЗаполнено(ВыбранныйСтатус) Тогда
        СписокСсылок = м_КэшСтатусов.Получить(ВыбранныйСтатус);
        
        ДокументСписок.Отбор.Ссылка.ВидСравнения = ВидСравнения.ВСписке;
        ДокументСписок.Отбор.Ссылка.Значение     = ?(СписокСсылок <> Неопределено, СписокСсылок, Новый СписокЗначений);
        ДокументСписок.Отбор.Ссылка.Использование = Истина;
    Иначе
        // Сброс отбора, если пользователь очистил поле выбора
        ДокументСписок.Отбор.Ссылка.Использование = Ложь;
    КонецЕсли;
КонецПроцедуры

Как корректно отключать и сбрасывать отбор

Выясним причину частых затруднений разработчиков при попытке вернуть список в исходное состояние. Если просто очистить значение отбора или передать пустой список, платформа попытается найти документы, входящие в пустой список, и табличное поле окажется полностью пустым.

Для полного отключения фильтрации по ссылке необходимо управлять свойством активности отбора. Это можно сделать двумя способами:


// Способ 1: Прямое отключение активности
ДокументСписок.Отбор.Ссылка.Использование = Ложь;

// Способ 2: Использование метода Установить()
ДокументСписок.Отбор.Ссылка.Установить(Неопределено, Ложь);

Оба варианта деактивируют условие в запросе СУБД, и список документов снова начинает отображаться без ограничений.

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

Хотя данный метод решает задачу на небольших базах данных, проанализируем риски, с которыми можно столкнуться при росте объема информации:

  1. Лимиты СУБД на количество параметров: Если документов с выбранным статусом накопилось несколько десятков тысяч, список ссылок превратится в огромную конструкцию IN (...). Серверы баз данных (включая MS SQL Server или PostgreSQL) имеют ограничения на размер текста запроса и максимальное число параметров, что приведет к аварийному завершению запроса.
  2. Падение производительности: Поиск по массивному списку значений заставляет СУБД перестраивать план запроса, нередко приводя к полному сканированию индекса или таблицы (Scan вместо Seek), что вызывает зависание клиентского интерфейса.
  3. Неактуальность кэша: Если другие пользователи создают, проводят или меняют документы, кэш статусов в переменной формы перестает соответствовать реальности. Пользователю потребуется вручную нажимать кнопку обновления кэша.

Альтернативные подходы к реализации

Если предполагается работа с большими массивами данных, рассмотрим более надежные архитектурные альтернативы:

  1. Материализация статуса в базе данных: Добавление физического реквизита в документ или использование непериодического регистра сведений (измерение — документ, ресурс — статус). При установке для реквизита свойства Индексировать штатный отбор в ДокументСписок начнет работать нативно и с максимальной скоростью без дополнительных скриптов.
  2. Использование ТаблицыЗначений вместо ДокументСписок: Если в форме предусмотрен жесткий отбор по датам (например, за текущую смену или месяц), можно заменить ДокументСписок на ТабличноеПоле с типом ТаблицаЗначений. Вся выборка формируется одним запросом сразу с вычислением статуса, а фильтрация по строкам на клиенте выполняется через свойство ОтборСтрок.
  3. Применение ПостроителяОтчета: Построитель отчета умеет выводить данные в табличное поле и динамически формировать условия в секции {ГДЕ ...} на уровне запроса СУБД, вычисляя статус с помощью выражений ВЫБОР КОГДА непосредственно в момент обращения.
← На главную