# Пространства правил

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

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

Механизм подходит для сценариев, в которых обработка зависит от реквизитов объекта, переданных параметров, данных запроса или значений, полученных при выполнении предыдущих правил.

Путь: `Конфигуратор > Настройки и сервисы > Сервисы JEXL > Управление бизнес-правилами`.

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

Для работы с пространством правил:

1. **Создайте пространство правил** — оно объединяет связанную бизнес-логику и является основной точкой настройки механизма.
2. **Определите входные данные** — настройте переменные, значения которых будут передаваться при запуске. Если для выполнения правил необходимо получить данные запросом, добавьте источник данных.
3. **Создайте наборы правил** — разделите бизнес-логику на этапы и определите порядок их выполнения.
4. **Добавьте правила в наборы** — настройте условия и действия, которые должны выполняться в рамках каждого этапа.
5. **При необходимости создайте функции** — вынесите в них логику, которую необходимо использовать повторно.
6. **Запустите пространство** — в прикладном сценарии вызовите его из кода и передайте необходимые параметры. Для ручной проверки настройки используйте операцию **Тестирование документа** в интерфейсе управления бизнес-правилами.

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

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

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

```text
Пространство правил
├── параметры пространства
├── доступ
├── источники данных
├── функции
└── наборы правил
    ├── параметры набора
    └── правила
```

Основные компоненты пространства:

| Компонент | Назначение |
|---|---|
| Пространство правил | Объединяет связанную бизнес-логику и ее настройки |
| Источник данных | Получает строки данных, для которых выполняются правила |
| Параметр источника | Связывает входное значение с колонкой источника данных |
| Переменная | Хранит входное или вычисленное значение, доступное во время выполнения |
| Набор правил | Объединяет правила в отдельный этап и определяет порядок их выполнения |
| Правило | Проверяет условие и выполняет настроенное действие |
| Функция | Содержит повторно используемую логику, которую можно вызвать из правила |
| Доступ | Определяет пользователей, которым разрешено редактировать пространство |

## Работа в интерфейсе

Откройте пункт **Управление бизнес-правилами**. В левой части формы отображается дерево пространств. При выборе узла справа открывается соответствующая карточка.

![Интерфейс управления бизнес-правилами](img_serv/rules1.png)

Внутри каждого пространства дерево содержит группы:

- **Источники данных**;
- **Функции**;
- **Наборы правил**.

Правила открываются из карточки соответствующего набора.

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

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

1. Откройте операцию **Создать**.
2. Выберите **Пространство правил**.
3. Заполните обязательные поля **Код** и **Наименование**.
4. При необходимости укажите **Описание** и **Уровень логирования**.
5. Сохраните пространство.

Создатель пространства автоматически получает право его редактировать.

Основные поля пространства:

| Поле | Назначение |
|---|---|
| Код | Уникальное имя, которое передается при программном запуске |
| Наименование | Понятное пользователю название пространства |
| Описание | Назначение и область применения настройки |
| Уровень логирования | Уровень, используемый при запуске без явно переданного уровня |
| Не используется | Запрещает выполнение пространства |

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

### Настройка доступа

Добавьте пользователя на вкладке **Доступ** и установите признак **Доступно редактирование**.

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

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

### Настройка переменных

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

Переменные бывают двух уровней:

- переменные пространства доступны всем его наборам;
- переменные набора добавляются к контексту при выполнении этого набора.

Основные поля переменной:

| Поле | Назначение |
|---|---|
| Код | Имя переменной в карте параметров и JEXL-коде |
| Наименование | Понятное пользователю название |
| Тип данных | Тип значения переменной |
| Тип атрибута | Дополнительная характеристика значения |
| Ссылочный класс | Класс объекта для ссылочных значений |
| Значение по умолчанию | Значение при отсутствии другого источника |
| Доступен для фильтра | Разрешает использовать переменную в универсальном фильтре |
| Входящий параметр | Требует получить значение при запуске либо использовать значение по умолчанию |

Коды переменных должны быть уникальными в своей области применения.

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

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

### Добавление источника данных

Источник данных определяет набор строк, для которых выполняются правила.

1. Выберите пространство в дереве.
2. Откройте операцию **Создать**.
3. Выберите **Источник данных**.
4. Укажите модель запроса.
5. При необходимости установите признак **Запрос инициации выполнения правил**.
6. Настройте параметры источника.
7. Сохраните изменения.

![Источник данных пространства правил](img_serv/rules2.png)

В одном пространстве может быть только один источник с признаком **Запрос инициации выполнения правил**. При установке признака у нового источника он снимается у ранее выбранного источника.

Карточка источника предоставляет доступ к его тексту, колонкам и параметрам. Параметр источника связывает:

- колонку модели запроса;
- переменную пространства;
- значение этой переменной из карты параметров запуска.

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

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

```{note}
Значение параметра источника добавляется в условие отбора только тогда, когда одноименный параметр передан при запуске.
```

### Создание набора правил

1. Выберите пространство или группу **Наборы правил**.
2. Откройте операцию **Создать**.
3. Выберите **Набор правил**.
4. Заполните код и наименование.
5. При необходимости укажите даты начала и окончания действия.
6. Добавьте параметры и правила.
7. Сохраните набор.

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

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

Признак **Не используется** отключает набор.

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

Карточка набора содержит вкладки:

- **Правила** — список правил набора;
- **Параметры** — переменные, действующие при выполнении набора.

### Создание правила

1. Откройте вкладку **Правила** в карточке набора.
2. Нажмите **Создать**.
3. Выберите тип правила.
4. Заполните код, наименование и описание.
5. Настройте условие входа и действие.
6. Сохраните правило.

![Набор правил и условие универсального фильтра](img_serv/rules3.png)

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

Признак **Не используется** исключает правило из выполнения пространства или набора.

Правило может содержать условие входа и действие. Система всегда проверяет условие перед выполнением действия. Если процедура условия не задана, условие считается выполненным.

Доступны следующие типы правил:

| Тип | Поведение |
|---|---|
| Условие универсального фильтра | Формирует читаемый текст и JEXL-процедуру по настройке фильтра |
| Вызов функции | Выполняет процедуру выбранной функции пространства |
| JEXL-код | Выполняет JEXL-процедуру, связанную с правилом |

```{attention}
Если условие возвращает `false`, система не только пропускает действие текущего правила, но и прекращает выполнение оставшихся правил этого набора.
```

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

### Условие универсального фильтра

Для правила типа **Условие универсального фильтра** условие можно настроить без ручного написания JEXL-кода. В фильтре доступны:

- колонки источника-инициатора;
- переменные пространства с признаком **Доступен для фильтра**;
- переменные текущего набора с таким же признаком.

При сохранении фильтра система формирует текстовое представление условия и JEXL-процедуру.

```{attention}
Ручное изменение сформированного JEXL-кода не изменяет настройку универсального фильтра.
```

### Настройка функции

Функция содержит повторно используемое действие пространства. Правило типа **Вызов функции** выбирает функцию и выполняет связанную с ней процедуру.

1. Выберите пространство или группу **Функции**.
2. Откройте операцию **Создать**.
3. Выберите **Функция**.
4. Заполните код и наименование.
5. Выберите тип функции.
6. Настройте универсальный фильтр или JEXL-код.
7. Сохраните функцию.

Для функции можно использовать универсальный фильтр или JEXL-код. В универсальном фильтре функции доступны переменные пространства с признаком **Доступен для фильтра**.

## Способы запуска

Пространство правил можно запустить двумя способами:

- **из прикладного кода** — через методы `Bts_RuleSpacePkg.execute(...)` и `Bts_RuleSpacePkg.executeSets(...)`;
- **из интерфейса** — операцией **Дополнительно > Тестирование документа** в форме управления бизнес-правилами.

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

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

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

1. Находит пространство по коду и проверяет, что оно используется.
2. Определяет уровень логирования.
3. Получает строки источника-инициатора либо создает один контекст без источника.
4. Создает отдельный контекст для каждой строки.
5. Заполняет переменные пространства.
6. Выбирает активные наборы и сортирует их по порядку срабатывания.
7. Перед каждым набором добавляет его локальные переменные.
8. Выбирает активные правила набора и сортирует их по порядку выполнения.
9. Проверяет условие и выполняет действие каждого правила.
10. Возвращает результат и идентификатор лог-сессии.

Переменные одного обработанного ряда источника не переходят в контекст следующего ряда.

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

## Запуск из прикладного кода

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

### Запуск всего пространства

Метод `execute` принимает:

| Параметр | Назначение |
|---|---|
| `spSpaceCode` | Код пространства |
| `params` | Карта «код переменной → значение» |
| `spLogLevel` | Необязательный уровень логирования |

<!-- Начало кода -->
```text
var ropSomeDoc = Some_DocApi.load(id);
var spaceCode = "SomeSpace";
var args = {
  "gidObj" -> ropSomeDoc.gid,
  "someFlag" -> true
};

var result = Bts_RuleSpacePkg.execute(spaceCode, args);
var resultValue = result.value;
var idLogSession = result.idLogSession;
```
<!-- Конец кода -->

### Запуск выбранного набора

Метод `executeSets` дополнительно принимает код набора. Остальные наборы пространства не выполняются.

<!-- Начало кода -->
```text
var ropSomeDoc = Some_DocApi.load(id);
var spaceCode = "SomeSpace";
var setCode = "SomeSet";
var args = {
  "gidObj" -> ropSomeDoc.gid,
  "someFlag" -> true
};

var result = Bts_RuleSpacePkg.executeSets(
  spaceCode,
  setCode,
  args,
  "INFO"
);

var resultValue = result.value;
var idLogSession = result.idLogSession;
```
<!-- Конец кода -->

Методы возвращают объект со свойствами:

- `value` — результат последней выполненной процедуры;
- `idLogSession` — идентификатор лог-сессии.

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

## Контекст выполнения

Каждая процедура получает переменные контекста как именованные параметры. Дополнительно доступен объект `ctx`, через который JEXL-код может читать и изменять значения.

При формировании контекста используются:

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

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

### Работа с `ctx`

Объект `ctx` позволяет передавать значения между последовательно выполняемыми правилами и функциями в рамках текущего контекста. Для записи значения используйте `ctx.setVar(...)`, для получения — `ctx.getVar(...)`.

Например, если результат проверки определяет счет для общей функции, после правила с условием добавьте правило типа **JEXL-код** и сохраните выбранное значение:

<!-- Начало кода -->
```text
ctx.setVar('idAccToSet', idSelectedAcc);
```
<!-- Конец кода -->

В вызываемой функции получите это значение:

<!-- Начало кода -->
```text
var idAcc = ctx.getVar('idAccToSet');
```
<!-- Конец кода -->

В первом аргументе методов указывается код переменной. Значение, установленное через `ctx.setVar(...)`, доступно последующим правилам и функциям текущего контекста.

Если переменная с указанным кодом отсутствует в контексте, `ctx.getVar(...)` возвращает `null`. Метод `ctx.setVar(...)` изменяет только контекст выполнения: значение не сохраняется в объекте или базе данных автоматически и не преобразуется к типу, указанному в настройке переменной. Передаваемое значение должно соответствовать типу, ожидаемому последующими правилами и функциями.

## Логирование

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

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

Журнал может содержать:

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

Если до запуска уже существует лог-сессия, механизм использует ее. В противном случае создается новая сессия. Ее идентификатор возвращается в `idLogSession`.

## Тестирование в интерфейсе

Операция **Дополнительно > Тестирование документа** доступна в карточке набора правил.

1. Откройте нужный набор.
2. Выберите **Дополнительно > Тестирование документа**.
3. Введите GID объекта.
4. Подтвердите запуск.
5. Просмотрите открывшийся журнал выполнения.

```{attention}
Текущая операция запускает все пространство, а не только открытый набор. Она передает GID в параметре `gid` и выполняет обычные процедуры правил. Операция не создает изолированную проверочную транзакцию, не отменяет изменения и не формирует отчет «было — стало». Используйте ее только для правил, безопасных для выполнения над выбранным объектом.
```

Дополнительные параметры и уровень логирования в диалоге операции не запрашиваются.

## Ошибки и ограничения

### Ошибки программного запуска

Метод `execute` записывает ошибки выполнения в журнал, но может не передать `AppException` вызывающему коду. В таком случае вызов возвращает результат, значение которого может быть пустым. Если результат влияет на продолжение прикладной операции, учитывайте состояние журнала выполнения.

Метод `executeSets` после записи ошибки передает исключение вызывающему коду.

### Значения `null` в параметрах источника

Не передавайте `null` как значение параметра отбора источника. Текущее условие отбора сравнивает колонку с `null` через оператор равенства, поэтому строка не будет выбрана.

## Рекомендации по настройке

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