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

# Дискретный доступ

Дискретный доступ позволяет выдавать привилегии в рамках объектов классов или записей в администрируемой выборке.

## Настройка привилегии

### Административный объект

1. Открыть карточку `Адм. объекта`

2. Установить признак `Дискретный доступ`

3. Перейти на закладку `Дискретные ограничения доступа`

4. Создать `Дискретное ограничение`.

#### Дискретное ограничение доступа

1. Создать новую запись или встать на запись для редактирования

2. Указать уникальное в рамка адм. объекта имя и наименование

3. Настроить тип правила

4. Настроить параметры правила.

5. На закладке `Скрипт для фильтрации объектных привилегий` написать sql-запрос, который будет фильтровать списки объектов

6. На закладке `Скрипт проверки строк по объектному кешу` написать jexl-скрипт, который будет по rop-у объекта или строке выборки возвращать признак, что условие выполнилось для этого объекта.

#### Тип правила

Доступны 3 варианта типа правила:

- Примитивное

- Составное

- Без параметров

##### Примитивное правило

Состоит из одного параметра.

При анализе прав пользователя со всех его ролей будут собраны значения параметров и собраны в один массив примитивных значений. Например для числового правила будет собран массив вида: `[10, 20, 30]`

##### Составное правило

Состоит из нескольких параметров.

При анализе прав пользователя используется следующий алгоритм:

1. Для каждого набора значений, указанных на роли, формируется json-объект вида:

       {
         <ИД параметра 1>: [<Массив указанных значений>],
         <ИД параметра 2>: [<Массив указанных значений>],
         ...
         <ИД параметра n>: [<Массив указанных значений>]
       }

2. Каждая роль может обладать несколькими наборами значений на одно правило. По этому результатом агрегации настроенных значений на роли будет массив, содержащий объекты, указанные в п.1:

       [
         {json-объект для набора 1},
         {json-объект для набора 2},
         ...
         {json-объект для набора n},
       ]

3. При агрегации прав пользователя:

   - со всех ролей анализируются массивы, описанные в п.2

   - в json-е заменяются ИД параметров на их имена

   - каждый элемент массива приводится к строке

   - собирается результирующий массив, содержащий уникальные строки.

     Таким образом исключаются дублирующие настройки на ролях.

Пример агрегации прав пользователя:

    [
      {
        "paramName1": ["a", "b", "c"],
        "paramName2": [1, 2, 3]
      },
      {
        "paramName1": ["c", "d", "e"],
        "paramName2": [3, 4, 5]
      }
    ]

##### Правило без параметров

Не содержит параметров.

При анализе прав пользователя, если хотя на одной роли будет правило без параметров, оно будет проверяться.

#### Параметры правила

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

- „Обычный“: значения параметров определяются на ролях.

- „Динамический“: значения параметров определяются динамически по скрипту динамического определения под параметром в настройке дискретного доступа на одминистрируемом объекте. на ролях.

- „Определяется профилем“: значения параметров в этом случае определяются от параметров с подчиненных профилей пользователя.

{important} Если для роли назначен параметр с типом «Определяется профилем», сохранить роль можно только если она имеет тип «Мастер-роль».

Если правило примитивное, то у него доступен для настройки только один параметр с системным именем «SimpleAcObjectRuleParam». Для составного правила доступны несколько параметров.

##### Динамическое определение значения параметров

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

- В карточке администрируемого объекта, на закладке с параметрами под динамическими параметрами написать jexl-скрипт, который возвращает массив строк Пример:

    var idvUser = Btk_UserApi.getCurrentUserID(); //получаем id текущего пользователя
    var res =[...];  //объявляем массив для сбора значений параметра
    if (idvUser != null){
      var rvUser = Btk_UserApi.load(idvUser);
      var idvObjAttr = Btk_UserApi.getAttrValue(rvUser, "idObjAttr"); // получаем значение атрибута с именем "idObjAttr", хранящийся для текущего пользователя
      if (!empty idvBisObj){
        res.add(idvBisObj.toString()); // добавляем полученное значение в массив
      }
    }
    res; // возвращаем массив со значениями

- На роли, на закладке с настройками дискретного доступа для нашего правила у параметра выбираем тип «Динамический».

#### Скрипт для фильтрации объектных привилегий

Используется для фильтрации списков объектов, через наложение макроса универсального фильтра `&DefUniFltMacros`.

Доступные макросы внутри текста скрипта:

- `(&<Имя атрибута>)`
  Будет заменено на указанное имя атрибута. Например, `t.id`
  Для фильтрации доступны все атрибуты дата-сета выборки шапки БО.

- `(&params)`
  Массив строк, в котором будут параметры, собранные со всех ролей пользователя.

- `(&params_<Имя атрибута>)`
  Массив значений указанного атрибута.

- `(&CurrentUserID)`
  Будет заменено на ИД текущего пользователя.

- `(&CurrentUserName)`
  Будет заменено на имя текущего пользователя.

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

#### Бинды дискретного доступа

Бинды дискретного доступа используются для параметризации выборок и SQL‑запросов. Доступны два вида биндов:

- Selection — установка параметров в выборку.

- Anorm — установка параметров в SQL‑запрос.

Если для формирования биндов использовался макрос `(&params_<Имя атрибута>)`, такие бинды хранят массив значений:

- Selection-бинд хранит массив `Array[Any]`.

- Anorm-бинд хранит `PgArray` одного из типов:

  - `StringPgArray`;

  - `NumberPgArray`;

  - `LongPgArray`;

  - `DatePgArray`;

  - `GidPgArray`.

Если бинд был сформирован от `(&params)`, `(&CurrentUserID)` или `(&CurrentUserName)`, он хранит строковое значение. Для обоих биндов доступен метод `value`, возвращающий `NString`.

Внимание

Вызов метода `value` для биндов, содержащих массив значений, не поддерживается. Попытка вызова приведёт к генерации `UnsupportedOperationException`.

##### Использование Selection‑биндов

Для получения Selection‑биндов в метод `generateDiscreteAccessMacros` необходимо передать:

    useBinds = true
    bindType = DiscreteAccessBindType.Selection

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

- `asSelectionBind.addVar`;

- `asSelectionBind.setVar`.

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

    override def beforeOpen(): Unit = {
      super.beforeOpen()
      val discrAccess = Btk_AdminPkg().generateDiscreteAccessMacros(
        spAcObject = "Stk_RegistryGdsMov",
        tableAlias = "someTableAlias",
        useBinds = true,
        bindType = DiscreteAccessBindType.Selection
      )

      discrAccess.binds.foreach { bind =>
        if (!varExists(bind.name.get)) {
          bind.asSelectionBind.addVar(selection)
        } else {
          bind.asSelectionBind.setVar(selection)
        }
      }
    }

Внимание

Вызов `asAnormBind` у Selection‑бинда запрещён. Попытка вызова приведёт к генерации `UnsupportedOperationException`.

Внимание!

Следующий способ является устаревшим:

    discrAccess.binds.foreach { bind =>
      if (!varExists(bind.name.get)) {
        selection.addVar(bind.name.get, bind.value, FieldType.ftString)
      } else {
        selection.setVar(bind.name.get, bind.value)
      }
    }

##### Использование Anorm‑биндов

Для получения Anorm‑биндов в метод `generateDiscreteAccessMacros` необходимо передать:

    useBinds = true
    bindType = DiscreteAccessBindType.Anorm

Метод для извлечения бинда:

- `asAnormBind.get` — возвращает `NamedParameter`.

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

    override protected def onRefresh: Recs = {
      val discrAccess = Btk_AdminPkg().generateDiscreteAccessMacros(
        spAcObject = Stk_RegistryGdsMovApi().entityName,
        tableAlias = "someTableAlias",
        useBinds = true,
        bindType = DiscreteAccessBindType.Anorm
      )

      val bindBuffer = ArrayBuffer[NamedParameter]()
      discrAccess.binds.foreach { discreetBind =>
        bindBuffer += discreetBind.asAnormBind.get
      }

      for (sel <- new ASelect {
        val id = asNLong("id")
        SQL(
          s"""select someTableAlias.id as id
             |  from Stk_RegistryGdsMov someTableAlias
             | where
             | ${discrAccess.text}""".stripMargin)
        on(bindBuffer.toArray: _*)
      }) yield sel.id()
    }

Внимание

Вызов `asSelectionBind` у Anorm‑бинда запрещён. Попытка вызова приведёт к генерации `UnsupportedOperationException`.

Внимание!

Следующий способ является устаревшим:

    val bindBuffer = ArrayBuffer[NamedParameter]()
    macros.binds.foreach { discreetBind =>
      bindBuffer += NamedParameter(discreetBind.name.get, discreetBind.value)
    }

#### Скрипт проверки строк по объектному кешу

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

Представляет из себя `jexl-скрипт`, который возвращает `true`, если объект проходит условие, и `false` - если не проходит.

Доступные переменные:

- `row` \

  - Если адм. объект создан по классу, то в этой переменной будет `rop` проверяемого объекта. Это не исходный `rop`, а его обертка `JexlRop`, которая позволяет обращаться к полям объекта.

    Совет

    Для получения исходного `rop` воспользуйтесь следующим примером:

        //в переменной jexlRop находится объект класса JexlRop
        var rop = jexlRop.data()

  - Если адм. объект создан не по классу, то здесь будет json-представление строки дата-сета выборки.

- `params`
  массив значений параметров, собранных со всех ролей пользователя.

  - если правило примитивное - то будет содержать примитивы (строка или число)

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

### Роль

1. Открыть карточку роли или выбрать ее в списке;

2. перейти на закладку `Дискретный доступ`;

3. создать новую запись, выбрав адм. объект;

4. указать значение параметров для ограничений;

5. сопоставить объектные и элементарные привилегии с нужным значением параметров ограничений, заполнив поле `Ограничение дискретного доступа`

#### Ограничение доступа к объектам класса.

Выдается через объектные привилегии с системными именами `edit#` и `view#`

Совет

Если таких привилегий нет у адм. объекта, то выполните его синхронизацию.

Дискретные ограничения применяются только, если:

- это главная выборка адм. объекта (шапка БО);

- это не супер-пользователь;

- к адм. объекту приминаются настройки администрирования;

- адм. объект имеет признак `Дискретный доступ`.

#### Определение объектов для проверки дискретных прав

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

Алгоритм работы метода:

1. Если выборка принадлежит классу (`thisApi() != null`), то для всех выделенных записей через поле первичного ключа загружаются `провайдеры строк`, из них через поле `gidRoot_dz` определяются шапки БО

2. Если выборка не принадлежит классу (`thisApi() == null`), то по всем выделенным строкам собирается json-массив, содержащий на каждую строку json-объект, в котором ключ - имя атрибута, значение - значение атрибута.

##### Ограничение списков

Используя значения параметров всех ролей и настроенные «Скрипты для фильтрации объектных привилегий» формируется условие ограничения и накладывается фильтрация через макрос `&DefUniFltMacros`.

##### Ограничение открытия карточек

На открытие карточки используя значения параметров всех ролей выполняется `jexl-скрипт`, настроенный в `Скрипт проверки строк по объектному кешу`. Если у пользователя нет прав на объект, то будет выдана ошибка.

##### Ограничение на редактирование объектов

На `beforeEdit` или `delete` проверяется наличие привилегии `edit#` (выполняется `jexl-скрипт`, настроенный в `Скрипт проверки строк по объектному кешу`). Если у пользователя нет прав на объект, то будет выдана ошибка.

#### Ограничение на объектные привилегии

Если для объектной привилегии указано `Ограничение дискретного доступа`, то такая привилегия может быть проверена только в контексте какого-либо объекта. Для проверки объектных привилегий используется метод `Btk_AdminPkg().hasObjPriv`.

Пример:

    if (Btk_AdminPkg().hasObjPriv("SomeObjectName", "SomePrivName", Seq(rop))) {
       //код, выполняемый при наличии привилегии
    }

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

#### Ограничение на элементарные привилегии

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

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

При выполнении операции проверяются дискретные права на эту элементарную привилегию:

1. Получение `acRows`

2. Для полученных строк выполняется `jexl-скрипт`, настроенный в `Скрипт проверки строк по объектному кешу`

3. Если прав нет, то выдается ошибка.

#### Принудительное включение и отключение проверки дискретного доступа

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

Таким образом, установка параметра `UseDiscreteAccess` в значение равное `1` позволит принудительно применить настройки дискретного доступа, независимо от настройки на административном объекте. Пример принудительного включения проверки дискретного доступа:

    Bs_BisObjAvi.processIdMCEvent(
          event,
          { id => setClientidBisObjFrom(editRef(), id) },
          params = Map(RefEventParam.UseDiscreteAccess -> 1)
    )

При установка значения параметра `UseDiscreteAccess` в значение равное `0` настройки дискретного доступа будут игнорироваться, вне зависимости от настройки дискретного доступа на административном объекте. Пример принудительного выключения проверки дискретного доступа:

    Bs_BisObjAvi.processIdMCEvent(
          event,
          { id => setClientidBisObjFrom(editRef(), id) },
          params = Map(RefEventParam.UseDiscreteAccess -> 0)
    )

Если параметр не был передан при вызове метода-обработчика то проверка дискретного доступа осуществляется по настройке на административном объекте.

Параметр учитывается на всех методах-обработчиках (`processRefEvent`, `processHLEvent`, `processIdMCEvent` и т.д.).

### Проверка прав доступа на объект после его редактирования

1. Открыть карточку `Адм. объекта`

2. Установить признак `Дискретный доступ`

3. Установить признак `Проверять изменение прав объектов на сохранении`

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

## Деривация профилей

Для дифференцирования доступа в разрезе требуемых организационных уровней (складов, бизнес-единиц, организаций и пр.), реализован следующий подход:

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

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

3. Для подчиненного профиля указываются конкретные значения параметров дискретного доступа.

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

## Подправила

### Особенности подправил

Для каждого правила можно создать подправило. Оно будет использовать те же параметры, что и основное правило, но при этом автоматически нигде не проверяется. Программист должен самостоятельно внедрить соответствующую проверку в коде. После этого администратор, зная имя подправила, сможет задать для него нужные ограничения и проверки.

### Для администрируемого объекта можно создать подправило, выполнив следующие шаги:

1. Открыть карточку администрируемого объекта.

2. Перейти в закладку Дискретные ограничения доступа.

3. Выбрать существующие правило или создать его.

4. Под выбранным или созданным правилом добавить подправило.

5. Назвать подправило именем, которое используется в коде для проверки доступа.

6. Задать необходимые скрипты для подправила.

После выполнения этих шагов подправило будет создано и применено в соответствии с заданными параметрами.

Пример проверки подправила в выборке:

    override protected def onRefresh: Recs = {
       lazy val sSubRule = "SomeSubRuleName"

       val ropav = SomeApi().refreshByParent(getIdMaster)

       if (needApplyDiscreteAccess) {
          lazy val bHasGroup = Btk_AcLib().hasBtkGroup(acObject, acObject)
          ropav.filter { rv =>
             val rows = AcRows(Seq(rv))

             val viewPriv = if (bHasGroup) Btk_AdminPkg.viewGroupPriv.get else Btk_AdminPkg.viewObjPriv.get
             val editPriv = if (bHasGroup) Btk_AdminPkg.editGroupPriv.get else Btk_AdminPkg.editObjPriv.get

             Btk_AdminPkg().hasObjPrivByRows(acObject.get, viewPriv, rows, spSubRule = sSubRule) ||
                     Btk_AdminPkg().hasObjPrivByRows(acObject.get, editPriv, rows, spSubRule = sSubRule)
          }.map { rop =>
             Row(rop.idJ, SomeApi().getHeadLine(rop.idJ))
          }
       } else {
          ropav.map { rop =>
             Row(rop.idJ, SomeApi().getHeadLine(rop.idJ))
          }
       }
    }

## Проверка полномочий в печатных формах

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

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

- Настроек административного объекта

- Параметров, настроенных в ролях пользователя

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

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

1. На закладке настроен доступ к администрируемым объектам

2. В запросе есть обращение к соответствующей таблице

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

### Настройка дискретного доступа в отчетах

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

1. В отчете `Rpt_Report` на закладке «Настройка контроля доступа к данным печатной формы» выбрать администрируемый объект для ограничения доступа

2. Указать объектную привилегию (если не выбрана, будет проверяться привилегия «Доступ на чтение из печатных форм» - `viewReport#`)

3. В поле «Список алиасов» указать алиасы таблиц через «;» (если поле пустое, по умолчанию используется «t»)

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

### Использование макросов дискретного доступа в Jasper-отчетах

В Jasper-отчетах макрос передается как параметр. Имя параметра формируется по шаблону: `DM_(<системное_имя_административного_объекта>)_(<алиас_таблицы>)`

Чтобы использовать параметр как макрос для подстановки скрипта дискретного доступа, в запросе его нужно указать со знаком «!» (восклицательный знак).

Пример объявления параметра и использования в запросе для ограничения доступа к таблице `Btk_User`:

    <parameter name="DM_(BTK_USER)_(T)" class="java.lang.String">
        <defaultValueExpression><![CDATA["1=2"]]></defaultValueExpression>
    </parameter>
    <queryString>
        <![CDATA[select sHeadLine_dz
                  from btk_user t
                  where $P!{DM_(BTK_USER)_(T)}]]>
    </queryString>

Рекомендация: Для тестирования отчета можно указать значение по умолчанию как «1=1», но перед загрузкой формы в приложение его следует убрать, чтобы избежать случайного предоставления полного доступа при ошибках в настройках.

### Использование макросов дискретного доступа в отчетах типа «Текстовый», «Excel», «Word»

В запросе необходимо указать макрос, который будет заменен скриптом дискретного доступа. Макрос формируется по шаблону:

`&DM_(<системное_имя_административного_объекта>)_(<алиас_таблицы>)`

Пример запроса для печатной формы типа «Word» с ограничением доступа к таблице `Btk_User`:

    	[#&Order= select sHeadLine_dz, sMnemoCode_dz from BTK_USER t
    where &DM_(Btk_User) #]

## Примеры скриптов

### Примитивное правило по атрибуту класса

Правило значимое, тип данных строка. Фильтрация по like атрибута `sCode`

Скрипт для фильтрации объектных привилегий:

    select 1
      from RplTst_ClassTree tt
      join (
         select cast(json_array_elements_text(cast((&params) as json)) as varchar) as sCode
          )  as codes
       on tt.sCode like codes.sCode
    where tt.id = (&id)

Скрипт проверки строк по объектному кешу:

    for (p: params) {
      if (row.sCode != null && p != null) {
        if (like(row.sCode, p)) {
         return true;
        }
      }
    }

### Примитивное правило по атрибуту коллекции

Правило ссылочное на `Btk_Group`. Фильтрация по прямому вхождению объекта в группу (фильтрация коллекции `Btk_ObjectGroup`).

Скрипт для фильтрации объектных привилегий:

    select 1
      from Rpltst_TestGroup tt
      join Btk_ObjectGroup og
        on tt.gid = og.gidSrcObject
      join (
         select cast(json_array_elements_text(cast((&params) as json)) as int8) as id
          )  as params
       on og.idGroup = params.id
    where tt.id = (&id)

Скрипт проверки строк по объектному кешу:

    for (p: params) {
      var rops = toJRops(Btk_ObjectGroupApi.byParent(row.data())).asList();
      if (p != null) {
        for (r: rops) {
          if (r.idGroup == p) {
            return true;
          }
        }
      }
    }

### Примитивное правило по адм. объекту, созданному не по классу (произвольная выборка)

Правило значимое, тип данных строка. Фильтрация регистронезависимая по like поля `sClass`

Скрипт для фильтрации объектных привилегий:

    select 1
       from json_array_elements_text(cast((&params) as json)) as p
      where upper((&sClass)) like upper(p)

Скрипт проверки строк по объектному кешу:

    for (p: params) {
      if (row.sClass != null && p != null) {
        if (like(row.sClass.toUpperCase(), p.toUpperCase())) {
         return true;
        }
      }
    }

### Составное правило по 3 атрибутам класса

Параметры правила:

1. `dDate` - Значимый, тип данных дата.

2. `nNumber` - Значимый, тип данных число

3. `sCaption` - Значимый, тип данных строка

Фильтрация отбирает записи у которых дата меньше указанной, число равно указанному, и Наименование по like регистронезависимо совпадает с указанной

Скрипт для фильтрации объектных привилегий:

    select 1
      from RplTst_AllDbTypes tt
     where tt.id = (&id)
       and exists (
        select 1
          from (
            select v -> 'dDate' as dDate
                  ,v -> 'nNumber' as nNumber
                  ,v -> 'sCaption' as sCaption
              from json_array_elements(cast((&params) as json)
                ) as v
            ) p
          where exists (
            select 1
              from json_array_elements_text(p.dDate) pp
              where tt.dDate <= to_timestamp(pp, 'DD.MM.YYYY HH24:MI:SS')
          )
            and exists (
            select 1
              from json_array_elements_text(p.nNumber) pp
              where tt.nNumber = cast(pp as numeric)
          )
            and exists (
            select 1
              from json_array_elements_text(p.sCaption) pp
              where upper(tt.sCaption) like upper(pp)
          )
       )

Скрипт проверки строк по объектному кешу:

    for (p: params) {
      var jObj = toJObject(p);
      var res = true

      //проверка даты
      if (res) {
        res = false
        var aValues = jObj.childJArray("dDate");
        var i = 0
        while(i < aValues.size() && !res) {
          var value = aValues.getDate(i);
          if (row.dDate <= value) {
            res = true;
          }
          i = i + 1;
        }
      }

      //проверка числа
      if (res) {
        res = false
        var aValues = jObj.childJArray("nNumber");
        var i = 0
        while(i < aValues.size() && !res) {
          var value = aValues.getNumber(i);
          if (row.nNumber == value) {
            res = true;
          }
          i = i + 1;
        }
      }

      //проверка наименования
      if (res) {
        res = false
        var aValues = jObj.childJArray("sCaption");
        var i = 0
        while(i < aValues.size() && !res) {
          var value = aValues.getString(i);
          if (row.sCaption != null && value != null) {
            if (like(row.sCaption.toUpperCase(), value.toUpperCase())) {
              res = true;
            }
          }
          i = i + 1;
        }
      }

      if (res) {
        return true;
      }

    }

### Составное правило по атрибуту класса и коллекции

Параметры правила:

1. `sCaption` - Значимый, тип данных строка

2. `idGroup` - ссылочный на `Btk_Group`

Фильтрация отбирает записи, у которых Наименование по like регистронезависимо совпадает с указанным, и есть прямое вхождение в группу (фильтрация коллекции `Btk_ObjectGroup`).

Скрипт для фильтрации объектных привилегий:

    select 1
      from RplTst_TestGroup tt
     where tt.id = (&id)
       and exists (
         select 1
         from (
           select v -> 'sCaption' as sCaption
                 ,v -> 'idGroup' as idGroup
             from json_array_elements(cast((&params) as json)) as v
           ) p
         where exists (
           select 1
             from json_array_elements_text(p.sCaption) pp
             where upper(tt.sCaption) like upper(pp)
         )
           and exists (
             select 1
               from Btk_ObjectGroup og
               join json_array_elements_text(p.idGroup) pp
                 on og.idGroup = cast(pp as int8)
               where og.gidSrcObject = tt.gid
           )
       )

Скрипт проверки строк по объектному кешу:

    for (p: params) {
      var jObj = toJObject(p);
      var res = true

      //проверка наименования
      if (res) {
        res = false
        var aValues = jObj.childJArray("sCaption");
        var i = 0
        while(i < aValues.size() && !res) {
          var value = aValues.getString(i);
          if (row.sCaption != null && value != null) {
            if (like(row.sCaption.toUpperCase(), value.toUpperCase())) {
              res = true;
            }
          }
          i = i + 1;
        }
      }

      //проверка группы
      if (res) {
        res = false
        var rops = toJRops(Btk_ObjectGroupApi.byParent(row.data())).asList();
        var aValues = jObj.childJArray("idGroup");
        var i = 0
        while(i < aValues.size() && !res) {
          var value = aValues.getLong(i);
          for (r: rops) {
            if (r.idGroup == value) {
              res = true;
            }
          }
          i = i + 1;
        }
      }

      if (res) {
        return true;
      }

    }

### Составное правило по 2 атрибутам адм. объекта, созданного не по классу (произвольная выборка)

Параметры правила:

1. `sClass` - Значимый, тип данных строка

2. `idRefClass` - ссылочный на `Btk_Class`

Фильтрация отбирает записи, у которых поле `sClass` по like регистронезависимо совпадает с указанным, и поле `idRefClass` совпадает с указанным

Скрипт для фильтрации объектных привилегий:

    select 1
       from (
         select v -> 'sClass' as sClass
              ,v -> 'idRefClass' as idRefClass
           from json_array_elements(cast((&params) as json)) as v
        ) p
      where exists (
          select 1
            from json_array_elements_text(p.sClass) pp
           where upper((&sClass)) like upper(pp)
        )
        and exists (
          select 1
            from json_array_elements_text(p.idRefClass) pp
           where (&idRefClass) = cast(pp as int8)
        )

Скрипт проверки строк по объектному кешу:

    for (p: params) {
      var jObj = toJObject(p);
      var res = true

      //проверка sClass
      if (res) {
        res = false
        var aValues = jObj.childJArray("sClass");
        var i = 0
        while(i < aValues.size() && !res) {
          var value = aValues.getString(i);
          if (row.sClass != null && value != null) {
            if (like(row.sClass.toUpperCase(), value.toUpperCase())) {
              res = true;
            }
          }
          i = i + 1;
        }
      }

      //проверка idRefClass
      if (res) {
        res = false
        var aValues = jObj.childJArray("idRefClass");
        var i = 0
        while(i < aValues.size() && !res) {
          var value = aValues.getLong(i);
          if (row.idRefClass == value) {
            res = true;
          }
          i = i + 1;
        }
      }

      if (res) {
        return true;
      }

    }