Перейти к основному содержанию
`Ctrl`+`K`

# Универсальный фильтр

## Основные положения

Операции универсального фильтра объявлены в `EntityAvi`, что означает, что он доступен в каждой выборке.

Вся фильтрация построена на создании объектов scala-классов, которые хранятся в памяти. Сохранение всех настроек осуществляется в таблицу базы данных в виде json.

Универсальная фильтрация управляется объектом, который хранится в переменной `fltManager`. Каждое отображение создаёт свой экземпляр менеджера фильтра.

По умолчанию для фильтрации доступны атрибуты выборки, а так же класса, который определяется через Avi-функцию `thisApi()`.

Сохранённая настройка игнорирует изменения свойства атрибутов, и она использует те настройки атрибутов, которые были в момент добавления этого атрибута с панели. Чтобы изменить поведение атрибута в настройке, необходимо его удалить, затем снова добавить и сохранить настройку.

### Физические атрибуты и выражения фильтрации

- Атрибуты в макросе представлены их «физическими атрибутами». По умолчанию физический атрибут равен системному имени атрибута, но существуют атрибуты, для которых это неверно. Примером может служить технический атрибут класса «IdObject#», имеющий физическое имя «ID» (или gidRef для Mixin-ов и пр.). В макросе такой атрибут всегда представлен как ID.

- Для атрибутов фильтра можно задать выражение фильтрации. Это произвольное SQL-выражение, определяющее значение атрибута. Например, такие выражения используются в объектных характеристиках, хранящихся в json.

### Активация фильтра в выборке

Для использования универсального фильтра в отображении необходимо активировать операцию uniFilter. Так же убедиться в активности операций onApplyFilter, onFilterFinalize, onFilterInit.

Внимание

Фильтр не работает в выборках с объектными запросами.

### HL-атрибуты

HL – атрибуты (далее IdAttrHL) считаются ссылочными атрибутами и в фильтрации не участвуют. Фильтрация осуществляется по их id-атрибутам (далее IdAttr).

- Если в выборке присутствует IdAttrHL, но отсутствует IdAttr, то фильтрация по атрибуту IdAttrHL будет недоступна.

- Все расширенные настройки требуется указывать на атрибуте IdAttr.

- Ссылочность определяется:

  - Через тег `<ref>`, например: `<ref class="Rpl_IntOutSession"/>`.

  - Через одноименный атрибут класса этой выборки.

### Связь выборки и класса

Для выборки класс определяется через Avi-функцию `thisApi()`.

### Связь атрибута выборки и атрибута класса

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

### Тип редактора атрибута

Тип редактора атрибута в большинстве случаев такой же, как указан в выборке, исключения:

- Выпадающие списки открываются через `редактор с тремя точками`.

- Даты фильтруются через интерфейс `«Фильтрации по периоду»`.

- Ссылочные атрибуты открываются через `редактор с тремя точками`.

- Специфичные типы сравнения (множественность) изменяют редактор через `редактор с тремя точками`.

Для атрибутов класса тип редактора определяется:

- Если указан в odm, то этот тип редактора.

- В остальных случаях определяется от типа атрибута и его типа данных.

### Коллекции класса

В перечне атрибутов, доступных для фильтрации (как для выборок с классами, так и для самих классов), некоторые пункты отмечены красными иконками. Эти иконки позволяют фильтровать данные по коллекциям. Коллекции доступные для фильтрации определяются через odm- файл в теге `<collections>`.

Так же в карточке класса на закладке «Коллекции универсального фильтра» есть возможность добавить дополнительные коллекции для фильтрации. Например, подключить какой-то класс, который не входит в бизнес-объект, но имеет ссылки на текущий.

На этой же закладке можно переопределить и стандартные коллекции класса:

- отключить фильтрацию по коллекции, сняв признак «Активность»;

- переименовать коллекцию, изменив поле «Наименование».

## Доступные элементы для фильтрации

### Типы доступных элементов

- `атрибут класса/выборки` – доступен в случае генерации доступных атрибутов по классу или выборке;

- `коллекции` - добавляются при генерации по классу ;

- `Объектные характеристики, хранящиеся в json` :

  - Фильтрация:

    - Осуществляется через выражение, которое получает значение атрибута из json.

  - Заполнение на панели доступных атрибутов:

    - Все объектные характеристики класса или класса, с которым сопоставлена выборка.

- `Универсальные характеристики` :

  - Фильтрация:

    - Для единичных - осуществляется через выражение, которое получает значение атрибута из json.

    - Для множественных - формирует выражение через exists или not exists, по аналогии с фильтрацией по коллекциям.

  - Заполнение на панели доступных атрибутов. Все универсальные характеристики, объявленные на:

    - группах класса, если у класса включена `Группировка`;

    - типах объекта класса.

- `Постоянные/технические атрибуты класса`

  - Заполнение на панели доступных атрибутов:

    - всегда, при раскрытии узла, ссылочного на класс;

    - всегда, если выборка связана с классом.

  - Атрибуты

    - sHeadLine# (Заголовок);

    - sMnemoCode# (Мнемокод);

    - dModifyDate# (Дата изменения);

    - dCreateDate# (Дата создания);

    - sModifyUser# (Изменивший пользователь);

    - sCreateUser# (Создатель);

    - IdGroup# (Группа) – заполняется только для классов, имеющих группировку. Позволяет фильтроваться по группе;

    - idObject# (Объект) – специальный атрибут, позволяющий фильтроваться по идентификатору(или gidRef) объекта.

### Первичная генерация по выборке

В операции `lazyInitFilter` происходит заполнение доступных атрибутов по выборке. Ниже рассмотрены варианты.

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

### Генерация по классу

Для класса формируются:

- Атрибуты класса (кроме idClass), с не отключенным свойством доступности в фильтре:

      <attr name="bError">
          <uniFilter isActive="false"/>
      </attr>

- Все объектные характеристики, настроенные пользователями и хранящиеся в jObjAttrs_dz.

- Технические атрибуты (заголовок, дата создания, объект и тд).

- Коллекции, перечисленные в odm и в карточке класса на закладке «Коллекции универсального фильтра».

- Универсальные характеристики, объявленные:

  - на группах класса, если в классе включен сервис группировки;

  - на `типах объекта` класса, если сервис группировки не включен для класса.

Примечание

Для управления формированием атрибутов класса или добавления новых используется специальная точка расширения.

Подробности в главе `Расширенная настройка` - `Класс`.

### Атрибуты переменной ссылочности или ссылочные на миксин.

Атрибуты переменной ссылочности фильтруются через их раскрытие в дереве доступных элементов. Такие атрибуты раскрываются на классы, доступные к фильтрации по этому атрибуту:

- атрибут ссылочный на миксин, фильтруется по всем классам, к которым подключен этот миксин;

- атрибут, ссылочный на один класс, сразу фильтруется по этому классу;

- атрибут, у которого настроен перечень ссылочных классов, фильтруется по всем классам, указанных в этом перечне. Пример настройки:

      <attr name="gidRefDocument" attribute-type="Varchar" caption="Документ" order="20" type="refAnyObject">
           <uniFilter>
               <refAnyObject>
                   <ref name="Wf_Doc"/>
                   <ref name="Cnt_Contract"/>
               </refAnyObject>
           </uniFilter>
       </attr>

- Атрибут с ненастроенной ссылочностью фильтруется как значимый. Приоритет обработки ссылочности:

1. Ссылочный на класс в refClass в теге uniFilter.

2. Ссылочный на перечень классов в теге refAnyObject в теге unitFilter.

3. Ссылочный на класс в refClass в настройке самого атрибута.

## Формирование макроса

Макрос формируется в операции onApplyFilter. По умолчанию в фильтре создается главная группа, имеющая признак bisMainGroup = 1 и системное имя MainGroup, по ней и формируются макросы.

Каждая фильтруема коллекция будет представлять Exists в макросе.

### Режим onlyWhere

Стандартный режим работы фильтра, в результате выдается текст одного макроса, который будет представлять собой exists, примером может служить:

    Exists (
        select 1
          from bs_goods t#2
         where t#2.sName = '1'
           and t#2.id = t.id
        )

### Обработка сервером приложений сформированного макроса

На операции onApplyFilter сформированный макрос передается серверу, чтобы он наложил условие на запрос данных выборки:

    selection.filters().server.alias = mainGroupAlias
    selection.filters().server.condition = fltMacro.where
    selection.filters().server.isEnabled = true

## Фильтрация ROP-объектов в универсальном фильтре

### Принцип работы

Механизм фильтрации ROP-объектов работает следующим образом:

1. Преобразование данных: ROP-объекты преобразуются во временную JSON-структуру.

2. Формирование SQL-запроса: На основе настроек универсального фильтра формируется SQL-выражение.

3. Выполнение фильтрации: SQL-запрос выполняется против JSON-данных.

4. Возврат результата: Возвращаются только те ROP-объекты, которые соответствуют условиям фильтрации.

## Использование

### Основной метод

Для применения универсального фильтра к коллекции ROP-объектов используется метод `applyUniFilter` из пакета `Btk_FltObjectFilterPkg`:

    val filteredRops = Btk_FltObjectFilterPkg().applyUniFilter(rops, fltManager)

### Параметры метода

- `rops: Seq[Rop[ID, ARO]]` - коллекция ROP-объектов для фильтрации.

- `fltManager: FltManager` - менеджер универсального фильтра с настроенными условиями.

### Возвращаемое значение

Метод возвращает отфильтрованную коллекцию ROP-объектов, содержащую только те записи, которые соответствуют условиям универсального фильтра.

## Примеры использования

### Базовый пример

    // Получаем коллекцию ROP-объектов
    val rops = SomeClassApi().getList()

    // Применяем универсальный фильтр
    val filteredRops = Btk_FltObjectFilterPkg().applyUniFilter(rops, fltManager)

    // Работаем с отфильтрованной коллекцией
    filteredRops.foreach { rop =>
      // обработка отфильтрованных объектов
    }

### Использование в выборке

    override def onRefresh: Recs = {
      // Получаем исходную коллекцию ROP-объектов
      val allRops = loadRopObjects()

      // Применяем фильтрацию, если универсальный фильтр активен
      val filteredRops = if (fltManager.isActive) {
        Btk_FltObjectFilterPkg().applyUniFilter(allRops, fltManager)
      } else {
        allRops
      }

      filteredRops
    }

## Передача условий фильтра через параметры выборки

Для формирования условия используется объект класса FltCondition. В качестве имен атрибутов и коллекций можно использовать:

- Системное имя – в таком случае атрибут или коллекция ищется на первом уровне дерева доступных элементов.

- Путь – позволяет добавлять условия со вложенных уровней доступных элементов. Например, по атрибуту класса, на который ссылается атрибут верхнего уровня. Путь можно получить через `дебаггер` (атрибут `sPath`), стоя на доступном к фильтрации элементе. Пример использования:

    val fltCondition = FltCondition()

    //условия группы "отбор"
    fltCondition.withMainGroup{mainGroup =>
        //добавляем условие по атрибутам выборки
        mainGroup.addAttr("nNumber", 1.nn)
        //условие "Заполнен"
        mainGroup.addIsNotNullAttr("sCaption")
        //добавляем условие по ссылочному атрибуту с раскрытием его на подуровни
        mainGroup.addAttr("attr:idPurchaseDirection/attr:sCaption", "643")

        //добавляем условие по атрибуту класса выборки, который отсутствует в дата-сете
        mainGroup.cl.addAttr("sCreateUser_dz", "admin")

        //добавляем условие по атрибуту даты
        mainGroup.cl.addDateInterval("dCreateDate_dz", "10.02.2020".nd, "10.02.2021".nd)

        //условие "в списке"
        mainGroup.addAttr("idRef", Seq(1.nl, 2.nl, 3.nl))

        //условие "Не в списке"
        mainGroup.addAttr("idRef", Seq(1.nl, 2.nl, 3.nl), FltAttributeCompareKind.notInList)

        //добавляем группу "Или"
        mainGroup.addOrGroup{orGr =>
            orGr.addAttr("sCaption", "123")
            orGr.addAttr("sCaption", "321")
        }

        //добавляем условие по коллекции
        mainGroup.withCollection("Btk_SomeCollection") {col =>
            col.addAttr("sCaption","someValue")
        }
    }

    //добавляем условие по другой рутовой группе
    fltCondition.withRootGroup("groupName"){group =>
      //условия по атрибутам и коллекциям
      //...
    }

    //передаем условие в выборку
    Btk_ClassAvi.defList.newForm().params(Map("uniFilterCondition_dz" -> fltCondition)).open()

Внимание

Если нужно добавить условия по атрибуту класса выборки, который отсутствует в дата-сете, и объединить их в отдельную группу «И»/группу «ИЛИ», необходимо добавить .cl непосредственно перед добавлением соответствующей группы, а не самого атрибута.

Корректный пример добавления группы:

      fltCondition.withMainGroup { mainGroup =>
        mainGroup.cl.addOrGroup { orGr =>
          orGr.addAttr("attr:id/attr:jAttrDataDeb_sCaption", "123")
          orGr.addAttr("attr:id/attr:jAttrDataCr_sCaption", "123")
        }
      }

Следующий код не будет работать (ни при компиляции, ни при работе вы не получите сообщение об ошибке, но переданное подобным образом условие функционировать не будет):

      fltCondition.withMainGroup { mainGroup =>
        mainGroup.addOrGroup { orGr =>
          orGr.cl.addAttr("attr:id/attr:jAttrDataDeb_sCaption", "123")
          orGr.cl.addAttr("attr:id/attr:jAttrDataCr_sCaption", "123")
        }
      }

Примечание

Узнать путь до определенного атрибута универсального фильтра можно при помощи отладчика (Ctrl+Alt+Shift+w). Заметим, что при формировании в коде пути до ссылочного атрибута необходимо указывать строку пути полностью, например, attr:idPurchaseDirection/attr:sCaption. Для обычных атрибутов указывается сокращенный путь, например, строка mainGroup.addAttr(«nNumber», 1.nn) добавляет условие фильтрации по атрибуту attr:nNumber.

## События универсального фильтра

Работа универсального фильтра сопровождается вызовом событий — ключевых моментов жизненного цикла фильтра, которые позволяют переопределять его поведение в прикладных классах. Каждое такое событие определяется одним из наследников интерфейса `FltEvent`. Например, к событиям относится взаимодействие пользователя с объектами фильтра: установка значения ссылочного атрибута через интерфейс и т.п.

### Список событий

Внимание

Все используемые типы событий должны быть добавлены в перечисление `FltEvent.Type`.

Событие | Описание
--- | ---
`FltBeforeClientSetRefObjectEvent` | Срабатывает перед установкой значения ссылочного атрибута из интерфейса. Может переопределить `setterParams` (инициализируется пустым) и `availableValues` (создаётся от availableValues атрибута фильтра).

### Обработчик событий

Обработчиком событий УФ называется класс, который наследует интерфейс `FltEventHandler` и определяет `handleEvent(event: FltEvent)`. Экземпляр создаётся на этапе инициализации фильтра: `EntityAviDefault#onFilterInit`.

На данный момент существует единственный и стандартный обработчик — `FltAviEventHandler`:

- передаёт объект события через переменную выборки;

- находит прокси-операцию `EntityAviDefault#invokeHandleFltEvents` в целевой выборке с учётом переопределений в прикладном коде;

- выполняет обработку события.

Примечание

Можно создать собственный обработчик с изменённой логикой.

### Способы использования

#### Переопределение свойств

Целевая выборка, в которой определен универсальный фильтр, может переопределять функцию обработки событий `EntityAviDefault#handleFltEvents`. Это основной механизм взаимодействия с событиями УФ.

- Работают с мультивыбором: Переопределённые параметры корректно передаются и обрабатываются для атрибутов фильтра с мультивыбором.

- Переопределённые параметры имеют высший приоритет: Параметры ивента имеют приоритет над всеми остальными, поэтому могут перезаписывать ранее заданные ключи (даже ключи, передающиеся по умолчанию).

Это даёт большую свободу в изменении поведения универсального фильтра и связанного с ним стандартного фильтра. Пример использования можно найти в разделе Практические советы.

## Описание scala-классов, используемых фильтром

Во всех отображениях присутствует переменная `fltManager`, которая является менеджером фильтров. Она хранит объект, создающийся в операции `onFilterInit`.

Менеджер управляет фильтром, загружает/выгружает настройки, формирует макрос. Основные классы фильтра находятся в пакете `ru.bitec.app.btk.flt`.

### Avi-методы выборки

- `onFilterInit` – событие инициализации фильтра. Создает фильтр-менеджер `fltManager`.

- `onApplyFilter` – первичное применение настройки по умолчанию при отрытии выборки. Формирование макроса, применение макроса.

- `onFilterFinalize` – уничтожение фильтра.

- `uniFilter` – операция фильтра, если ее активировать, то будет возможность фильтрации.

- `lazyInitFilter` – ленивая инициализация. Вызывается при обращении к фильтру. Формирует главную группу фильтра.

## Расширенная настройка

### Класс

#### Разметка класса (odm.xml)

Для атрибута доступен тег uniFilter:

- `isActive` - по умолчанию включено, определяет наличие этого атрибута в перечне доступных для фильтрации;

- `refClass` – ссылочный класс, перекрывает настройку ссылочности самого атрибута(указанного в теге `attr`);

- тег `refAnyObject` – позволяет настроить перечень классов, на который ссылается атрибут переменной ссылочности.

Пример настройки атрибута класса в `odm.xml`:

    <attr name="bError" attribute-type="Number" caption="С ошибками" order="70" type="basic" editorType="check" defaultValue="0">
        <uniFilter isActive="false" refClass="Bs_Goods">
            <refAnyObject>
                <ref name="Bs_Goods"/>
            </refAnyObject>
        </uniFilter>
        <booleanColumn/>
    </attr>

#### Управление атрибутами в бизнес-логике

Для того чтобы управлять атрибутами в бизнес-логике используется точка расширения `Btk_Ext.afterBuildFltEntityMeta`, которая позволяет изменять атрибуты, которые формируются по умолчанию, а так же добавлять свои собственные.

Для управления атрибутами используются методы пакета `ru.bitec.app.btk.flt.meta.Btk_FltMetaAttributePkg`.

Примеры использования

### Коллекции

Для классов реализована возможность настройки отображения коллекций в УФ. Настройка находится в карточке класса на закладке «Коллекции универсального фильтра». Позволяет подключать дополнительные коллекции или переопределять существующие.

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

Для коллекций атрибут мастера – id, для v-коллекций – gid.

Для регистрации записей в настройку используется метод: `ru.bitec.app.btk.class_.flt.Btk_ClassFltCollectionApi#register`.

### Выборка

В avm выборки для атрибута доступен тег `uniFilter`, имеющий свойства:

- `isActive` – если снят, то такой атрибут не будет отображаться в списке доступных для фильтрации. По умолчанию включено.

- `isSelectionAttr` – если снят, то такой атрибут будет фильтроваться как атрибут класса (через join с таблицей класса по id), если у выборки нет класса, то атрибут не доступен для фильтрации. По умолчанию включено.

- `refSelection` – позволяет явно указать имя выборки, которая будет открываться при выборе значения в фильтре. Имеет приоритет перед свойством `refSelection` тега `ref`.

- `refRepresentation` – позволяет явно указать имя отображения, которое будет открываться при выборе значения в фильтре. Имеет приоритет перед свойством `refRepresentation` тега `ref`.

### Создание дополнительных групп фильтрации

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

Для этого требуется:

1. Инициализировать новую группу;

2. Сформировать макрос фильтрации по этой группе;

3. Полученный текст подставить в основной запрос onRefresh.

Пример создания дополнительной группы, которая позволяет фильтровать по условиям, наложенным на класс `ТМЦ`:

1. На операции `lazyInitFilter` инициализируем дополнительную группу:

       override def lazyInitFilter(): Unit = {
         if (!fltManager.isPopulatedRootGroup) {
           Btk_FltPkg().createRootGroupByClass(fltManager, "Bs_Goods", "Bs_Goods")
         }
         super.lazyInitFilter()
       }

2. На `onApplyFiler` получаем текст фильтрации по группе:

       override def onApplyFilter(): Unit = {
         super.onApplyFilter()
         //проверяем, что группы инициализированы и фильтр активен в выборке
         if (fltManager.isPopulatedRootGroup && fltManager.isActive) {
           val alias = "tt"
           val macros = Btk_FltPkg().generateMacroByGroup(fltManager, "Bs_Goods", alias)
           //если наложено условие по этой группе, то формируем текст макроса фильтрации, который будет применим к запросу данных нашей выборки
           if (macros.hasFilter) {
             selection.setMacro(
                "GdsMacro",
                s"""
                 exists (
                   select 1
                     from bs_goods $alias
                    where $alias.id = t.idGds -- join к атрибуту из дата-сета (t.idGds)
                      and ${macros.where} -- условия по этой группе
                 )
                """
            )
           } else {
             //иначе делаем макрос без наложения условий
             selection.setMacro("GdsMacro", "1=1")
           }
         }
       }

3. В операции `onRefresh` используем установленный в `onApplyFiler` макрос:

   - Через метод `prepareSelectStatement`:

       override protected def onRefresh: Recs = {
         prepareSelectStatement("&GdsMacro")
       }

   - В тексте sql-запроса:

       override protected def onRefresh: Recs = {
         """
           select .....
             from .....
            where ....
              and &GdsMacro
         """
       }

### Связь панели стандартного фильтра с универсальным фильтром

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

Основным принципом является то, что атрибуты стандартного фильтра задаются по определенным правилам, в сеттерах этих атрибутов используются специальные библиотечные методы.

Если атрибут на панели стандартного фильтра выведен без возможности смены типа сравнения, то в универсальном фильтре ему так же нельзя будет изменить тип сравнения.

Существует 3 режима связи атрибута стандартного фильтра и универсального:

- `Через панель доступных для фильтрации атрибутов`- режим, при котором атрибуты стандартного и универсального фильтра полностью интегрированы. Ограничение по таким атрибутам накладывается через макрос универсального фильтра.

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

- `Режим "Только сохранение"`- режим, при котором значения стандартного фильтра только транслируется в универсальный, без возможности его изменения в универсальном фильтре. Ограничения по таким атрибутам накладываются по правилам стандартного фильтра.

#### Добавление атрибута через панель доступных для фильтрации атрибутов УФ

Если вам требуется вывести на панель стандартного фильтра один из атрибутов, по которым позволяет фильтровать УФ, то можно воспользоваться способом создания атрибута по его пути.

Фильтрация по атрибутам коллекций также доступа в этом режиме.

##### Настройка

1. Откройте интерфейс универсального фильтра выборки, для которой хотите создать новый атрибут на панели фильтров.

2. Откройте панель доступных атрибутов.

3. Найдите доступный атрибут, условия по которому хотите накладывать через панель стандартного фильтра.

4. Выполните контекстную операцию `Сформировать текст разметки стандартного фильтра`. Операция доступа супер-пользователям или пользователям, обладающих объектной привилегий `Формирование текста разметки связей стандартного фильтра` пакета `Btk_FltPkg`.

5. В открывшемся мастере укажите:

   - `системное имя атрибута стандартного фильтра`
     По умолчанию flt_<Имя атрибута УФ>;

   - `наименование`
     По умолчанию наименование атрибута УФ;

   - `порядковый номер`
     Порядковый номер для атрибута стандартного фильтра;

   - `с условием сравнения`
     Если признак установлен, то будет сформированы атрибуты стандартного фильтра, позволяющие изменять тип сравнения.

6. Подтвердите выбор в мастере.

7. Из открывшегося окна скопируйте в `avm.xml` и `Avi` сформированный текст.

###### Создание ссылочного атрибута с типом редактора «Выпадающий список»

1. Выполните действия описанные в главе `Настройка`.

2. Смените тип данных атрибута на `Long`.

3. Сделайте атрибут не видимым.

4. Добавьте `hl`-атрибут, в котором настройте выпадающий список.

##### Описание xml-разметки

Для атрибута стандартного фильтра добавляется тег `uniFilter`, в котором указываются следующие `xml-атрибуты`:

- `attrPath`- путь атрибута универсального фильтра. Это уникальный путь атрибута, по которому можно однозначно определить атрибут в УФ. Если это атрибут коллекции, то перед его путем добавляется путь до коллекции.

- `attribute-type`- тип данных атрибута.

- `conditionAttr`- имя атрибута стандартного фильтра с типом сравнения. Содержит имя атрибута, который будет отвечать за смену типа сравнения. Если не указан, то тип сравнения для этого атрибута нельзя будет изменить в универсальном фильтре.

- `conditionType` - тип сравнения по умолчанию. Если не указан, то будет использоваться стандартный алгоритм определения типа сравнения. В качестве значений используется системное имя объекта: `ru.bitec.app.btk.flt.selected.attribute.FltAttributeCompareKind`.

- `defaultValue`- Значение по умолчанию

- `defaultValueType`- Тип значения по умолчанию

- `ref.selection` - имя выборки, которая будет открываться для выбора значений ссылочных атрибутов

- `ref.representation` - имя отображения выборки, которое будет открываться для выбора значений ссылочных атрибутов.

#### Добавление произвольного атрибута

Иногда требуется добавить в универсальный фильтр произвольный атрибут, которого нет среди доступных для фильтрации.

##### Настройка

1. Добавьте в `avm.xml` для нужного атрибута тег `uniFilter` и настройте нужные свойства.

2. Добавьте в `Avi` сеттеры для атрибутов:

   - `атрибут значения`:

         @Setter()
         def setflt_someValue(event: SetterEvent): Unit = {
           Btk_FltLib().setStdFilterValue(event)
         }

   - `атрибут типа сравнения`:

         @Setter()
         def setflt_someCondition(event: SetterEvent): Unit = {
           Btk_FltLib().setStdFilterConditionType(event)
         }

3. Добавьте ограничение в запрос выборки.
   Для добавления ограничения добавьте в операции `onRefresh` использование макросов,сформированных по каждому из атрибутов. Имя макроса указывается в свойстве `macros` тега `uniFilter`.

   Например:

        override def onRefresh: Recs = {
          prepareSelectStatement(
            s"""&flt_stdMacroName -- условие по атрибуту, имя макроса которого равно flt_stdMacroName
                """
          )
        }

   Пример сложного условия с под-запросами. В этом примере, сначала проверяется есть ли наложенное ограничение по атрибуту, и если оно есть, то добавляется под-запрос:

       override def onRefresh: Recs = {
         prepareSelectStatement(
           s"""
             ${if (fltManager.hasStdAttrCondition("flt_someAttrName")) {
               """exists (
                      select 1
                        from Some_class tt
                       where tt.id = t.idSomeAttr -- связь между основной коллекцией и таблицей   подзапроса
                         and &flt_stdMacroName -- макрос с сформированным ограничением по атрибуту
                    )"""
            } else {
              ""
            }}
          """
         )
       }

##### Описание xml-разметки

Для атрибута стандартного фильтра добавляется тег `uniFilter`, в котором указываются следующие `xml-атрибуты`:

- `attribute-type` - тип данных атрибута.

- `type` - тип ссылочности атрибута. Доступные значения:

  - `basic` - значимый;

  - `boolean` - булево;

  - `refObject` - ссылочный на объект ;

  - `refState` - ссылочный на состояние;

  - `refGroup` - ссылочный на группу.

- `expression` - `sql-выражение` для получения значения атрибута. Например: `tt.idGs`. Будет использовано при формировании макроса атрибута.

- `macros` - имя макроса, в который будет установлено сформированное `sql-выражение` по этому атрибуту. Пример выражения: `tt.idGds = 42`. В качестве правого операнда используется выражение указанное в `expression`.

- `valueMacros` - имя макроса, в который будет установленно `sql-выражение` этого атрибута, но содержащее только часть, относящуюся к значению. Например: `42`. Если в значении указан интервал, то будет использовано выражение вида `(10, 20)`, где первое значение - начало интервала, второе значение - конец. Если одна из границ не задана, то будет указано `null`.

- `ref.class` - ссылочный класс. Используется для атрибутов:

  - `Ссылочный на объект` - содержит имя класса, объекты которого будут доступны для выбора.

  - `Ссылочный на группу` или `Ссылочный на состояние` - содержит имя класса, которым будет ограничен выбор групп и состояний.

- `conditionAttr` - имя атрибута стандартного фильтра с типом сравнения. Содержит имя атрибута, который будет отвечать за смену типа сравнения. Если не указан, то тип сравнения для этого атрибута нельзя будет изменить в универсальном фильтре.

- `conditionType` - тип сравнения по умолчанию. Если не указан, то будет использоваться стандартный алгоритм определения типа сравнения. В качестве значений используется системное имя объекта: `ru.bitec.app.btk.flt.selected.attribute.FltAttributeCompareKind`

- `defaultValue` - Значение по умолчанию.

- `defaultValueType` - Тип значения по умолчанию.

- `ref.selection` - имя выборки, которая будет открываться для выбора значений ссылочных атрибутов.

- `ref.representation` - Имя отображения выборки, которое будет открываться для выбора значений ссылочных атрибутов.

##### Пример кода

`avm.xml`

    <!-- Атрибуты условия-->
    <filterAttr name="flt_idClass_idCond" attribute-type="Varchar" order="20" isVisible="false"/>
    <filterAttr name="flt_idClass_idCondHl" attribute-type="Varchar" caption="Класс" order="21" isLastInLine="false">
      <editor>
        <lookup lookupQuery="gtk-Btk_FltSelectedItemAvi#Lookup_CompareKindStdFilter" changeableAttr="flt_idClass_idCond" lookupListAttr="sListValue" lookupKeyAttr="sKeyValue" isLookupLazyLoad="true"/>
      </editor>
      <card controlWidth="40" isControlWidthFixed="true"/>
    </filterAttr>

    <!-- Атрибут для ввода значения, ссылочный на объект-->
    <filterAttr name="flt_idClass" attribute-type="Varchar" order="22" editorType="editButton">
      <editor labelPosition="none"/>
      <uniFilter conditionAttr="flt_idClass_idCondHl" attribute-type="Long" type="refObject" ref.class="Btk_Class" expression="c.id" macros="flt_idClassMacros"/>
    </filterAttr>

`Avi`

    --сеттер атрибута
    @Setter()
    def setflt_idClass(event: SetterEvent): Unit = {
      Btk_FltLib().setStdFilterValue(event)
    }

    --сеттер типа сравнения
    @Setter()
    def setflt_idClass_idCond(event: SetterEvent): Unit = {
      Btk_FltLib().setStdFilterConditionType(event)
    }

    override def onRefresh: Recs = {
      /*
      Использование макроса в запросе:
      Если по атрибуту flt_idClass не будет пользователем выбрано значение, то в макрос поставится 1=1
      Если по атрибуту flt_idClass будет пользователем наложено условие "равно <ID>", то в макрос поставится "c.id = <ID>"
      */
      """
      select t.ssystemname, count(*) as nCnt
        from btk_attribute t
        join btk_class c
          on c.id = t.idbtkclass
       where &flt_idClassMacros
      """
    }

#### Режим «Только сохранение»

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

Такой подход позволяет сохранять значения стандартного фильтра в настройках универсального.

##### Настройка

1. Настройте атрибут стандартного фильтра используя базовый подход.

2. Для атрибута добавьте тег `uniFilter`, установите свойство `saveOnly="true"`.

3. Добавьте сеттер для фильтра:

       @Setter()
       def setflt_someValue(event: SetterEvent): Unit = {
         Btk_FltLib().setStdFilterValue(event)
       }

#### Принудительная отправка значения в универсальный фильтр

Иногда требуется заполнять атрибуты стандартного фильтра не вызывая их сеттер. Т.к. на сеттерах выполняется синхронизация значения стандартного фильтра и универсального, то такой подход требует ручного указания, что значения какого-либо атрибута требуется отправить в универсальный фильтр.

Для этого можно воспользоваться методом: `Btk_FltLib.submitStdFilterValue`.

Пример использования:

    @Setter()
    def setflt_idClassHl(event: SetterEvent): Unit = {
      event.editButton match {
        case EditButton.lookup =>
          Btk_DialogLib().withLookupIdMulti(Btk_ClassAvi.listForChoose()) { ids =>
            val idss = ids.mkString(";")
            val hls = ids.map(id => Btk_ClassApi().getMnemoCode(id)).mkString(";")

            selection.clientSetSelfVar("flt_idClass", idss) // установка значения другого фильтра без вызова сеттера
            Btk_FltLib().submitStdFilterValue("flt_idClass") // отправка значения стандартного фильтра в универсальный
            selection.clientSetSelfVar("flt_idClassHl", hls)
          }
        case _ =>
      }
      Btk_FltLib().setStdFilterValue(event) // значение фильтра, для которого был вызван сеттер, будет отправлено в универсальный в этом вызове
    }

#### Управление возможностью редактирования значения атрибута в универсальном фильтре.

Если требуется запретить редактировать значение в универсальном фильтре, то можно воспользоваться методом: `Btk_FltStdLinkLib.setUniFilterReadOnly`. Часто это используется в связке вместе с блокирование атрибута и на панели фильтрации.

    val bool = true

    selection.filters().std.attrs("flt_someAttrName").isReadOnly = bool
    Btk_FltStdLinkLib().setUniFilterReadOnly("flt_someAttrName", bool)

Примечание

Не работает для атрибутов, работающих в режиме `saveOnly`. Для таких атрибутов значение всегда не редактируется.

#### Значение по умолчанию

Значение по умолчанию указывается в теге `uniFilter` в свойстве `defaultValue`. Кроме самого значения может быть указан и его тип.

Тип значения по умолчанию указывается в свойстве `defaultValueType` и может принимать значения:

- `Constant` - константа. Если тип значения по умолчанию не указан, то он считается константой.

- `Jexl`
  `Jexl`- скрипт.

##### Константное значение

В свойстве `defaultValue` указывается константное значение. Например:

- для числовых атрибутов: `123`;

- для строковых: `abc`;

- для даты: `20.12.2023 10:30:42` Если требуется указать множественное значение, то указывается массив значений, разделенных запятой;

- для числовых атрибутов: `[123,321]`;

- для строковых: `[abc,cde]` Если указывается интервальное значение, то значения указывается массив, содержащий 2 значения. Первое значение - начало интервала, второе значение - конец. Если для одной из границ интервала не задано значение, то оставляется пустая строка:

  - `[20.12.2023 10:30:42,20.12.2023 10:30:42]` ;

  - `[,20.12.2023 10:30:42]`.

##### Jexl-скрипт

В свойстве `defaultValue` указывается `jexl-скрипт`, который будет выполнен в контексте выборки для вычисления значения по умолчанию.

Если требуется указать множественное значение, то `jexl-скрипт` должен вернуть массив значений.

Если требуется указать интервальное значение, то `jexl-скрипт` должен вернуть массив, содержащий 2 значения. Первое значение - начало интервала, второе значение - конец.

#### Передача значений фильтра через параметры выборки

Для передачи значений используется тот же механизм, что и для передачи значений в универсальный фильтр.

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

Пример:

    val fltCondition = FltCondition().withStdLinkGroup{std =>
      std.addAttr("flt_idClass", Btk_ClassApi().findByMnemoCode("Btk_Class"))
      std.addAttr("flt_bBool", 1.nn)
    }

    Some_Avi.defList().newForm().params(Map("uniFilterCondition_dz" -> fltCondition)).open()

#### Получение значения атрибута фильтра

Если в прикладной логике для какого-то либо атрибута стандартного фильтра требуется получить его значение, особенно это актуально для ссылочных атрибутов, необходимо воспользоваться `библиотечным` методом: `ru.bitec.app.btk.flt.Btk_FltStdLinkLib#getAttrValue`, в который требуется передать имя атрибута `стандартного фильтра`.

Пример использования:

    val fltCond = Btk_FltStdLinkLib().getAttrValue("flt_idBisObj")
    //проверяем, что условие активно.
    if (fltCond.isActive) {
      //проверяем, что выбран множественный тип сравнения
      if (fltCond.compareKind.isMultiValue) {
        //конвертируем к массиву и обходим значения
        fltCond.value.asNLongArray.foreach{v =>
          println(v)
        }
      } else if (fltCond.compareKind.isInterval) {
        //сравнение "В интервале"
        val interval = fltCond.value.value.asInstanceOf[FltLongInterval]
        println(interval.from)
        println(interval.to)
      } else {
        //единичное значение
        println(fltCond.value.asNLong)
      }
    }

## Формирование макроса

### Строковый атрибут

- `Содержит` - накладывает условие по регистронезависимому вхождению строки.

Пример макроса:

    upper(t.sCaption) like upper('%'||'value'||'%')

- `Начинается с` - накладывает условие на регистронезависимое начало строки.

Пример макроса:

    upper(t.sCaption) like upper('value'||'%')

- `Заканчивается на` - накладывает условие на регистронезависимое окончание строки.

Пример макроса:

    upper(t.sCaption) like upper('%'||'value')

- `Равно` - накладывает условие на регистронезависимое совпадение строки.

Пример макроса:

    upper(t.sCaption) = upper('value')

- `Не равно` - накладывает условие на регистронезависимое несовпадение строки.

Пример макроса:

    upper(t.sCaption) <> upper('value')

- `В списке` - накладывает условие на регистронезависимое вхождение в список.

Пример макроса:

    upper(t.sCaption) in (upper('value'))

- `Не в списке` - накладывает условие на регистронезависимое отсутствие в списке.

Пример макроса:

    upper(t.sCaption) not in (upper('value'))

- `Заполнено` - накладывает условие на наличие значения.

Пример макроса:

    t.sCaption is not null

- `Не заполнено` - накладывает условие на отсутствие значения.

Пример макроса:

    t.sCaption is null

- `Не содержит` - накладывает условие на регистронезависимое отсутствие подстроки.

Пример макроса:

    upper(t.sCaption) not like upper('%'||'value'||'%')

- `В интервале` - накладывает условие на вхождение в заданный интервал.

Пример макроса:

    upper(t.sCaption) >= upper('value') and upper(t.sCaption) <= upper('value')

### Числовой атрибут

- `Равно` - накладывает условие на точное совпадение числового значения.

Пример макроса:

    t.nNumber = value

- `Не равно` - накладывает условие на несовпадение числового значения.

Пример макроса:

    t.nNumber <> value

- `В списке` - накладывает условие на вхождение числового значения в список.

Пример макроса:

    t.nNumber in (value)

- `Не в списке` - накладывает условие на отсутствие числового значения в списке.

Пример макроса:

    t.nNumber not in (value)

- `Заполнено` - накладывает условие на наличие числового значения.

Пример макроса:

    t.nNumber is not null

- `Не заполнено` - накладывает условие на отсутствие числового значения.

Пример макроса:

    t.nNumber is null

- `В интервале` - накладывает условие на вхождение числового значения в заданный интервал.

Пример макроса:

    t.nNumber >= value and t.nNumber <= value

- `Меньше` - накладывает условие на то, что числовое значение меньше заданного.

Пример макроса:

    t.nNumber < 1

- `Меньше или равно` - накладывает условие на то, что числовое значение меньше или равно заданному.

Пример макроса:

    t.nNumber <= 1

- `Больше` - накладывает условие на то, что числовое значение больше заданного.

Пример макроса:

    t.nNumber > 1

- `Больше или равно` - накладывает условие на то, что числовое значение больше или равно заданному.

Пример макроса:

    t.nNumber >= 1

Внимание

Для типа Long виды сравнения: Меньше, Меньше или равно, Больше, Больше или равно - не доступны!

### Булевый атрибут

- `Равно` - накладывает условие на точное совпадение булевого значения.

Пример макроса:

    and t.bBool = 1

- `Не равно` - накладывает условие на несовпадение булевого значения.

Пример макроса:

    and t.bBool <> 1

- `Заполнено` - накладывает условие на наличие числового значения.

Пример макроса:

    t.bBool is not null

- `Не заполнено` - накладывает условие на отсутствие булевого значения.

Пример макроса:

    t.bBool is null

### Дата

- `Равно` - накладывает условие на точное совпадение даты.

Пример макроса:

    t.dDate = to_timestamp('31.12.2000 00:00:00','DD.MM.YYYY HH24:MI:SS')

- `Не равно` - накладывает условие на несовпадение даты.

Пример макроса:

    t.dDate <> to_timestamp('31.12.2000 00:00:00','DD.MM.YYYY HH24:MI:SS')

- `Заполнено` - накладывает условие на наличие даты.

Пример макроса:

    t.dDate is not null

- `Не заполнено` - накладывает условие на отсутствие даты.

Пример макроса:

    t.dDate is null

- `В интервале` - накладывает условие на вхождение даты в заданный интервал.

Пример макроса:

    t.dDate >= to_timestamp('31.12.2000 00:00:00','DD.MM.YYYY HH24:MI:SS') and t.dDate <= to_timestamp('31.12.2000 00:00:00','DD.MM.YYYY HH24:MI:SS')

- `Меньше` - накладывает условие на то, что дата меньше заданной.

Пример макроса:

    t.dDate < to_timestamp('31.12.2000 00:00:00','DD.MM.YYYY HH24:MI:SS')

- `Меньше или равно` - накладывает условие на то, что дата меньше или равна заданной.

Пример макроса:

    t.dDate <= to_timestamp('31.12.2000 00:00:00','DD.MM.YYYY HH24:MI:SS')

- `Больше` - накладывает условие на то, что дата больше заданной.

Пример макроса:

    t.dDate > to_timestamp('31.12.2000 00:00:00','DD.MM.YYYY HH24:MI:SS')

- `Больше или равно` - накладывает условие на то, что дата больше или равна заданной.

Пример макроса:

    t.dDate >= to_timestamp('31.12.2000 00:00:00','DD.MM.YYYY HH24:MI:SS')

### Json атрибут

- `Заполнено` - накладывает условие на наличие json значения.

Пример макроса:

    t.jJson is not null

- `Не заполнено` - накладывает условие на отсутствие json значения.

Пример макроса:

    t.jJson is null

### Blob атрибут

- `Заполнено` - накладывает условие на наличие числового значения.

Пример макроса:

    t.lBlob is not null

- `Не заполнено` - накладывает условие на отсутствие булевого значения.

Пример макроса:

    t.lBlob is null

### Сlob атрибут

- По аналогий, как с `Строковым атрибутом`.

### Ссылочный атрибут

- По аналогий, как с `Числовым атрибутом`.

### Примеры формируемого макроса

В примере будет показана фильтрация списка объектов класса `Bs_Goods`. В макросах алиас `t` обозначает исходный запрос выборки.

#### Структура таблиц, показанных в примере

#### Примеры формируемого макроса

##### Собственное поле класса Bs_Goods

1. Добавили значимый строковый атрибут

2. Полученный макрос:

    upper(t.sPartNumber) like upper('%'||'abc'||'%')

##### Ссылочное поле класса Bs_Goods через join

1. Развернули ссылочный атрибут и добавили атрибут ссылочного класса

2. Полученный макрос:

    exists(
    select 1
      from Msr_MeasureItem t336
     where t336.id = t.idMeasureItem
    and upper(t336.sCodeGOST) = upper('10')
    )

##### Ссылочное поле класса Bs_Goods с множественным выбором

1. Добавили ссылочный атрибут с множественным выбором значений

2. Полученный макрос:

    t.idMeasureItem in (102104,101954,101967,101973)

##### Фильтрация коллекций

###### Фильтрация коллекции первого уровня вложенности Bs_GoodsSrc по значимому атрибуту

1. Добавили коллекцию `Bs_GoodsSrc`.

2. Добавили значимый атрибут.

3. Полученный макрос:

    exists(
    select 1
      from Bs_Goods t354
     where t354.id = t.id
       and exists(
         select 1
           from Bs_GoodsSrc t370
          where t370.idGds = t354.id
            and t370.bDefault = 1
       )
    )

###### Фильтрация коллекции первого уровня вложенности Bs_GoodsSrc по ссылочному атрибуту через join

1. Добавили коллекцию `Bs_GoodsSrc`.

2. Развернули ссылочный атрибут и добавили атрибут ссылочного класса.

3. Полученный макрос:

    exists(
    select 1
      from Bs_Goods t354
     where t354.id = t.id
       and exists(
         select 1
           from Bs_GoodsSrc t407
           join Bs_Prj t408 on t408.id = t407.idPrj
          where t407.idGds = t354.id
            and t408.idDepOwner = 117801
       )
    )

###### Фильтрация коллекции второго уровня вложенности Bs_PrjVerRange по атрибуту без join

1. Добавили коллекцию `Bs_GoodsSrc`.

2. Добавили коллекцию `Bs_PrjVerRange`.

3. Добавили атрибут.

4. Полученный макрос:

    exists(
    select 1
      from Bs_Goods t354
     where t354.id = t.id
       and exists(
         select 1
           from Bs_GoodsSrc t410
          where t410.idGds = t354.id
            and exists(
              select 1
                from Bs_PrjVerRange t423
               where t423.gidSrc = t410.gid
                 and t423.idPrjVerFrom = 105501
            )
       )
    )

##### Макрос со всеми вышеуказанными условиями

1. Условия:

2. Макрос:

    t.idMeasureItem in (102104,101967,101971,101973)
    and upper(t.sPartNumber) like upper('%'||'abc'||'%')
    and exists(
    select 1
      from Bs_Goods t354
      join Msr_MeasureItem t447 on t447.id = t.idMeasureItem
     where t354.id = t.id
       and upper(t447.sCodeGOST) = upper('10')
       and exists(
         select 1
           from Bs_GoodsSrc t410
           join Bs_Prj t451 on t451.id = t410.idPrj
          where t410.idGds = t354.id
            and t410.bDefault = 1
            and t451.idDepOwner = 117801
            and exists(
              select 1
                from Bs_PrjVerRange t423
               where t423.gidSrc = t410.gid
                 and t423.idPrjVerFrom = 105501
            )
       )
    )