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

# Особенности работы в системе Global

Система Global предоставляет расширенные возможности для работы с данными через JEXL-скрипты. В статье описаны основные методы работы с переменными, выборками, данными и внешними сервисами.

## Наименование переменных для nullable типов и атрибутов

Правила именования переменных: `[Variable][a][t][Scope][Name][Suffix]`.

Компоненты именования:

- [Variable] — определяет тип данных:

  - n — число;

  - s — строка;

  - j — строка в формате JSON;

  - d — дата;

  - r, x — запись;

  - u, cur — курсор;

  - l, blob — бинарные данные;

  - с — символьные данные;

  - b — булево значение;

  - id — идентификатор;

  - gid — глобальный идентификатор.

- [a] — определяет, является ли переменная последовательностью;

- [t] — определяет пользовательский тип;

- [Scope] — область действия:

  - v — переменная процедуры;

  - p — параметр процедуры;

- [Name] — имя переменной;

- [Suffix] — суффикс:

  - _dz — системные атрибуты;

  - _z — проектные атрибуты.

Примеры именования параметров:

- dStart — дата начала в таблице;

- dvStart — дата начала переменной процедуры;

- dgMaxDate — максимальная дата переменной пакета;

- tvdaDate — тип коллекции дат в процедуре;

- davDate — коллекция дат в процедуре;

- uvStudents — курсор в процедуре.

## Основные методы и принципы работы

### Поиск методов класса

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

1. Перейдите в Настройки системы → Обозреватель проектов.

2. Введите имя класса (например, `Stm_OrderOut`).

3. Откройте файл с суффиксом `Api` (например, `Stm_OrderOut_Api`).

4. Перейдите на вкладку Методы.

В этом разделе отображаются все доступные методы класса. Большинство классов содержат стандартный набор методов для работы.

### Работа с текущей выборкой

При запуске JEXL-скрипта часто происходит работа внутри существующей выборки (например, при выделении строки в таблице).

С помощью `selection.getVar` можно получить атрибуты текущей выборки:

    var id = selection.getVar("атрибут");
    var idvOrder = selection.getVar("id"); // "id" — название атрибута

### Загрузка строки через rop

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

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

1. Сначала проверяет наличие данных в кэше.

2. Если данных нет — загружает их из базы.

3. Предоставляет удобный доступ к полям строки.

Получить строку можно с помощью метода load. Полученное значение будет типа rop:

    ropOrder = Stm_OrderOutApi.load(idvOrder);

### Доступ к атрибутам строки

Обычные атрибуты (колонки таблицы) доступны напрямую через точку:

    var svNumDoc = ropOrder.sNumDoc; // Номер документа
    var idvClient = ropOrder.idClient; // ID клиента

Работа с JSON-атрибутами на примере Stm_OrderOut

Получение значений атрибутов:

1. Получение всего JSON объекта:

    var jvParams = ropOrder.jParams_dz; // Получаем полный JSON-объект параметров

2. Получение конкретного значения по имени атрибута:

    var svStation = Stm_OrderOutApi.getObjAttrValue(ropOrder, 'station'); // Получаем значение атрибута 'station'

3. Получение значения по ID атрибута:

    // Получаем ID класса из текущей выборки
    var idvClass = selection.getVar('idClass'); // Получим id класса
    // Находим ID атрибута по мнемокоду 'station' для указанного класса
    var idvAttrStation = Btk_AttributeApi.findByMnemoCode(idvClass, 'station');
    // Получаем значение атрибута для указанного заказа
    var svStationById = Stm_OrderOutApi.getObjAttrValue(ropOrder, idvAttrStation);

Установка значений атрибутов:

1. Установка по имени атрибута — передать название класса и название атрибута:

    Stm_OrderOutApi.setObjAttrValue(ropOrder, 'station', "Новая станция");

2. Установка по ID атрибута:

    // Получаем ID класса из текущей выборки
    var idvClass = selection.getVar('idClass');
    // Находим ID атрибута по мнемокоду 'station' для указанного класса
    var idvAttrStation = Btk_AttributeApi.findByMnemoCode(idvClass, 'station');
    // Получаем значение атрибута для указанного заказа
    var svStationById = Stm_OrderOutApi.getObjAttrValue(ropOrder, svStationById);
    Stm_OrderOutApi.setObjAttrValue(ropOrder, svStationById, "Новое значение"); // Установка значения для атрибута с указанным ID

### Изменение значений

Чтобы изменить значение в строке, используйте специальные методы-сеттеры. У каждого класса свои сеттеры (смотрите в методах API):

    // Меняем номер документа
    Stm_OrderOutApi.setsNumDoc(ropOrder, "НОВЫЙ-001");

    // Меняем клиента
    Stm_OrderOutApi.setidClient(ropOrder, 12345L);

### Поиск по мнемокоду

Часто нужно получить ID по коду (например, код способа доставки):

    // Получаем ID способа доставки "ЖД"
    var idvZHD = Bs_DeliveryMethodApi.findByMnemoCode('ZHD');

Внимание

код чувствителен к регистру! „ZHD“ и „zhd“ — разные коды.

Пример полного скрипта:

    // 1. Получаем ID из текущей выборки
    var idvOrder = selection.getVar("id");

    // 2. Загружаем строку заказа
    var ropOrder = Stm_OrderOutApi.load(idvOrder);

    // 3. Получаем атрибуты
    var svNumDoc = ropOrder.sNumDoc; // Номер
    var idvStationEndJson = Stm_OrderOutApi.getObjAttrValue(ropOrder, 'idStationEndJson');

    // 4. Меняем номер
    Stm_OrderOutApi.setsNumDoc(ropOrder, "НОВЫЙ-002");

    // 5. Ищем ID способа доставки
    var idvDelivery = Bs_DeliveryMethodApi.findByMnemoCode('ZHD');

## Преобразование типов данных

Метод | Пример использования
--- | ---
`.asJLong()` | `"123".asJLong()`
`.toLong()` | `"456".toLong()`
`.toInteger()` | `"789".toInteger()`
`.toDouble()` | `"12.34".toDouble()`
`.toBigDecimal()` | `"123.45".toBigDecimal()`
`.asDate()` | `"2024-01-01".asDate()`
`.toDate("формат")` | `toDate("01.01.24", "dd.MM.yy")`
`.toString()` | `123.toString()`
`.asString()` | `321.asString()`
`.asList()` | `sql("SELECT...").asList()`
`.asSingle()` | `sql("SELECT...").asSingle()`
`.asScala()` | `[1,2,3].asScala()`
`.asJava()` | `scalaSeq.asJava()`
`.toJObject()` | `toJObject('{"id":1}')`

## Работа с датами

Метод | Описание | Пример
--- | --- | ---
`getFirstDayOfMonth` | Возвращает дату начала месяца от текущей даты | `var a = getFirstDayOfMonth();` `var b = getFirstDayOfMonth('27.01.2025 12:42:10');`
`getFirstDayOfPeriod` | Возвращает первый день указанного периода | `var a = getFirstDayOfPeriod(sysDate(), 'M');` `var b = getFirstDayOfPeriod('27.01.2025 12:42:10', 'Q');`
`getLastDayOfMonth` | Возвращает дату конца месяца от текущей даты | `var a = getLastDayOfMonth();` `var b = getLastDayOfMonth('27.01.2025 12:42:10');`
`getLastDayOfPeriod` | Возвращает последний день указанного периода | `var a = getLastDayOfPeriod(sysDate(), 'M');` `var b = getLastDayOfPeriod('27.01.2025 12:42:10', 'Q');`
`nanoTime` | Получить значение System.nanoTime() | `var a = nanoTime();`
`sysDate` | Возвращает текущую дату | `var a = sysDate();`
`toDate` | Переводит строку в дату по указанному формату | `var a = toDate("27.02.2020 12:42:10");` `var b = toDate("27.02.2020 12:42:10", "dd.MM.yyyy HH:mm:ss");`
`truncDate` | Получение даты на начало дня от переданной | `var a = truncDate(sysDate());`
`truncYear` | Получение даты на начало года от переданной | `var a = truncYear(sysDate());`
`-` | Вычитает из даты указанное количество дней | `var dYesterday = sysDate() - 1;`
`+` | Добавляет к дате указанное количество дней | `var dTomorrow = sysDate() + 1;`

## Работа в контексте выборки

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

Метод | Описание | Пример использования
--- | --- | ---
`varExists(name)` | Проверяет существование переменной в текущей и мастер-выборках | `if (varExists("client_id")) { ... }`
`selfVarExists(name)` | Проверяет существование переменной только в текущей выборке | `if (selection.selfVarExists("temp_value")) { ... }`
`getVar(name)` | Получает значение переменной из текущей или мастер-выборки | `var id = selection.getVar("doc_id").asLong();`
`getSelfVar(name)` | Получает значение только из текущей выборки | `var code = selection.getSelfVar("product_code");`
`setVar(name, value)` | Устанавливает значение переменной (ищет в иерархии выборок) | `selection.setVar("status", "approved");`
`setSelfVar(name, value)` | Устанавливает значение только в текущей выборке | `selection.setSelfVar("comment", "Не заполнено");`
`addVar(name, value)` | Добавляет новую переменную в текущую выборку | `selection.addVar("processed_at", sysDate());`
`newForm()` | Создает новую форму | `Act_DocEdit.newForm().params(...).open();`
`.form()` | Возвращает форму текущего контекста | `selection.form();`
`.opers('Операция')` | Возвращает указанную операцию для дальнейшего выполнения | `selection.opers("CREATESTMORDERIN");`
`.execute()` | Выполняет ранее полученную операцию | `selection.opers("CREATESTMORDERIN").execute();`

### Работа с формой выборки

Создание и настройка формы:

    var res = Btk_ClassAvi // Класс, предоставляющий форму выбора
    .list() // Инициализация формы списка (табличное отображение)
    .newForm() // Создание новой формы
    .params({ // Передача параметров в форму в виде мапы
        "one": 1,
        "two": 2,
        "three": 3,
        "more": "many more"
    })
    .locates({"id": 146800}) // Выбор строки
    .results(["id", "idClass"]) // Какие поля вернуть после выбора
    .openLookup(); // Открытие окна

Метод | Описание
--- | ---
`.list()` | Форма будет отображаться как таблица (список записей). Возможные отображения можно посмотреть в обозревателе проекта в классе с расширением Avi
`.newForm()` | Создает новую формы
`.params()` | Передает параметры в форму (могут использоваться для фильтрации/логики)
`.locates()` | Установка строки на активную
`.results()` | Задает поля, которые вернутся после выбора
`.openLookup()` | Открывает окно выбора
`.findSelection('Отображение')` | Ищет и возвращает выборку, содержащую указанное отображение
`.baseRep()` | Возвращает ссылку на объект, к которому был вызван метод

Пример: Поиск и выполнение операций в связанной выборке

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

    var sel = selection.form() // получает форму по выделенному элементу
    .findSelection(Stm_OrderOutAvi.Card() // Ищет и возвращает **выборку**, содержащую указанное отображение
    .baseRep()); // возвращает базовое представление объекта (ссылку)

    if (sel != null) { // если выборка найдена, то выполняет действие
        sel.opers("CREATESTMORDERIN").execute(); // выполняет переданную операцию
    }

Для работы с текущей выборкой можно использовать сокращенный синтаксис:

    selection.opers("CREATESTMORDERIN").execute();

Обработка результата:

    if (res.getLookupResult() == LookupResult.ok()) {
        // Пользователь выбрал значение и нажал "OK"
        for(var i: 1 .. res.size()) { // Цикл по всем выбранным записям
            dialogs.showMessage("id " + res.getData(i, 0) + " idClass " + res.getData(i, 1));
        }
    } else {
        // Пользователь закрыл форму или нажал "Отмена"
        dialogs.showMessage("Значение не было выбрано");
    }

- res.getLookupResult() — проверяет, как закрыли форму:

  - LookupResult.ok() — нажали «Выбрать»;

  - LookupResult.cancel() — нажали «Отмена» или закрыли окно.

- res.size() — количество выбранных записей (если форма поддерживает множественный выбор).

- res.getData(i, 0) — доступ к данным:

  - i — номер строки (начинается с 1);

  - 0 — индекс поля (соответствует порядку в .results([«id», «idClass»])).

Пример работы с мастер-выборками:

    // Получаем значение из атрибута "id" выборки, приводим его к типу NString и пишем в переменную idTree
    var idTree = getVar("id").asNString();

    // Получаем атрибут DGLOBALENDDATE из мастер-выборки
    var dEndDate = getVar("super$DGLOBALENDDATE").asNDate();

    // Вызываем метод из пакета с передачей параметров
    // Обратите внимание, для обращения к Api или пакетам используются их короткие имена (без скобок)
    var fltCond = Act_UniversalReportPkg.getUniFilterCondByIdTree(idTree, dEndDate);

    // Вызывается ещё один метод, возвращающий объект scala-класса immutable.Map[NString, Any]
    var filters = Act_UniversalReportPkg.getFilterValues(idTree);

    /* Определение Map-ы внутри jexl-скрипта. Отметим, что Map внутри jexl и объект scala-класса
    Map (неважно mutable или immutable) - это разные объекты. Наиболее важным
    фактом является то, что передать scala-объект Map, полученный в предыдущей
    строке,
    в пакетный метод, принимающий scala-объект Map, в jexl-скрипте напрямую нельзя,
    именно поэтому приходится получать значения из переменной filters,
    перезаписывать
    их в jexl-Map param и потом передавать param в scala-метод. */

    var param = {
        "flt_idDepOwner" : filters['flt_idDepOwner'],
        "flt_idAcc" : filters['flt_idAcc'],
        "flt_dFrom" : filters['flt_dFrom'],
        "flt_dTo" : filters['flt_dTo'],
        "flt_idAdjustMethod" : filters['flt_idAdjustMethod'],
        "flt_idAccCor" : filters['flt_idAccCor'],
        "uniFilterCondition_dz": fltCond
    };

    // Открываем новую форму с переданными параметрами
    Act_TransAvi.defList().newForm().params(param).open();

Мастер-выборки:

- Доступ через префикс `super$`.

- Пример: `getVar("super$PARENT_ID")`.

Типизация:

- Используйте `.asString()`, `.asDate()` для явного преобразования.

- Для чисел: `.asBigDecimal()`, `.asJLong()`.

### Дополнительные методы работы с формой выборки

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

    Btk_WizardLib().launch(getSelfVar("id").asNLong); // В качестве параметра передается id формы.

## Работа с SQL

Для работы с SQL используются команды:

1. Для запросов на чтение:

    sql(sqlText:String)

2. Для запросов на запись:

    tsql(sqlText:String).execute()

Методы обработки для sql:

Метод | Описание | Пример
--- | --- | ---
`.asList()` | Вернуть список записей | `sql("SELECT * FROM users").asList()`
`.asSingle()` | Вернуть одну запись | `sql("SELECT * FROM users WHERE id=1").asSingle()`

Методы обработки для tSql:

Метод | Описание | Пример
--- | --- | ---
`.execute()` | Выполнить запрос | `tsql("DELETE FROM temp").execute()`

Примеры:

Простые запросы:

    // Чтение
    var activeUsers = sql("SELECT name FROM users WHERE is_active=true").asList();

    // Запись
    tsql("UPDATE orders SET status='done' WHERE id=42").execute();

Обработка результатов:

    // Вывод имен пользователей
    sql("SELECT name FROM users").foreach(row -> {
        logInfo("User: " + row.name);
    });

    // Подсчет суммы
    var total = 0;
    sql("SELECT amount FROM payments").foreach(p -> {
        total = total + p.amount;
    });

Параметризация:

    var productId = selection.getVar('id');
    var product = sql("SELECT * FROM products WHERE id=${productId}").asSingle();

    var productId = selection.getVar('id');
    var sSqlText = ''; // объявляем переменную, в которой будем собирать sql-выражение
    sSqlText = "SELECT * FROM products WHERE id="; //
    sSqlText += toString(productId);
    sSqlText += "\r\n order by id";
    var product = sql(sSqlText).asSingle();

    var productId = selection.getVar('id');
    var product = sql(`
        SELECT *
        FROM products
        WHERE id=${productId}`)
    .asSingle();

## Дополнительные методы

Метод | Описание | Пример использования
--- | --- | ---
`lpad(str, len, ch)` | Дополнение строки слева | `lpad("5", 3, "0") → "005"`
`rpad(str, len, ch)` | Дополнение строки справа | `rpad("5", 3, "0") → "500"`
`flush()` | Синхронизация с БД | `flush()`
`commit()` | Подтверждение транзакции | `commit()`
`nvl(a, b)` | Возвращает a, если не null, иначе b | `nvl(var, "default")`
`isNull(x)` | Проверка на null | `isNull(obj)`
`isNotNull(x)` | Проверяет, что переданное значение не null | `isNotNull(obj)`
`pgArrayToNLongList` | Конвертация массива в список | `pgArrayToNLongList(arr)`
`parseId(gid: Object): Long` | Получение идентификатора объекта из NGid-а Работает для строкового или NGid-значения | `var sGid = "14323/44334"; var id = parseId(sGid);`
`parseIdClass(gid: Object): Long` | Получение идентификатора класса из NGid-а Работает для строкового или NGid-значения | `var sGid = "14323/44334"; var idClass = parseIdClass(sGid);`
`toJRops(rops: Traversable[Rop]): JexlToJRops` | Обход записей, возвращенных объектным запросом, например byParent Выполняет запрос на чтение к базе данных и позволяет обойти записи | `var l = toJRops(Btk_SomeEntityApi.byParent(rop)).asList(); for(r:l){ logInfo(r.id); logInfo(r.sSystemName); }`
`toJObject(json: String): JexlToJObject` | Парсинг json-объекта | `var jData = toJObject('{"id": 1}'); logInfo(jData.getLong("id"));`

## Работа с BTS процедурами

Переход в раздел процедур:

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

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

Написание JEXL-кода:

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

    var nvParam1 = npInParam1;
    var nvParam2 = npInParam2;
    var nvParam3 = npInParam3;
    var nvSum = (npInParam1 + npInParam2) * npInParam3;

    // Для того чтобы процедура вернула значение, добавим возврат результата:
    return nvSum;

Сохраните код.

Вызов процедуры через JEXL-редактор:

Перейдите в отдельный редактор JEXL. Объявите служебную функцию для типизации данных. Данная функция будет возвращать тип данных scala.Tuple2, состоящий из двух переменных.

    var tuple2 = (v1, v2) -> { return new(`scala.Tuple2`, v1, v2); };

Заведите массив параметров, в котором запишите входящие значения. В нем будет объявлен массив Scala (это делается за счет указания последнего элемента с троеточием). Первые три элемента будут объявлены с помощью функции tuple2. В каждом таком элементе массива будет указано название параметра и его значение.

    var tuple2Params = asScala([
        tuple2("npInParam1", 10l),
        tuple2("npInParam2", 125l),
        tuple2("npInParam3", 1000l),
        ...
    ]);

Объявите переменную, в которую при помощи API-функции findByMnemoCode поместите идентификатор ранее созданной процедуры:

    val idvBtsProcedure = Bts_ProcedureApi.findByMnemoCode(`Sale_Test1`);

Далее вызовите конструкцию для расчета значения BTS-процедуры и запишите результат в переменную. Для этого используйте API-метод Bts_ProcedureLib, передав в него:

- идентификатор процедуры;

- массив параметров;

- дополнительный контекст выборки для расчета.

    var nvSumCalc = Bts_ProcedureLib.execById(idvBtsProcedure, tuple2Params, selection.coreRep());

Метод `Bts_ProcedureLib.execById` ожидает параметры в формате, который понимает Scala, поэтому мы используем кортеж. Кортежи здесь — это «мост» между JEXL и Scala, обеспечивающий четкую, безопасную и удобную передачу параметров.

Верните результат процедуры и выведите его на экран.

    dialogs.showMessage(toString(nvSumCalc));

Аналогичным образом можно создавать и более сложные BTS-процедуры.

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

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

Настройки системы → Сервис → Инструменты → Мониторинг сессий сервера приложений.

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

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

Получение текущего действия:

    session.applicationInfo().action();

Обновление информации о действии:

    session.applicationInfo().action_$eq("Значение текущего действия");

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

    for (ropWW: ropaIntWar) {
        session.applicationInfo().action_$eq(```Phosagro_SB0205_NEW - ВП - ${toString(ropWW.gid)}```);
        Stk_InternalWarrantApi.setdExecDateOut(ropWW, cDate);
        session.applicationInfo().action_$eq(```Phosagro_SB0205_NEW - выполняем ВП - ${toString(ropWW.gid)}```);
        Stk_InternalWarrantApi.setidStateOut(ropWW, idvStateIntWar);
        session.applicationInfo().action_$eq(```Phosagro_SB0205_NEW - Создаем РСО```);
        var ropWarOut = Stk_WarrantOutApi.createWarrantOutByInternalWarrant(ropWW);
        // Заполняем даты исполнения
        Stk_WarrantOutApi.setdDocDate(ropWarOut, cDate);
        Stk_WarrantOutApi.setdExecDate(ropWarOut, cDate);
        // Выполняем РСО
        session.applicationInfo().action_$eq(```Phosagro_SB0205_NEW - выполняем ВП - ${toString(ropWarOut.gid)}```);
        Stk_WarrantOutApi.setidState(ropWarOut, idvStateWarr);
    };

Метод setUniCharValue используется для установки значения универсальной характеристики (универсального атрибута) объекта в системе.

    setUniCharValue(rop: ApiRop, idpUniChar: Long, pValue: Any): Unit

Параметр | Тип (Scala) | Описание
--- | --- | ---
rop | Rop | Контекст выполнения операции (объект, к которому применяется изменение)
idpUniChar | Long | Идентификатор универсальной характеристики
pValue | Any | Новое значение характеристики (может быть Long, String и др)

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

    // 1. Получаем GID значения "." из справочника
    val gidChr = sql("""
        SELECT r.gid
        FROM Btk_UniversalReference r
        JOIN btk_objecttype b ON r.idobjecttype = b.id
        WHERE r.sSystemname = '.'
        AND b.scode = 'ur_O2C_sort_type'
    """).asSingle();

    Stm_OrderOutApi.setUniCharValue(rop, 'O2C_sort_type', gidChr.gid);

## Работа с печатью

Пользователь может:

- Сформировать отчёт по его системному имени.

- Запустить JEXL-скрипт печатной формы, привязанной к типу объекта.

Создание отчета происходит с помощью следующего метода:

    Rpt_Lib.createReportExJexl(
        reportName: String,
        reportVersionDate: Date,
        postBuildAction: PostBuildAction,
        propertyMap: Map[String, Any],
        idpObjectTypePrintForm: NLong = None.nl
    ): Unit

Параметр | Тип (Scala) | Описание
--- | --- | ---
idpObjectTypePrintForm | Long | id ПФ на типе объекта. Может быть null
reportName | String | Имя отчета
reportVersionDate | Date | Дата
postBuildAction | PostBuildAction | Действие, которое необходимо произвести после заполнения отчёта
propertyMap | Map[String, Any] | Карта входящих параметров

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

    var idvOrderClass = selection.getVar("idClass");
    var idvOrder = selection.getVar("id");

    var NLong = function(number) { // локальная функция для преобразования Long в NLong
        return new ("ru.bitec.app.gtk.lang.NLong", number);
    };

    var spReportName = "Stm_InvoiceOut";
    var dpReportVersionDate = sysDate();
    var vPostBuildAction = session
        .sbtClassLoader()
        .loadClass('ru.bitec.app.gtk.gl.postbuildaction.PostBuildAction')
        .design();

    var propertyMap = {
        "idSrcObject" : NLong(idvOrder),
        "idSrcClass" : NLong(idvOrderClass)
    };

    var idpObjectTypePrintForm = null;

    Rpt_Lib.createReportExJexl(
        spReportName,
        dpReportVersionDate,
        vPostBuildAction,
        asScala(propertyMap),
        idpObjectTypePrintForm
    );

Тип PostBuildAction:

PostBuildAction — это перечисление (enum) из класса ru.bitec.app.gtk.gl.postbuildaction.PostBuildAction, определяющее действия после построения отчета.

Доступные значения:

- `.show()` — загружает отчет на клиент;

- `.save()` — сохраняет отчет;

- `.print()` — загружает отчет на клиент и, если в конфигурации системы включена опция печати (Configuration.Printing.enableNetworkPrinting), печатает отчет на локальном (для сервера) принтере.

Примечание: Для `.print()` поведение зависит от конфигурации сервера. Если печать не настроена, отчет просто загружается на клиент.

Особенности:

- Для работы с ID используется тип NLong (внутренний класс системы).

- asScala() преобразует Java-коллекции в Scala-совместимые.

- Если idpObjectTypePrintForm = null — отчёт строится по reportName.