Перейти к основному содержанию
`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');

### Доступ к универсальным характеристикам

Помимо объектных характеристик, к классам могут быть подключены универсальные характеристики (UC). Доступ к ним осуществляется через `ClassApi`:

- `ClassApi.getUniCharValue(rop, "CharName")` — получение по системному имени.

- `ClassApi.getUniCharValue(rop, idAttr)` — получение по ID атрибута.

Примечание

Значения UC также могут быть `null`. Перед использованием применяйте проверку `isNotNull()` или `.nvl()`.

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

Метод | Пример использования
--- | ---
`.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, FieldType)` | Добавляет новую переменную в текущую выборку | `selection.addVar("id", 1, FieldType.ftFloat());`
`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();

### Особенности работы с JexlSqlRow

Результат `sql().asList()` возвращает список объектов типа `JexlSqlRow`.

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

- `row.fieldName` — по имени колонки из `SELECT`;

- `row.getValue("fieldName")` — безопасный доступ (рекомендуется при динамических именах);

- `row.containsKey("fieldName")` — проверка наличия поля перед обращением;

Примечание

Поля в `JexlSqlRow` формируются строго из запроса. Обращение к несуществующему полю или к полю со значением `NULL` через точечную нотацию вызовет ошибку выполнения скрипта.

### Поведение NULL при интерполяции

JEXL-интерполяция `${variable}` преобразует значение `null` в пустую строку `""`, а не в SQL-`NULL`. Выражение `WHERE id=${nullField}` сгенерирует `WHERE id=`, что вызовет синтаксическую ошибку PostgreSQL.

Перед подстановкой в SQL всегда проверяйте значение на `null`:

    if (row.fieldName.isNotNull) {
    sql("SELECT * FROM table WHERE col=${row.fieldName}").asList()

Либо используйте параметризованный запрос через `on(Symbol("param") -> value)`.

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

    | Метод | Описание | Пример использования |
    |-------|----------|---------------------|
    | `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.