Особенности работы в системе 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 — курсор в процедуре.
Основные методы и принципы работы#
Поиск методов класса#
Для просмотра доступных методов любого класса выполните следующие шаги:
Перейдите в Настройки системы → Обозреватель проектов.
Введите имя класса (например,
Stm_OrderOut).Откройте файл с суффиксом
Api(например,Stm_OrderOut_Api).Перейдите на вкладку Методы.
В этом разделе отображаются все доступные методы класса. Большинство классов содержат стандартный набор методов для работы.
Работа с текущей выборкой#
При запуске JEXL-скрипта часто происходит работа внутри существующей выборки (например, при выделении строки в таблице).
С помощью selection.getVar можно получить атрибуты текущей выборки:
var id = selection.getVar("атрибут");
var idvOrder = selection.getVar("id"); // "id" — название атрибута
Загрузка строки через rop#
rop — специальный тип данных для переменных, соответствующий строке объекта в таблице базы данных. Внутри данной строки можно обратиться к любому физическому столбцу. По сути это проводник к строке в базе данных.
Принцип работы:
Сначала проверяет наличие данных в кэше.
Если данных нет — загружает их из базы.
Предоставляет удобный доступ к полям строки.
Получить строку можно с помощью метода load. Полученное значение будет типа rop:
ropOrder = Stm_OrderOutApi.load(idvOrder);
Доступ к атрибутам строки#
Обычные атрибуты (колонки таблицы) доступны напрямую через точку:
var svNumDoc = ropOrder.sNumDoc; // Номер документа
var idvClient = ropOrder.idClient; // ID клиента
Работа с JSON-атрибутами на примере Stm_OrderOut
Получение значений атрибутов:
Получение всего JSON объекта:
var jvParams = ropOrder.jParams_dz; // Получаем полный JSON-объект параметров
Получение конкретного значения по имени атрибута:
var svStation = Stm_OrderOutApi.getObjAttrValue(ropOrder, 'station'); // Получаем значение атрибута 'station'
Получение значения по ID атрибута:
// Получаем ID класса из текущей выборки
var idvClass = selection.getVar('idClass'); // Получим id класса
// Находим ID атрибута по мнемокоду 'station' для указанного класса
var idvAttrStation = Btk_AttributeApi.findByMnemoCode(idvClass, 'station');
// Получаем значение атрибута для указанного заказа
var svStationById = Stm_OrderOutApi.getObjAttrValue(ropOrder, idvAttrStation);
Установка значений атрибутов:
Установка по имени атрибута — передать название класса и название атрибута:
Stm_OrderOutApi.setObjAttrValue(ropOrder, 'station', "Новая станция");
Установка по 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().
Преобразование типов данных#
Метод |
Пример использования |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Работа с датами#
Метод |
Описание |
Пример |
|---|---|---|
|
Возвращает дату начала месяца от текущей даты |
|
|
Возвращает первый день указанного периода |
|
|
Возвращает дату конца месяца от текущей даты |
|
|
Возвращает последний день указанного периода |
|
|
Получить значение System.nanoTime() |
|
|
Возвращает текущую дату |
|
|
Переводит строку в дату по указанному формату |
|
|
Получение даты на начало дня от переданной |
|
|
Получение даты на начало года от переданной |
|
|
Вычитает из даты указанное количество дней |
|
|
Добавляет к дате указанное количество дней |
|
Работа в контексте выборки#
Контекст выборки (JexlSelScript) расширяет контекст бизнес-логики возможностью работы с выборками и пользовательским интерфейсом. Используется в тех случаях, когда необходимо получить данные из полей для пользовательского ввода.
Метод |
Описание |
Пример использования |
|---|---|---|
|
Проверяет существование переменной в текущей и мастер-выборках |
|
|
Проверяет существование переменной только в текущей выборке |
|
|
Получает значение переменной из текущей или мастер-выборки |
|
|
Получает значение только из текущей выборки |
|
|
Устанавливает значение переменной (ищет в иерархии выборок) |
|
|
Устанавливает значение только в текущей выборке |
|
|
Добавляет новую переменную в текущую выборку |
|
|
Создает новую форму |
|
|
Возвращает форму текущего контекста |
|
|
Возвращает указанную операцию для дальнейшего выполнения |
|
|
Выполняет ранее полученную операцию |
|
Работа с формой выборки#
Создание и настройка формы:
var res = Btk_ClassAvi // Класс, предоставляющий форму выбора
.list() // Инициализация формы списка (табличное отображение)
.newForm() // Создание новой формы
.params({ // Передача параметров в форму в виде мапы
"one": 1,
"two": 2,
"three": 3,
"more": "many more"
})
.locates({"id": 146800}) // Выбор строки
.results(["id", "idClass"]) // Какие поля вернуть после выбора
.openLookup(); // Открытие окна
Метод |
Описание |
|---|---|
|
Форма будет отображаться как таблица (список записей). Возможные отображения можно посмотреть в обозревателе проекта в классе с расширением Avi |
|
Создает новую формы |
|
Передает параметры в форму (могут использоваться для фильтрации/логики) |
|
Установка строки на активную |
|
Задает поля, которые вернутся после выбора |
|
Открывает окно выбора |
|
Ищет и возвращает выборку, содержащую указанное отображение |
|
Возвращает ссылку на объект, к которому был вызван метод |
Пример: Поиск и выполнение операций в связанной выборке
Демонстрирует, как найти выборку, связанную с конкретным документом, и выполнить в ней операцию.
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 используются команды:
Для запросов на чтение:
sql(sqlText:String)
Для запросов на запись:
tsql(sqlText:String).execute()
Методы обработки для sql:
Метод |
Описание |
Пример |
|---|---|---|
|
Вернуть список записей |
|
|
Вернуть одну запись |
|
Методы обработки для tSql:
Метод |
Описание |
Пример |
|---|---|---|
|
Выполнить запрос |
|
Примеры:
Простые запросы:
// Чтение
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.