SPEL Formula
A minimum of knowledge in Java / Javascript programming is required.
Formulation often requires the use of SpEL Formulas to calculate results (for example to get the production cost of a product, or to calculate its energetic value).
This page is dedicated to the creation of these formulas in the system, and gives examples of simple and complex formulas to calculate automatically a result.
Formulas Localization
Formulas can be found in two main places:
- The list of administration characteristics, e.g. for nutrients, physico-chemical characteristics...
- The list of dynamic characteristics of a product/entity model.
But you can also find them on :
- Labeling rules on a product / entity model
- A .spel file linked to an entity model
- Catalogs
- Search exports
- Connectors
SPEL Context in beCPG
There exists several contexts where SPEL formulas are being used.
Formulation
Formulation is the main context where SPEL formula is used, it's important to understand what is the formulation chain and when formulas are triggered. The formulation chain's main goal is to load an entity from alfresco models and apply several transformations on it at the end the entity object is saved.
There are several formulation chains, one for a project and one for a product for example. On the product formulation chain we load an inherited ProductData JAVA object (RawMaterialData/ SemiFinishedProductData/ FinishedProductData), then we apply the corresponding formulation chain. At the end the ProductData is saved.
Important at the end of the formulation the engine can decide to save the ProductData only in memory (it's the case for OM simulation for example). You should take care of not saving data during the process it's why it's important to only manipulate the productData during SPEL formula.
The context of the formula is usually asked by most of SPEL functions. It's the element on which the formula is working.
#this
displays current entityentity
displays the productData type context of the formula.dataListItem
displays the line of a datalist, for example a line of the composition.dataListItemEntity
displays the entity (type ProductData) associated to a line of the datalist, for example the Raw Material in a composition list.variantData
displays the variant linked to a line of the composition list, packaging list,...compoList[0].product
displays the first composition lin's entity
Formulation context:
Context variable | Description |
---|---|
#root | entity being formulated |
Pre/post composition/packaging/process column formulation context:
Context variable | Description |
---|---|
entity | entity being formulated |
dataListItem | item row being formulated (compoList/packagingList/processList) |
dataListItemEntity | entity associated with the list (compoListProduct/packagingListProduct/ resourceProduct) |
Cost/Nut/Physico/Claim formulation context
Context variable | Description |
---|---|
entity | entity being formulated |
dataListItem | item row being formulated (compoList/packagingList/processList) |
Labeling context
Prefs or Render rules:
Context variable | Description |
---|---|
#root | LabelingFormulaContext JAVA object |
entity | entity being formulated |
Labeling filter rules:
Context variable | Description |
---|---|
#root | LabelingFormulaFilterContext JAVA object |
compoListDataItem | CompoListItem JAVA object to test filter on |
ingListDataItem | IngListItem JAVA object to test filter on |
ingTypeItem | IngTypeItem JAVA object to test filter on |
entity | entity being formulated |
dataListItem | Alias for compoListDataItem |
isClaimed(claimNodeRef) | Check for claim on ingredient or product |
Catalog Spel context
Same context than formulation
JXLS report context
Same context than formulation
Export search context
Context variable | Description |
---|---|
#root | DefaultExcelReportSearchPlugin.FormulaContext JAVA object |
prop | Array of properties extracted, ie props["bcpg:code"] |
No access to entity neither custom spel fonction
Connector Spel context
Context variable | Description |
---|---|
#root | StandardCSVVisitor.FormulaContext JAVA object |
prop | Array of properties extracted, ie props["bcpg:code"] |
No access to entity neither custom spel fonction
SPEL Language
The Spring Expression Language (SpEL) is a powerful expression language that supports querying and manipulating an object graph at runtime. It can be used with becpg at several places
There are several operators available in the language:
Type | Operators |
---|---|
Arithmetic | +, -, *, /, %, ^, div, mod |
Relational | <, >, ==, !=, <=, >=, lt, gt, eq, ne, le, ge |
Logical | and, or, not, &&, |
Conditional | ?: |
Regex | matches |
Operators
Arithmetic Operators
All basic arithmetic operators are supported.
Formula | Result |
---|---|
19 + 1 | 20 |
'String1 ' + 'string2' | "String1 string2" |
20 - 1 | 19 |
10 * 2 | 20 |
36 / 2 | 19 |
36 div 2 | 18, même que l'opérateur / |
37 % 10 | 7 |
37 mod 10 | 7, même que l'opérateur % |
2 ^ 9 | 512 |
(2 + 2) * 2 + 9 | 17 |
Divide and modulo operations have alphabetic aliases, div for / and mod for %. The + operator can also be used to concatenate strings.
Relational Operators
All basic relational and logical operations are also supported.
Formula | Result |
---|---|
1 == 1 | true |
1 eq 1 | true |
1 != 1 | false |
1 ne 1 | false |
1 < 1 | false |
1 lt 1 | false |
1 <= 1 | true |
1 le 1 | true |
1 > 1 | false |
1 gt 1 | false |
1 >= 1 | true |
1 ge 1 | true |
All relational operators have alphabetic aliases, as well. For example, in XML-based configs we can't use operators containing angle brackets (<, <=, >, >=). Instead, we can use lt (less than), le (less than or equal), gt (greater than), or ge (greater than or equal).
Logical Operators
SpEL supports all basic logical operations:
Formula | Result | ||
---|---|---|---|
250 > 200 && 200 < 4000 | true | ||
250 > 200 and 200 < 4000 | true | ||
```400 > 300 | 150 < 100``` | true | |
400 > 300 or 150 < 10 | true | ||
!true | false | ||
not true | false |
As with arithmetic and relational operators, all logical operators also have alphabetic clones.
Conditional Operators or Ternary
Conditional operators are used for injecting different values depending on some condition:
"Test" ? "valeur si test vrai" : "valeur si test faux"
The ternary operator is used for performing compact if-then-else conditional logic inside the expression. In this example we tried to check if it was true or not.
ex: 2 == 1 ? true : false // false
Another common use for the ternary operator is to check if some variable is null and then return the variable value or a default:
ex : qty != null ? qty : recipeQtyUsed
The Elvis operator is a way of shortening the ternary operator syntax for the case above used in the Groovy language. It is also available in SpEL. The code below is | the code above:
ex : qty ?:0d > Injecte 0 si qty est null
Using Regex
The matches operator can be used to check whether or not a string matches a given regular expression.
Formula | Result |
---|---|
'100' matches 'd+' | true |
'100fghdjf' matches 'd+' | false |
'valid alphabetic string' matches '[a-zA-Zs]+' | true |
'invalid alphabetic string #nodeRef' matches '[a-zA-Zs]+' | false |
'someBean.someValue matches '\d+'' | true if someValue contains only digits |
Variables
Variables can be referenced in the expression using the syntax #variableName. Variables are set using the var keyword
var myVar = "This is a test";
#myVar // This is a test
The #this and #root variables
The variable #this is always defined and refers to the current evaluation object (against which unqualified references are resolved). The variable #root is always defined and refers to the root context object. Although #this may vary as components of an expression are evaluated, #root always refers to the root.
Accessing beCPG Java Field
With the help of SpEL, we can access the contents of any Java Object in the formulation context. For example in case of product formulation we have access to ProductData object:
public class ProductData {
NodeRef hierarchy1;
NodeRef hierarchy2;
MLText legalName;
MLText pluralLegalName;
String title;
String erpCode;
SystemState state | SystemState.Simulation;
ProductUnit unit | ProductUnit.kg;
ProductData entityTpl;
List<NodeRef> plants | new ArrayList<>();
Double qty;
Double density;
Double yield;
Double manualYield;
Double secondaryYield;
Double yieldVolume;
Double netWeight;
Double weightPrimary;
Double netWeightSecondary;
Double weightSecondary;
Double netWeightTertiary;
Double weightTertiary;
Boolean dropPackagingOfComponents;
/*
* DataList
*/
List<AllergenListDataItem> allergenList;
List<CostListDataItem> costList;
List<PriceListDataItem> priceList;
List<IngListDataItem> ingList;
List<NutListDataItem> nutList;
List<OrganoListDataItem> organoList;
List<MicrobioListDataItem> microbioList;
List<PhysicoChemListDataItem> physicoChemList;
List<LabelClaimListDataItem> labelClaimList;
List<ControlDefListDataItem> controlDefList;
List<LabelingListDataItem> labelingList;
List<ResourceParamListItem> resourceParamList;
List<ReqCtrlListDataItem> reqCtrlList;
List<PackMaterialListDataItem> packMaterialList;
/*
* View
*/
CompoListView compoListView | new CompoListView();
ProcessListView processListView | new ProcessListView();
PackagingListView packagingListView | new PackagingListView();
LabelingListView labelingListView | new LabelingListView();
/*
* Variants
*/
List<VariantData> localVariants;
VariantPackagingData defaultVariantPackagingData;
VariantData defaultVariantData;
/*
* Product specifications
*/
List<ProductSpecificationData> productSpecifications;
List<ClientData> clients;
/*
* Origin geo
*/
List<NodeRef> geoOrigins | new ArrayList<>();
/*
* Completion scores
*/
String entityScore;
List<String> reportLocales;
List<ProductData> compareWithEntities;
}
To access members variable in SpEL just indicate the variable name:
Formula | Result |
---|---|
title | "nom du title" |
erpCode = "0001" | 0001 |
clients[0] | nodeRef |
compoListView.compoList[0] | CompoListDataItem |
utList.?[nut.toString() == 'nodeRef'].value | Nut list value |
You can also call any method available in the ProductData object:
isLiquid()
isRawMaterial()
isPackaging()
isPackagingKit()
isSemiFinished()
isLocalSemiFinished()
Collection Selection
Selection is a powerful expression language feature that allows you to transform some source collection into another by selecting from its entries.
Alias | Expression |
---|---|
cost['nodeRef'] | costList.^[cost.toString() == 'nodeRef'] |
nut['nodeRef'] | nutList.^[nut.toString() == 'nodeRef'] |
allergen['nodeRef'] | allergenList.^[allergen.toString() == 'nodeRef'] |
ing['nodeRef'] | ingList.^[ing.toString() == 'nodeRef'] |
organo['nodeRef'] | organoList.^[organo.toString() == 'nodeRef'] |
physico['nodeRef'] | physicoChemList.^[physicoChem.toString() == 'nodeRef'] |
microbio['nodeRef'] | microbioList.^[microBio.toString() == 'nodeRef'] |
compo['nodeRef'] | compoListView.compoList.^[product.toString() == 'nodeRef'] |
process['nodeRef'] | processListView.processList.^[resource.toString() == 'nodeRef'] |
resParam['nodeRef'] | resourceParamList.^[param.toString() == 'nodeRef'] |
pack['nodeRef'] | packagingListView.packagingList.^[product.toString() == 'nodeRef'] |
packaging['nodeRef'] | packagingListView.packagingList.^[product.toString() == 'nodeRef'] |
compoVar['name'] | compoListView.dynamicCharactList.^[title == 'name']?.value |
packVar['name'] | packagingListView.dynamicCharactList.^[title == 'name']?.value |
packagingVar['name'] | packagingListView.dynamicCharactList.^[title == 'name']?.value |
processVar['name'] | processListView.dynamicCharactList.^[title == 'nodeRef']?.value |
labelClaim['nodeRef'] | labelClaimList.^[labelClaim.toString() == 'nodeRef'] |
labeling['nodeRef'] | labelingListView.ingLabelingList.^[grp.toString() == 'nodeRef'] |
Selection uses the syntax .?[selectionExpression]. This will filter the collection and return a new collection containing a subset of the original elements. For example, selection would allow us to easily get a list of CostListDataIteltem :
Formula | Result |
---|---|
costList.?[cost == 'Cost MP'] | Array of costs where cost nodeRef = 'Cost MP' |
costList.?[value<27] | Array of costs where value < 27 |
In addition to returning all the selected elements, it is possible to retrieve just the first or the last value. To obtain the first entry matching the selection the syntax is ^[…]
whilst to obtain the last matching selection the syntax is $[…]
.
Formula | Result |
---|---|
costList.^[cost == 'Cost MP'] | First cost matching |
costList.$[cost == 'Cost MP'] | Last cost matching |
In addition to returning all selected items, it is possible to retrieve only the first or last value. To obtain the first entry corresponding to the selection, the syntax is ^[...]
while to obtain the last corresponding selection, the syntax is $[...]
.
Collection Projection
Projection allows a collection to drive the evaluation of a sub-expression and the result is a new collection. The syntax for projection is ![projectionExpression]. Most easily understood by example, suppose we have a list of costs but want the list of cost value. Effectively we want to evaluate 'value' for every entry in the cost list. Using projection:
Formula | Result |
---|---|
costList.![value] | returns [ '5', '25' ] |
Formula editor
Formula editor is presented as a text area, with numeroted lines, and two tables below.
Generalities on a simple formula
Most of the time, SpEL formulas will display a single result; which is the last calculated expression. That means that it's possible to use intermediate variables in calculation, if the variable containing the final result is called at the end of the formula.
For example, the following formula will give the result, stocked in the result
result
var result = qty;
var yieldCoeff = (yield/100);
#result = #result / #yieldCoeff;
#result;
However, most of the formulas will have only a single expression, and the use of intermediate variables won't be necessary. Then, the previous expression could be shortened:
qty / (yield/100)
One of the main limitation of SpEL language is the absence of conditions with if ... else
. You need to use ternary structure which can accumulate and make the reading complex.
e.g. Salt quantity calculation (based on sodium quantity):
nut['Sodium'].value == null ? 0d : nut['Sodium'].value * 2.5 / 1000
(Sodium is here the nodeRef of the nutrient Sodium)
The formula first checks that the Sodium value is not null. If null, the formula displays 0.
Most of the reading access to the propertu or calculation values have to include a default value, in case the value is not present. This value must have the suffix d
(or D
) to indicate to the sustem that this number contains comma, and not a character (example: 1d
is a number with comma, while 1 can represent the entire number 1, or the text character "1").
Multiplications work with 0d
, but divisions must have 1d
, and formulas which display text must have ""
as default value. These values must be used in condition formulas.
Helpers SPEL
CTRL + SPACE shortcut displays a help menu to have a quick access to the advanced fonctions.
Dynamic characteristic
Dynamic characteristics can be done in a formulated product, or in every lines of the linked list. Every list with components (packaging, composition,process) owns its own dynamic characteristic list.
It's possible to apply a formula to all elements of a datalist by choosing a column. The results will then be displayed in a column.
eg:"Costs (€)" is calculated for every components of the list.
Characteristics
Tables under the text area enable users to quickly select fragments of formulas to insert in the text zone where the cursor is.
You can choose elements on the left, and choose the information to display from this elements at the right (value, minimumValue,...)
Advanced
beCPG Spel custom functions
beCPG defines a set of customs functions accessible from the SPEL formula.
Syntax to use this functions is @beCPG.function
Functions | Description |
---|---|
propValue | Displays a property. |
setValue | Set a value to a property. |
assocValue | Displays the nodeRef value of an association |
assocValues | Displays the values of a multiple associations in table. |
setAssocs | Set one or several assoc values. |
assocPropValues | Displays properties from the associations extracted. |
assocAssocValues | Displays associations from the associations extracted. |
propMLValue | Extract a multilingual property in the specified locale or all the translation |
updateMLText | Set a locale value to a multilingual field. |
copy | Copy associations and/or lists from a source to the current product. |
applyFormulaToList | Applicate a formula to a list, like a dynamic column. |
filter | Use to filter. Is generally combined with other formulas, like min, max, avg or sum. |
max | Use to find the maximum value of an array. |
min | Use to find the minimim value of an array. |
avg | Use to find the average value of an array. |
sum | Use to sum the values of an array. |
children | Used to get the children of entity , used to go down in the composition levels. |
findOne | Used to get the entity designed with the nodeRef |
sourcesAssocValues | Displays the where-used nodeRef array, based on a target association. |
formatNumber | For numbers, it is possible to specify the decimal number |
formatDate | For dates, you can specify the format |
Warning: if java fields are available, functions of type propValue, assocValue, setValue, assocValue, assocValues will not work. ex: @beCPG.propValue(#this, “bcpg:productQty”) is equivalent to the java field “qty ”. For more information on java fields Click here
Example
- propValue : Displays a property. we want to extract the custom:name field from the entity with a fixed dynamic characteristic.
@beCPG.propValue(#this,"custom:name")
we want to extract the custom:code field from the entities of a list with a characteristic in column.
@beCPG.propValue(dataListItemEntity,"custom:code")
you want to extract the value of a hierarchical family (d:noderef). In this case, propValue : must be typed twice.
@beCPG.propValue(@beCPG.propValue(#this,"bcpg:productHierarchy1"),"bcpg:lkvValue")
- setValue : Set a value to a property.
set "kg" to custom:productUnit :
@beCPG.setValue(#this,"custom:productUnit","kg")
we want to assign the custom:description value to the custom:legalName field
@beCPG.setValue(#this,"custom:legalName",@beCPG.propValue(#this,"cm:description"))
- assocValue :Displays the nodeRef value of an association
displays association trademarkRef :
@beCPG.assocValue(#this,"bcpg:trademarkRef")
- assocValues : Displays the values of a multiple associations in table.
we want to extract the multiple association "bcpg:productGeoOrigin":
@beCPG.assocValues(#this,"bcpg:productGeoOrigin")
Please note that you can extract the first element with [0]
@beCPG.assocValues(#this,"bcpg:productGeoOrigin")[0]
Which is then similar to:
@beCPG.assocValue(#this,"bcpg:productGeoOrigin")
- setAssocs : Set one or several assoc values.
we want to set the plant with nodeRef "workspace://SpacesStore/169a0cf8-2f0a-4011-9b19-dea14676e136" to the "bcpg:plant" association:
@beCPG.setAssocs(#this,"bcpg:plants", "workspace://SpacesStore/169a0cf8-2f0a-4011-9b19-dea14676e136")
we want to add the client with nodeRef "workspace://SpacesStore/844aa160-f344-43be-a659-434d319e584f" to the "bcpg:clients" association:
var clients = @beCPG.assocValues(#this,"bcpg:clients");
#clients.add("workspace://SpacesStore/844aa160-f344-43be-a659-434d319e584f");
@beCPG.setAssocs(#this,"bcpg:clients", #clients)
- assocPropValues : Displays properties from the associations extracted.
we want to extract title for client :
@beCPG.assocPropValues(#this,"bcpg:client","cm:title")
- assocAssocValues : Displays associations from the associations extracted.
we want to extract bcpg:plantCertifications from bcpg:plant :
@beCPG.assocAssocValues(#this,"bcpg:plant","bcpg:plantCertifications")
- propMLValue : Extract a multilingual property in the specified locale.
we wan to extract the bcpg:legalName in english US:
@beCPG.propMLValue(#this,"bcpg:legalName","en_US")
we wan to extract the bcpg:legalName all translation:
@beCPG.propMLValue(#this,"bcpg:legalName",null)
- updateMLText : Set a locale value to a multilingual field.
we want to set tthe value "Finish Legal Name" to the legalName in finish:
@beCPG.updateMLText(legalName,"fi","Finish Legal Name")
Beware: this works for multilingual fields as helpers only.
- copy : Copy associations and/or lists from a source to the current product.
The function is working like this:
@beCPG.copy(sourceNodeRef, assocToCopyList, listToCopyList)
we want to copy the associations "bcpg:trademarkRef","bcpg:subsidiaryRef","bcpg:clients", and "bcpg:suppliers" from the first entity of the compoList:
@beCPG.copy(compoList[0].product,{"bcpg:trademarkRef","bcpg:subsidiaryRef","bcpg:clients","bcpg:suppliers"},"")
we to copy the microbioList from the association productMicrobioCriteriaRef:
var productMicrobioCriteriaRef = @beCPG.assocValue(#this,"bcpg:productMicrobioCriteriaRef");
@beCPG.copy(#productMicrobioCriteriaRef,"",{"bcpg:microbioList"})
we want to copy the list and properties from the parent entity:
@beCPG.copy(parentEntity,{"bcpg:legalName", "bcpg:productHierarchy1", "bcpg:erpCode"},{"bcpg:compoList","bcpg:packagingList","mpm:processList"})
- applyFormulaToList : Applicate a formula to a list, like a dynamic column.
we want to set compoListQtySubFormula of compoList as 0 :
@beCPG.applyFormulaToList(getCompoList(),"@beCPG.setValue(dataListItem,'bcpg:compoListQtySubFormula', 0d)")
This works on most of standards listes, like:
- getCompoList() ( or compoList )
- getPackagingList() ( or packagingList )
- getProcessList() ( or processList )
- getControlDefList() ( or controlDefList)
- priceList
- costList
- reqCtrlList
- filter Use to filter. Is generally combined with other formulas, like min, max, avg or sum.
we want to find the reqCtrlList elements, which have a reqMaxQty value and are not associated to "Info" type:
@beCPG.filter(reqCtrlList, "reqMaxQty!=null && reqType.toString() !='Info'")
we wan to extract the compoList elements of the main variant which have a qty > 0.5 kg:
@beCPG.filter(getCompoList(new fr.becpg.repo.variant.filters.VariantFilters(true)), "dataListItem.qty > 0.5")
- max Use to find the maximum value of an array:
@beCPG.max($arrayOfDouble)
we want to find the maxmim reqMaxQty of the reqCtrlList:
@beCPG.max(reqCtrlList.![reqMaxQty])
we want to find the maximum qty of the compoList:
@beCPG.max(compoList.![qty])
- min Use to find the minimim value of an array:
@beCPG.min($arrayOfDouble)
we want to find the minimum among the reqCtrlList elements which have a reqMaxQty and are not associated to "Info" type:
@beCPG.min(@beCPG.filter(reqCtrlList, "reqMaxQty!=null && reqType.toString() !='Info'").![reqMaxQty])
- avg Use to find the average value of an array:
@beCPG.avg($arrayOfDouble)
- sum Use to sum the values of an array.
@beCPG.sum(range, formula)
You can have the sum of the elements in a datalist.
@beCPG.sum(getCompoList(new fr.becpg.repo.variant.filters.VariantFilters(true)),dataListItem.qty)
We add the filter to only consider the quantity of the composition elements with the default variant. However, we can go further and only takes in account the quantity of the componenents which are not local semi finished products.
@beCPG.sum(
@beCPG.filter(
getCompoList(new fr.becpg.repo.variant.filters.VariantFilters(true)),
!(dataListItemEntity instanceof T(fr.becpg.repo.product.data.LocalSemiFinishedProductData))
),
dataListItem.qty
)
Functions isLiquid()
to isLocalSemiFinished()
are shotcut to not use instanceof
seen in the last formula.
Second example, you want to do a sum of the elements in a dynamic column. The fomula is the following using column 7 in the composition as an example:
@beCPG.sum(compoList, "@beCPG.propValue(dataListItem,'bcpg:dynamicCharactColumn7')" )
children Used to get the children of
entity
, used to go down in the composition levels.@beCPG.children(entity)
findOne Used to get the entity designed with the
nodeRef
@beCPG.findOne(nodeRef)
- sourcesAssocValues Displays the where-used nodeRef array, based on a target association.
we want to have the project list using my current product:
@beCPG.sourcesAssocValues(#this,"pjt:projectEntity");
we want to have the number of product using my current product in their composition:
var sources = @beCPG.sourcesAssocValues(#this,"bcpg:compoListProduct");
var size = #sources.size();
#size;
- updateDate Modify a date value by adding day/ week / month / year:
@beCPG.updateDate($date, $field, $amount )
we substract 5 days to the today's date:
@beCPG.updateDate(new java.util.Date(), T(java.util.Calendar).DAY_OF_MONTH, -5 )
on ajoute 1 an à la date de création :
@beCPG.updateDate(@beCPG.propValue(#this, "cm:created"), T(java.util.Calendar).YEAR, 1 )
We add 3 months to the modified date:
@beCPG.updateDate(@beCPG.propValue(#this, "cm:modified"), T(java.util.Calendar).MONTH, 3 )
- formatNumber : you can specify the format for numeric fields
we want to display the protein nutrient with 2 significant digits
@beCPG.formatNumber(nut['workspace://SpacesStore/d49d02aa-3443-48f8-b6ee-9e66fe2ba4c4']?.value, "0.##")
- formatDate : you can specify the date format.
we want to display the month in which the product was last formulated
@beCPG.formatDate(formulatedDate,"MM/YYYY")
@beCPG.formatDate(formulatedDate,"dd/MM/YYYY")
Nutriscore formula (beCPG 4.0)
To extract the nutriscore details, we need to initialise the context as variante
var context = T(fr.becpg.repo.product.formulation.score.NutriScoreContext).parse(nutrientDetails);
Then, we can extract a summarize:
#context.toDisplayValue();
We can also extract details on each nutrient:
- getNutriScore()
- getEnergy()
- getSatFat()
- getTotalFat()
- getTotalSugar()
- getSodium()
- getPercFruitsAndVetgs()
- getNspFibre()
- getAoacFibre()
- getProtein()
We can also extract details on nutriscore:
- getCategory()
- getNutrientClass()
- getClassLowerValue()
- getClassUpperValue()
- getAScore()
- getCScore()
We can use the value to extract from them:
- getLowerValue()
- getUpperValue()
- getScore()
- getValue()
For example:
#context.getProtein().getValue();
#context.getTotalSugar().getLowerValue();
#context.getAScore();
#context.getClassUpperValue();
Accessing java type
The special 'T' operator can be used to specify an instance of java.lang.Class (the 'type'). Static methods are invoked using this operator as well.
T(java.util.Date)
T(String)
T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR
Constructors
Constructors can be invoked using the new operator. The fully qualified class name should be used for all but the primitive type and String (where int, float, etc, can be used).
Expression templating
Essentially used in labeling Render formula. Expression templates allow a mixing of literal text with one or more evaluation blocks. Each evaluation block is delimited with prefix and suffix characters that you can define, a common choice is to use #{ }
as the delimiters. For example,
"random number is #{T(java.lang.Math).random()}"
Inline lists, maps and array construction
Lists can be expressed directly in an expression using {}
notation. Maps can also be expressed directly in an expression using {key:value}
notation. Arrays can be built using the familiar Java syntax, optionally supplying an initializer to have the array populated at construction time.
"{1,2,3,4}" // List construction
"{name:'Nikola',dob:'10-July-1856'}" // Map construction
"new int[]{1,2,3}" // Array construction
Examples of formulas
- This formula calculates the sum of wheat and wheat flour % (in ingredient slist) in the product. ('Wheat' is the nodeRef, not text).
(ingList.^[ing.toString() == 'Wheat']?.qtyPerc != null ? ingList.^[ing.toString() == 'Wheat']?.qtyPerc : 0d) +
(ingList.^[ing.toString() == 'Wheat flour']?.qtyPerc != null ? ingList.^[ing.toString() == 'Blé']?.qtyPerc : 0d)
- This formula calculates the sum of the dynamic column 2, for the components which are not local semi finished products.
@beCPG.sum(getCompoList(new fr.becpg.repo.variant.filters.VariantFilters()),
"dataListItemEntity instanceof T(fr.becpg.repo.product.data.LocalSemiFinishedProductData) ? 0d : @beCPG.propValue(dataListItem.nodeRef, 'bcpg:dynamicCharactColumn2')")
- In this formula, in following the entity model of the component, will be sum: The property "Reconstitution Rate", multiplied by the quantity of the composition line, if the template of the component is "Reconstituted FP". The physicy-chemical characteristic "Dry matter" of this component if the previous is not applicable
@beCPG.sum(getCompoList(), "@beCPG.findOne(dataListItemEntity).getEntityTpl() != 'Reconstituted FP' ? @beCPG.propValue(dataListItemEntity, "bcpg:reconstitutionRate") * dataListItem.qty : dataListItemEntity.physico['Dry matter']?.value")
- Used to get the today's date in the dd/MM/yyyy format.
new java.text.SimpleDateFormat("dd/MM/yyyy").format(new java.util.Date())
- Used to get personalized format on number.
new java.text.DecimalFormat("0.#").format(nut['Protein']?.value)
- Used to replace the text "to replace" to "new text" in the field cm:description.
@beCPG.propValue($nodeRef,"cm:description").replaceAll("toreplace","new text")
- Find out if a claim is claimed
entity.labelClaim['Kasher']?.isClaimed
- Extract the text of the field cm:name after the first occurance of a text string.
var productName = @beCPG.propValue(#this,"cm:name");
#productName.substring(#productName.indexOf("test"));
- Test the presence of a text string at the beggining of a text and display true / false.
@beCPG.propValue(#this,"cm:name").startsWith("X");