Особенности работы в системе 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');
Преобразование типов данных#
Метод |
Пример использования |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Работа с датами#
Метод |
Описание |
Пример |
|---|---|---|
|
Возвращает дату начала месяца от текущей даты |
|
|
Возвращает первый день указанного периода |
|
|
Возвращает дату конца месяца от текущей даты |
|
|
Возвращает последний день указанного периода |
|
|
Получить значение 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();
Дополнительные методы#
Метод |
Описание |
Пример использования |
|---|---|---|
|
Дополнение строки слева |
|
|
Дополнение строки справа |
|
|
Синхронизация с БД |
|
|
Подтверждение транзакции |
|
|
Возвращает a, если не null, иначе b |
|
|
Проверка на null |
|
|
Проверяет, что переданное значение не null |
|
|
Конвертация массива в список |
|
|
Получение идентификатора объекта из NGid-а Работает для строкового или NGid-значения |
|
|
Получение идентификатора класса из NGid-а Работает для строкового или NGid-значения |
|
|
Обход записей, возвращенных объектным запросом, например byParent Выполняет запрос на чтение к базе данных и позволяет обойти записи |
|
|
Парсинг json-объекта |
|
Работа с 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.