Výrazy a skripty se píší v jazyce JEXL. Pro úplný a přesný popis jazyka referujte na oficiální stránku https://commons.apache.org/proper/commons-jexl/reference/syntax.html. Níže se nachází i náš popis základních funkcionalit tohoto jayzka (neplést s "Specifické funkce"), ve kterém se ale mohou nacházet chyby.
var a = 2;
#
if (true) { } else if (false) { } else { }
(vč. vnořených ifů)for (var item : list) { }
, for (i : 1..42) a = a + b[i]
(vč. vnořených forů)`Text ${entity.id}`
(lze vkládat proměnné přímo do textu, podporuje i více řádků)"DOMICILE"
-> Address.Type.DOMICILE
?[#this.length()>5]
, musíte použít Stream API nebo jinou logiku (např. for + if a uložit do nové kolekce)findPersonByIdIfWorking
je nyní dostupná pod hr.findPersonByIdIfWorking
formatCzechDate
je nyní dostupná pod formatDate
1L
, namísto 1
, jinak dostanete error (např. undefined property 'id'
)?:
je nyní ??
Aplikace nastavuje několik předdefinovaných proměnných pro interní použití:
processDefinitionId
processExecutionId
publicId
processFreeAttributes
sessionPerson // aktuálně přihlášený uživatel
a dále proměnné pro přístup a práci s API (viz. Specifické funkce níže).
Při psaní scriptu v definici procesu je dostupné proměnná context
, do které můžete ukládat údaje pro potřeby skriptu. Např.
context['quantity'] = 111; // obě notace přes "." nebo "[]" jsou podporovány
context.newBatch = createNew('Batch');
context.newBatch.goods = findEntityById('Goods',716);
context.newBatch.quantity = context.quantity;
V případě Fill Task, je ve scriptu dostupná proměnná request
, která obsahuje hodnoty z formuláře (stejné použití jako volné atributy). Např.
entity.firstname = capitalize(request.getValue('firstname'));
Při definici procesu nastavujete entitu, ke které se proces vztahuje a tato zvolená entita je pak ve výrazech dostupná jako proměnná entity. Ve výrazech se pak můžete odkazovat na vlastnosti a metody této entity.
Např. výrazem
entity.state = Absence.State.ACTIVE
nastavíte stav entity na hodnotu Aktivní
výrazem
entity.createdBy
se odkážete na objekt (jinou entitu) - v tomto případě člověka, který danou entitu vytvořil.
Můžete se také odkazovat vnořeně. Pokud tedy chcete zjistit příjmení člověka, který danou entitu upravil jako poslední, použijete výraz
entity.updatedBy.lastname
Můžete také zavolat specifickou funkci, kterou entita poskytuje. V případě entity člověka, se jedná například o funkci getLastnameFirstname(), nebo getFullName().
entity.updatedBy.getFullName()
vrátí hodnotu např. Ing. Jméno Příjmení MBA
Aplikace pro zjednodušení práce poskytuje pomocné funkce.
funkce | popis, příklad |
---|---|
now() |
vrací aktuální datum a čas |
today() |
vrací aktuální datum |
withLastDayOfMonth(LocalDate date) |
vrací zadaný datum upravený na poslední den v daném měsíci |
formatDate(LocalDate date) |
vrací zadaný datum ve formátu 'den.mesic.rok'formatDate(today()) |
formatDate(LocalDate date, String format) |
vrací zadaný datum dle zadaného formátu formatDate(today(), 'yyyy-MM-dd') |
formatDateTime(LocalDate date) |
vrací zadaný datum ve formátu 'dd.MM.yyyy HH:mm'formatDateTime(now()) |
formatDateTime(LocalDate date, String format) |
vrací zadaný datum dle zadaného formátu formatDate(today(), 'yyyy-MM-dd HH:mm:ss') |
parseDate(String date) |
vrací datum (LocalDate) dle zadaného řetězce |
parseTime(String time) |
vrací čas (LocalTime) dle zadaného řetězce |
parseDateTime(String dateTime) |
vrací datum a čas (LocalDateTime) dle zadaného řetězce |
formatNumber(double value) |
vrací zadané číslo formátované dle národních zvyklostí |
formatCurrency(double value) |
vrací zadané číslo formátované dle národních zvyklostí pro měnu (zaokrouhlení) |
listOf(...) |
vrací seznam ze zadaných hodnot |
sum(List<?> seznam) |
vrací součet hodnot seznamu např. sum(listOf(1,2,3)) vrátí 6 |
min(List<?> seznam) |
vrací nejmenší hodnotu v seznamu |
max(List<?> seznam) |
vrací největší hodnotu v seznamu |
filter(List<?> seznam, Closure filter) |
filtruje seznam pomoci zadané funkce např. filter(listOf(1,2,3,4,5), (item) -> { item > 2 }) vrátí list {3,4,5} |
select(List<?> seznam, Closure mapper) |
mapuje seznam objektů na jiné objekty dle dané funkce např. select(listOf(1,2,3), (item) -> { 'ID:' + item }) vrátí list {'ID:1', 'ID:2', 'ID:3'} |
filterSelect(List<?> seznam, Closure filter, Closure mapper) selectFilter(List<?> seznam, Closure mapper, Closure filter) |
analogicky k funkcím filter /select s tím, že se v jednom volání použijí oba postupy, jen v příslušném pořadí |
parseLong(String number) parseDouble(String number) |
vrací číselnou hodnotu dle zadaného řetězce, nebo null pokud nejde převést na číslo |
round2(double value) |
zaokrouhlí na dvě desetinná místa |
round(double value, int scale) |
zaokrouhlí na zadaný počet desetinných míst |
roundToInt(double value) |
zaokrouhlí na celé číslo |
isNotBlank(String parameter) |
vrací logickou hodnotu (true/false) pro test, zda je parametr prázdný |
defaultIfBlank(String parameter) |
vrací parameter, nebo prázdný řetězec, pokud je parameter null |
defaultIfBlank(String parameter, String default) |
vrací parameter, nebo zadaný výchozí řetězec, pokud je parameter null |
join(List<?> list) |
vrací řetězec tvořený spojením jednotlivých položek seznamu (oddělovač je čárka) |
join(List<?> list, String oddelovac) |
vrací řetězec tvořený spojením jednotlivých položek seznamu, se zadaným oddělovačem |
capitalize(String parameter) |
vrací zadaný řetězec s velkým prvním písmenem ('ahoj' -> 'Ahoj') |
getEnumLabel(String enumId, String code) |
vrací popisek zadaného enum dle jeho kódu ve výchozím jazyce |
getEnumLabel(String enumId, String code, int languageId) |
vrací popisek zadaného enum dle jeho kódu v jazyce podle zvoleného id |
findState(String nazevEntity, String kod) |
vrací stav (EntityState) pro zadaný název entity a kód stavu |
findCountryById(long countryId) |
vrací zemi (Country) pro zadané idfindCountryById(1).name |
findEntityById(String entityName, long id) |
vrací entitu typu entityName pro zadané id, pokud nenajde hodí chybufindEntityById('Company',1) |
findEntityByIdIfPresent(String entityName, long id) |
vrací entitu typu entityName pro zadané id, pokud nenajde vrací nullfindEntityById('Company',1) |
findEntityByIdIfPresent(@NotNull String entityName, String id) |
vrací entitu typu entityName pro zadané id typu String, pokud nenajde vrací nullfindEntityByIdIfPresent('Currency', 'CZK') |
findEntitiesByFieldIfPresent(String entityName, String fieldName, Object value) |
vrací entitu typu entityName . Zadanou hodnotu value hledá v políčku fieldName . Pokud nenajde, vrací prázdnou kolekcifindEntitiesByFieldIfPresent('Goods','code','SAMPLE-code') |
findEntitiesByFieldIfPresent(String entityName, String fieldName, Collection<?> value) |
vrací entitu typu entityName . Zadanou hodnotu value hledá v políčku fieldName . Pokud nenajde, vrací prázdnou kolekcifindEntitiesByFieldIfPresent('Goods','code',listOf('SAMPLE-code-1','SAMPLE-code-2')) |
findEntitiesByFieldsIfPresent(String entityName, Map<String, Object>) |
vrací entitu typu entityName . Hledá podle zadané mapy klíč (fieldName ) hodnota (value ). Jednotlivé položky v mapě se přádávají jako podmínky hledání spojené výrazem AND. Pokud nenajde, vrací prázdnou kolekcifindEntitiesByFieldsIfPresent('Goods', {'code':listOf('SAMPLE-code-1','SAMPLE-code-2'), 'parameterA': 10}) |
generatePdfDocument(long documentId, long entityId) |
vygeneruje a uloží PDF pro dané parametry |
createNew(String nazevEntity) |
vytvoří novou entitu dle zadaného jména |
persist(BaseModel entita) persistInAccountingUnit(BaseModel entita, AccountingUnit unit) |
vynutí uložení entity, nutné např. při změnách cen položek objednávky, aby proběhl správně přepočet hodnot, lze specifikovat v jaké účetní jednotce má uložení proběhnout |
persistSilently(BaseModel entita) persistSilentlyInAccountingUnit(BaseModel entita, AccountingUnit unit) |
vynutí uložení entity, např. při vytvoření nové, kdy následně potřebujeme získat její Id, lze specifikovat v jaké účetní jednotce má uložení proběhnout |
buildFreeAttributes(FreeAttributes source) buildFreeAttributes(Map<String, Object> source) |
vytvoří nový objekt volných atributů, pokud je source null, bude prázdný, jinak zkopíruje FA ze zdrojového objektu |
copyValues(FreeAttributes source, FreeAttributes target) |
kopíruje volné atributy ze zdrojového do cílového objektu |
copyMatchingFields(FreeAttributes source, BaseModel targetEntity) |
kopíruje hodnoty volných atributů (source) do políček cílového objektu (targetEntity) |
setFieldValue(List<BaseModel> list, String fieldName, Object value) |
u všech položek v seznamu nastaví určené pole na zadanou hodnotu |
setFieldValueByExpression(List<BaseModel> list, String expression) |
u všech položek v seznamu provede zadaný výraz položka je ve výrazu dostupná přes proměnnou item |
getDefaultValue(String entityName, String fieldName) |
vrací výchozí hodnotu políčka z nastavení entity |
getTableValueByKeyInColumn(Table table, int keyColumn, String keyValue, int columnKey) |
vrátí ze zadané tabulky hodnotu ve sloupci columnKey , řádek dohledá dle zadané hodnoty keyValue ve sloupci keyColumn .Používá se, pokud načtený CSV soubor obsahuje sloupec, který se dá použít pro vyhledání. Pro získání hodnoty čistě dle číselných indexů se použije interní funkce tabulky, např. context['tabulka'].get(řádek,sloupec) |
informUser(String message) informUser(String message, Long personId) warnUser(String message) warnUser(String message, Long personId) informUserError(String message) informUserError(String message, Long personId) informUserSuccess(String message) informUserSuccess(String message, Long personId) |
pošle informativní zprávu uživateli Pokud není specifikován, je poslána aktuálnímu přihlášenému uživateli který spustil proces. Typ zprávy (warn, error, suceess) určuje barvu zprávy (oranžová, červená, zelená) |
funkce | popis, příklad |
---|---|
accounting.uploadBankStatements(String format, List<String> fileUrls) accounting.uploadBankStatements(String format, List<String> fileUrls, Long accountingUnitId) |
nahraje a zprocesuje bankovní výpis (alternativa k UI funkci). Jako třetí parametr jde zadat účetní jednotku pro kterou se má metoda spustit. Užitečné v případě automatizovaného procesu. |
funkce | popis, příklad |
---|---|
czech.isMale(jmeno) |
test zda se jedná o mužské jméno |
czech.vokativ(jmeno) |
vrací 5. pád (oslovujeme/voláme) zadaného jména (Zdeněk -> Zdeňku) |
czech.vokativ(jmeno,isWoman,isLastName) |
vrací 5. pád zadaného jména, se specifikací zda se jedná o ženu a případně její příjmení |
funkce | popis, příklad |
---|---|
files.pathOf(String ...) |
vrací Path, složenou z více řetězců např. files.pathOf('/home/uzivatel','data','soubor.txt') vrátí cestu /home/uzivatel/data/soubor.txt |
files.compressFiles(String filename, List<Path> files) |
seznam souborů files zkomprimuje do ZIP souboru filename |
files.extractArchiveFile(Attachment priloha) |
vrací adresář (Path), kde je rozbalený obsah zadané přílohy |
files.deleteTempDirectory(Path tempDir) |
smaže zadanou cestu (musí být vytvořena předchozím voláním metody extractArchiveFile ) |
files.listFilesInDirectory(String cesta, boolean recursive) files.listFilesInDirectory(Path cesta, boolean recursive) |
vrátí seznam souboru v zadaném adresáři |
files.listDirsInDirectory(String cesta, boolean recursive) files.listDirsInDirectory(Path cesta, boolean recursive) |
vrátí seznam adresářů v zadaném adresáři |
files.uploadFile(Path path) files.uploadTemporaryFile(Path path) |
uloží zadaný soubor do storage složky, vrací UploadedFile. V případě dočasného souboru bude tento vymazán při příštím startu aplikace. |
files.addAttachmentsToEntity(BaseModel entity, List<Path> files) |
přidá zadaný seznam souborů k entitě jako přílohy (pokud je daná entita podporuje) Pozor! Udělá kopii souboru do storage složky aplikace. Pokud už zadané url ve storage složce je tak vytvoří duplicitu! |
files.createAttachment(String entityName, String name, String url) files.createAttachment(String entityName, Multilingual name, String url) |
vytvoří novou přílohu se zadaným jménem a url. Url se nekopíruje do storage složky, ale použije se tak jak je zadané. Přílohu je pak třeba přidat k přílohám cílové entity. např. entity.getAttachments().add(files.createAttachment('OpportunityAttachment', 'název', 'storage/cesta/k/priloze.txt')); Lze takto přidat přílohu, která odkazuje na externí službu (např. www odkaz) |
files.parseCSV(Path soubor) files.parseCSV(Path soubor, char separator) files.parseCSV(Path soubor,char separator, String charset) |
parsuje zadaný soubor a vrátí tabulku vytvořenou z dat v souboru výchozí oddělovač je čárka a znaková sada UTF-8 vrácená tabulka je pouze v paměti, řádky a sloupce jsou indexovány číselně (Integer, od 0) a hodnoty jsou řetězce (String) viz pomocná funkce getTableValueByKeyInColumn |
files.parseExcel(Path soubor, String sheetName) |
parsuje zadaný soubor a vrátí tabulku načtenou ze zadaného listu v dokumentu Excel vrácená tabulka je pouze v paměti, řádky a sloupce jsou indexovány číselně (Integer, od 0) a hodnoty jsou řetězce (String) viz pomocná funkce getTableValueByKeyInColumn |
files.saveCSV(Path soubor, String separator, String charset, List<Map<String, Object>> data) |
uloží data do CSV souboru |
files.saveText(Path soubor, String text) files.saveText(Path soubor, String text, String charset) |
zapíše obsah proměnné text so souboru. Lze určit znakovou sadu (výchozí je UTF-8) |
files.createTempFile(String prefix, String suffix) |
vytvoří pracovní soubor v dočasném adresáři (po restartu aplikace bude smazán). Vrací cestu k novému souboru. |
funkce | popis, příklad |
---|---|
hr.findPersonContract(person) |
vrací pracovní smlouvu pro zadanou osobuhr.findPersonContract(entity.createdBy) |
hr.findNextOrEqualWorkingDay(date) |
vrací zadaný datum pokud je pracovním dnem, případně další následující pracovní denhr.findNextOrEquealWorkingDay(today()) |
hr.findPersonByIdIfWorking(personId) |
vrací osobu, pokud pracuje (je v práci nebo na home office) |
funkce | popis, příklad |
---|---|
planning.createProjectFromTemplate(long templateId, String code, Multilingual name, LocalDate startAt) |
vytvoří a vrátí nový projekt dle zadané šablony |
funkce | popis, příklad |
---|---|
prod.releaseBatch(id) |
uvolní dávku do výroby (jako parametr očekává id dávky) |
prod.findSupply(goodsId,stockId,batchId) |
vrátí dostupnou zásobu pro výrobu |
prod.reloadAndFinishBatch(BatchEntity batch,List<UsedSupply>) |
dokončí výrobní dávku |
prod.findBatchesByRootBatchId(long batchId) |
najde skladové dávky vytvořené ze zadané výrobní dávky |
prod.findScheduledJobs(Long salesOrderId, Long goodsId, Long stageId) |
najde aktuálně naplánované úkoly dle parametrů jednotlivé parametry funkce můžou být null |
funkce | popis, příklad |
---|---|
queryService.queryString() queryService.queryString(String url) queryService.queryString(String url, String location) |
použití převážne pro šablony dokumentů, vrací kombinaci location + url v případě nezadání location, nebo varianta bez parametrů, vrací http://localhost:<port> např. queryService.queryString('storage/123/obrazek.png') vrátí 'http://localhost:8080/storage/123/obrazek.png' |
queryService.createQueryRequest() |
vytvoří dotazovací požadavek |
queryService.createColumnFilter() queryService.createColumnFilter(String operator, Object value) |
vytvoří filtr pro dotazovací požadavek |
queryService.createQueryExternalParameters() |
vytvoří objekt pro dotazovací požadavek |
queryService.remoteQuery(String url, String securityToken, QueryRequest request) queryService.remoteQuery(String url, String securityToken, String request) |
spustí vzdálený dotaz, vrací seznam příklad |
query(String sql) |
spustí SQL dotaz v lokálním prostředí, vrací List<Map<String, Object> (reprezentace výsledné tabulky) |
query(String entityName, QueryRequest request) |
spustí dotaz v lokálním prostředí, dle zadaných vyhledávacích parametrů. Vrací seznam entit, nebo prázdný list. |
funkce | popis, příklad |
---|---|
reportingService.loadReportData(long reportId) reportingService.loadReportData(String secretUid, String param) |
vrací data reportu ve formátu List<Map<String, Object>> |
reportingService.saveReportAsCSV(long reportId, Path filePath) |
uloží data reportu do zadaného souboru (viz. files.pathOf())) |
Pomocí streamů lze filtrovat, mapovat a třídit položky kolekce. Lze také transformovat kolekce na jiný typ. Stream api nahrazuje předchozí notaci .?[#item.length>5]
. Argumenty pro funkce filter
a map
musí být obaleny s with
/to
/as
. Viz příklady pro Stream API. Tyto funkce mají jeden argument typu lambda/funkce, které definují samotnou transformaci/filtrování (viz. JEXL dokumentace/Function definition). Pro řazení kolekce se používá funkce comparator
.
Lze také použít funkci, která volání streamu a rovnou zafiltruje kolekci filter(kolekce, funkce)
(bez with
), např. filter(entity.orders, (order) -> {order.state?.name() != 'NEW'})
funkce | popis, příklad |
---|---|
.with(funkce) |
with obaluje argument pro filtrování, .filter(with(funkce)) |
.to(funkce) , .as(funkce) |
to a as jsou stejné funkce s jiným názvem, používají se pro mapování prvků z jednoho typu na další, .map(to(funkce)) , (také použito pro Collectors.toMap ) |
filter(kolekce, funkce) |
zafiltruje danou kolekci podle funkce , která vrací boolean |
.comparator(funkce) |
seřadí kolekci pomocí zadané funkce. Porovnávací funkce musí vracet záporné číslo (první objekt je menší než druhý, 0 pokud jsou stejné, nebo kladné číslo, pokud je první objekt větší než druhý, viz: ukázka logiky: comparator( (item1, item2) -> { if (item1 < item2) { return -1; } else if (item1 > item2) { return 1; } else { return 0; } ) |
entity.type = findEntityById('GoodsType',8)
entity.receiveStock = findEntityById('Stock',4);
entity.buyingPriceLists.add(createNew('BuyingPriceList'));
entity.buyingPriceLists[entity.buyingPriceLists.size() - 1].setSupplier( findEntityById('Company',1) );
aby se nevytvářel nákupní ceník pro jednoho dodavatele vícekrát, lze ve skriptu přidat kontrolu zda existuje:
if (entity.buyingPriceLists?.stream()
.filter(with( (item) -> { item.supplier?.id == 1 } ))
.collectors(Collectors.toList())
.isEmpty()) {
var newPriceList = createNew('BuyingPriceList');
newPriceList.setSupplier(findEntityById('Company',1));
entity.buyingPriceLists.add(newPriceList);
}
Stream api také umožňuje zjednodušit předchozí podmínku, kde anyMatch
říká "pokud alespoň jeden prvek splňuje zadanou podmínku".
entity.buyingPriceLists?.stream().anyMatch(with( (item) -> { item.supplier?.id == 1 } ))
dříve bylo
if (entity.buyingPriceLists?.?[this.supplier?.id == 1].isEmpty());
entity.buyingPriceLists.add(createNew('BuyingPriceList'));
entity.buyingPriceLists[entity.buyingPriceLists.size() - 1].setSupplier( findEntityById('Company',1) );
endif;
Z volných atributů získá hodnotu dle klíče 'FA_KEY' a otestuje zda je obsažena v seznamu čísel. Vykřičník na začátku výrazu logickou hodnotu neguje. Výsledný výraz by se dal popsat slovy: Podmínka vyhoví, pokud hodnota volného atributu není jedna ze zadaných hodnot.
! listOf(17,27,55,13).contains( entity.freeAttributes.getLong('FA_KEY') )
Stejného výsledku by se dalo dosáhnout výrazně delším výrazem:
entity.freeAttributes.getLong('FA_KEY') != 17 &&
entity.freeAttributes.getLong('FA_KEY') != 27 &&
entity.freeAttributes.getLong('FA_KEY') != 55 &&
entity.freeAttributes.getLong('FA_KEY') != 13
Následující funkce vyhledá přímého nadřízeného, který má nastavenou Roli id 56. Pokud je jich více na stejné úrovni, vybere náhodně jednoho.
Tento příklad není dokonalý. Pokud by byla struktura rolí a nadřízených hodně členitá, není zaručeno že se vrátí úplně nejbližší nadřízený s danou rolí.
// definice rekurzivní funkce
var findSuperiorRecursive = function(testRole, people) {
if (people == null || people.size() == 0) {
return null;
}
// zkusí najít člověka s požadovanou rolí, a pokud najde ukončí funkci
for (var person: people) {
if (person.roles.contains(testRole)) {
return person;
}
}
// pokud nebyl člověk z požadovanou rolí nalezen, projde rekurzivně jejich nadřízené
for(var person: people) {
var found = findSuperiorRecursive(testRole, hr.findPeopleByRoles(person.superiorRoles))
if (found != null) {
return found;
}
}
return null;
}
var superiors = hr.findPeopleByRoles(entity.createdBy.superiorRoles);
return findSuperiorRecursive(createReference('Role', 56), superiors);
Název entity nastavíme na Absenci. V návrháři vytvoříme kroky Začátek->Schválení->Konec
V konfiguraci kroku Schválení nastavíme:
entity.state = Absence.State.APPROVED
entity.state = Absence.State.REJECTED
entity.absenceType?.name} ${entity.person?.getFullName()} ${entity.fromDateFormatted()} - ${entity.toDateFormatted()}
Výraz pro přiřazení úkolu k roli.
entity.person.superiorRoles
(v tomto případě pro entitu Absence zjistíme člověka entity.person
a u ní se odkazujeme na jeho nadřízené role)
var request = queryService.createQueryRequest();
request.filterByColumn.put('id', queryService.createColumnFilter('GREATER_THAN', 3));
var columnFilter = queryService.createColumnFilter();
columnFilter.operator = 'NOT_EMPTY';
request.filterByColumn.put('EXPORT_TO', columnFilter);
request.allLanguages = true; // vrací kompletní multilang (všechny jazyky), standardne se vrací jen výchozí jazyk
var importGoods = queryService.remoteQuery(
'http://localhost:82/api/query/Goods',
'CARD:123456789', // 123456789 - přístupový kód na dotazovaném prostředí
// '{"allLanguages": true, "filterByColumn": {"id": {"operator": "GREATER_THAN", "value": 3},"EXPORT_TO": {"operator": "NOT_EMPTY", "noValueOperator": true}}}'
request // použití dříve vytvořeného dotazu, výše je zakomentovaná varianta filtru zadaného ručně jako text
)
var goodsType = findEntityById('GoodsType', 1);
for (var item: importGoods) {
var newGoods = false;
var newId = 1000000L + item.id;
var goods = findEntityByIdIfPresent('Goods', newId);
if (goods == null) {
goods = createNew('Goods');
goods.setId(newId);
newGoods = true;
}
goods.setNote('Imported goods id ' + item.id);
goods.setCode(item.code);
goods.setName(Multilingual.buildFromJson(item.name));
goods.setType(goodsType);
if (newGoods) {
insert(goods);
} else {
persist(goods);
}
}
Příklad načtení dat reportu
const reportId = 137L;
const parameters = ['2024-01-01', null, ...];
let request = queryService.createQueryRequest()
.withReportParameters(reportId, parameters);
let reportData = queryService.query('Report', request);
// pro report s pojmenovanýma parametrama
const namedParameters = {'dateFrom':'2025-01-01', 'dateTo': '2025-02-12'}; // mapa parametrů
let request = queryService.createQueryRequest()
.withReportParameters(reportId, null)
.withNamedParameters(namedParameters);
let reportData = queryService.query('Report', request);
for (let row: reportData) {
// iterace řádků reportu
let sloupec1 = row['nazevSloupce1'];
.....
}
var list = ['a', 'abcdefg', 'abcdefghijkl', 'abcde', ...]; // alternativa k listOf(..)
// filterFunction/filterLambda si jsou ekvivalentní - zahazují prvky s délku 4 a méně
var filterFunction = function(item){item.length() > 4};
var filterLambda = (item) -> {item.length() > 4};
return list.stream() // .stream() je metoda na Listu
// aplikuj filtr - všechny 3 volání .filter si jsou ekvivalentní
.filter(with(filterFunction))
.filter(with(filterLambda))
.filter(with((item) -> {item.length() > 4}))
// seřadí položky kolekce dle jejich délky
.sorted(comparator((item1, item2) -> {item1.length().compareTo(item2.length())}))
// místo každé položky (textu) vrať jeho délku (číslo)
.map(to((item) -> {item.length()}))
.collect(Collectors.toList()); // vrať List
// celkový výsledek = [5, 7, 12]
// vytvoří se 2 dávky pro tento příklad
var batch1 = createNew("BatchEntity");
batch1.id = 1L;
batch1.number = "num1";
var batch2 = createNew("BatchEntity");
batch2.id = 2L;
batch2.number = "num2";
var list = listOf(batch1, batch2);
// z listu se udělá mapa, která pro ID vrátí dávku
var batchesById = list.stream()
.collect(Collectors.toMap( // každou dávku vlož do mapy
as((batch) -> {batch.id}), // klíč - ID dávky
as((batch) -> {batch}) // hodnota - dávka
));
batchesById[1L].number = "num1edited"; // dávce s ID 1 se upraví číslo
if (entity.quantity > 100) { // pokud je kvantita větší 100
entity.quantity = 100; // zastropuj ji na 100
} else if (entity.quantity > 50) { // (pokud kvantita není větší 100 a) pokud je kvantita větší 50 (jinými slovy: pokud je mezi 50 a 100)
entity.quantity = 50; // nastav ji na 50
} else { // jinak
entity.quantity = 10; // nastav ji na 10
}
vnořené ify
if (entity.name.startsWith('1120 ')) { // pokud jméno začíná s 1120
if (entity.name.endsWith('3X')) { // pokud (jméno začíná s 1120 a) končí na 3X
entity.name = '1120 3A'; // přejmenuj (v podstatě změň koncovku 3X na 3A)
} else if (entity.name.endsWith('4X')) { // nebo pokud (jméno začíná s 1120 a) končí na 4X
entity.name = '1120 4A'; // přejmenuj (v podstatě změň koncovku 4X na 4A)
}
// poznámka - tento if kontroluje jen začátek a konec textu, nebude fungovat dobře, pokud jméno může být např. '1120 2V 3A'
}
ukončení cyklu
for (var item : items) {
if (item.id == 5) {
entity.name = item.name; // přejmenuj entitu na jméno položky s ID 5
break; // našli jsme požadovanou položku, zbývající položky listu můžeme ignorovat - cyklus ukončíme
}
}
Tohle je náš přepis, který může obsahovat chyby. Odkazujte se na JEXL dokumentaci.
K odkazování na vlastnosti objektů (entit) a pro volání jejich funkcí se používá tečková notace. Tedy objekt.vlastnost nebo objekt.funkce().
Příklady použití volání funkcí Java:
Zapsáním řetězce 'Ahoj světe'
do výrazu, bude výsledkem tento řetězec.
Výrazem 'Ahoj světe'.concat('!')
získáte řetězec Ahoj světe!.
Výrazem 'Ahoj světe' + '!')
získáte také řetězec Ahoj světe!.
Výrazem 'Ahoj světe'.substring(5,9)
získáte výsledný řetězec svět
V definici výrazu lze používat běžně známé a používané operátory
Typ | Operátory |
---|---|
aritmetické | +, -, *, /, %, ^, div, mod |
relační | <, >, ==, !=, <=, >=, lt, gt, eq, ne, le, ge |
logické | and, or, not, &&, ||, ! |
podmíněné | ?: |
regulární | =~ , !~ |
div a mod jsou textové aliasy pro operátory / a % (dělení a zbytek po dělení)
operátor + se dá použít také pro spojení řetězců
lt, gt , eq, ne, le, ge jsou textové aliasy k operátorům <, >, ==, !=, <=, >=
jsou to zkratky anglických slov (lt = Less Then, eq = EQual, ge = Greater or Equal, apod.)
příklady výrazu pro entitu absence: podmínka-pak-jinak (if-then-else)
(entity.state == 'APPROVED' || entity.state == 'REJECTED') ? 'Nadřízený rozhodl (schválil nebo zamítl)' : 'Nadřízený ještě nerozhodl nebo byla žádost zrušena'
podmínky lze vkládat i vnořeně (nové řádky jsou vloženy pro přehlednost)
entity.state == 'APPROVED'
? 'Nadřízený rozhodl'
: entity.state == 'REJECTED' ? 'Nadřízený zamítl' : entity.state == 'CANCELED' ? 'Žádost zrušena' : 'Nadřízený zatím nerozhodl'
Elvis operator ?:
je zkrácený if-then-else. Následující dva výrazy jsou funkčně identické. Oba výrazy vrátí entity.name
, nebo text 'beze jména' pokud entity.name
není nastaveno.
entity.name??'beze jména'
entity.name != null ? entity.name : 'beze jména'
Lze testovat textové pole, zda vyhoví nebo nevyhoví zadanému regulárnímu výrazu.
příklad pro entitu zboží, jejíž kód je 'Šroub TORX M10, L=150mm':
entity.code =~ 'Šroub.*M\d+.*' ? 'Je metrický šroub' : 'Není metrický šroub'
Některé vlastnosti entit obsahují více hodnot, například seznam nebo mapu (obecně kolekce).
V takových případech se na jednotlivé prvky odkazujeme přes index uvedený v hranatých závorkách. Index může být číselný (v případě seznamu) nebo text (v případě mapy)
Následující výraz vrátí osobu, která vytvořila první program pro stroj na dané operaci, nebo nevrátí nic (null), pokud stroj na operaci žádné programy nemá:
operation.machinePrograms.isEmpty() ? null : operation.machinePrograms[0].programmedBy
získání počtu programů pro stroj na danou operaci - size() vrátí velikost (délku) seznamu
operation.machinePrograms.size()
Pokud entita nemá nastavenou některou proměnnou a zkusíte k ní přistoupit, může nastat výjimka a aplikace zahlásí chybu.
Pro zabránění přístupu na null hodnoty (a způsobení výjimky) se používá operátor ?.
Příklad: Entitu nikdo neupravil (updatedBy obsahuje null), následující výraz způsobí chybu
entity.updatedBy.lastname
oproti tomu výraz
entity.updatedBy?.lastname
vrátí null nebo příjmení člověka (a chybu nezpůsobí).
V kódu skriptu jdou použít klasické komentáře jako v jazyce Java.
// komentář
(ignoruje vše od znaků // (včetně) až do konce řádku
/*
víceřádkový
komentář
*/
Pro řízení běhu skriptu jdou použít následující klíčová slova:
klíčové slovo | popis |
---|---|
if (podmínka) { .. } else if (podmínka2) { .. } else { .. } |
v případě že podmínka if vyhoví (true ), provedou se příkazy zadané mezi závorkamipokud prvotní podmínka v if není splněna, následující else if se postupně vyhodnocují (může jich být libovolný počet), dokud se nenajde podmínka, která vyhovípokud ani jedna podmínka nebyla vyhodnocena a je přítomný else příkaz, spustí se kód v else větvijak else if tak else jsou libovolné, tj. mohou a nemusí být přítomny, počet else if je libovolný, avšask else může být jen jeden a musí být poslední (tj. za if nebo else if ) |
for (var item : list) {} |
iteruje zadaný seznam a provádí blok příkazů mezi závorkami aktuální iterovaná položka je dostupná v proměnné item break; ukončí běh cyklu |
return null; |
ukončí běh skriptu |
Pro příklad viz JxltEngine.Template https://commons.apache.org/proper/commons-jexl/apidocs/org/apache/commons/jexl3/package-summary.html
$$ for (var item : list) { ... $$} |
celá řádka je JEXL výraz, lze začít if/for a ukončit na jiné řádce |
${entity.name} |
obsah závorek je výraz |