Formules SPEL

Un minimum de connaissances en programmation Java ou Javascript est attendu.

La formulation nécessite fréquemment l'usage de formules SPEL pour calculer des résultats, par exemple obtenir le coût de production d'un produit, ou bien calculer sa valeur énergétique en kJ.

Cette page est dédiée à la fabrication de telles formules dans le système, et donnera des exemples de formules simples et complexes pour calculer un résultat automatiquement.

Localisation des formules


Les formules peuvent se trouver principalement à deux endroits :

  • La liste caractéristiques de l'administration, par exemple pour les nutriments, caractéristiques physico-chimiques...
  • La liste des caractéristiques dynamiques d'un produit / d'un modèle d'entité

Mais vous pouvez aussi les retrouvées sur :

  • Les règles d'étiquetage sur un produit / Modèle d'entité
  • Un fichier .spel lié à un modèle d'entité
  • Les Catalogue
  • Les exports de recherche
  • Les connecteurs

Contexte SPEL dans beCPG


Il existe plusieurs contextes dans lesquels les formules SPEL sont utilisées.

Formulation

La formulation est le contexte principal dans lequel les formules SPEL sont utilisées, il est important de comprendre ce qu'est la chaîne de formulation et quand les formules sont déclenchées. L'objectif principal de la chaîne de formulation est de charger une entité à partir de modèles alfresco et d'appliquer plusieurs transformations sur cette entité, à la fin de laquelle l'objet entité est sauvegardé.

Il existe plusieurs chaînes de formulation, une pour un projet et une pour un produit par exemple. Sur la chaîne de formulation du produit, nous chargeons un objet JAVA ProductData hérité (RawMaterialData/ SemiFinishedProductData/ FinishedProductData), puis nous appliquons la chaîne de formulation correspondante. A la fin, les données du produit sont sauvegardées.

Important : à la fin de la formulation, le moteur peut décider de sauvegarder les ProductData uniquement en mémoire (c'est le cas pour la simulation OM par exemple). Il faut donc veiller à ne pas sauvegarder les données en cours de processus, c'est pourquoi il est important de ne manipuler les ProductData que pendant la formule SPEL.

Le contexte de la formule est le point d'entrée demandé dans la plupart des fonctions SPEL. C'est l'élément sur lequel va se passer l'application de la formule. Ce point d'entrée est implicite lors d'une formule effectuée sur un produit (sur une caractéristique dynamique ou une caractéristique d'une data list comme les nutriments), mais est l'élément fourni dans la formule lors de l'usage des fonctions types somme, avg, etc. décrites plus loin.

  • #this retourne l'entité courante.
  • entity retourne le type ProductData associé au context de la formule.
  • dataListItem renvoie la ligne d'une dataList, par exemple une ligne de la composition avec ses propriétés de quantité MeO, de freinte, de variante..
  • dataListItemEntity renvoie l'entité (type ProductData) associé à une ligne d'une dataList, par exemple la Matière Première correspondant à la ligne dans une composition
  • variantData renvoie la variante liée à une ligne de la liste de composition, emballages etc.
  • compoList[0].product renvoie le nodeRef de l'entité de la première ligne de la liste composition

Contexte formulation :

Context variable Description
#root entité formulée

Contexte de la formulation des colonnes de composition/emballage/process :

Context variable Description
entity entité formulée
dataListItem ligne d'une liste compoList/packagingList/processList de l'entité formulé
dataListItemEntity entité associé dans une liste (compoListProduct/packagingListProduct/ resourceProduct)

Cost/Nut/Physico/Claim formulation context

Context variable Description
entity entité formulée
dataListItem ligne d'une liste compoList/packagingList/processList de l'entité formulé

Contexte d'étiquetage

Sur les règles de préférance ou render:

Context variable Description
#root Objet JAVA LabelingFormulaContext
entity entité formulée

Règles de filtrage dans l'étiquetage :

Context variable Description
#root Objet JAVA LabelingFormulaContext
compoListDataItem Objet JAVA CompoListItem sur lequel tester le filtre
ingListDataItem Objet JAVA IngListItem sur lequel tester le filtre
ingTypeItem Objet JAVA IngTypeItem sur lequel tester le filtre
entity entité formulée
dataListItem Alias de compoListDataItem
isClaimed(claimNodeRef) Vérifier la présence d'une allégation sur l'ingrédient ou le produit

Contexte Catalogue

Même contexte que la formulation

Contexte du rapport JXLS

Même contexte que la formulation

Contexte de l'export recherche

Context variable Description
#root Objet JAVA DefaultExcelReportSearchPlugin.FormulaContext
prop Tableau des propriétés extraites, par exemple props[« bcpg:code »]

Pas d'accès à l'entité ni au fonction spel personnalisée

Contexte connecteur

Context variable Description
#root Objet JAVA StandardCSVVisitor.FormulaContext
prop Tableau des propriétés extraites, par exemple props[« bcpg:code »]

Pas d'accès à l'entité ni au fonction spel personnalisée

Spel Language


Le Spring Expression Language (SpEL) est un langage d'expression puissant qui permet d'interroger et de manipuler des objets. Plusieurs opérateurs sont disponibles.

Type Opérateurs
Arithmetique +, -, *, /, %, ^, div, mod
Relationel <, >, ==, !=, <=, >=, lt, gt, eq, ne, le, ge
Logique and, or, not, &&,
Conditionel ?:
Regex matches

Les Opérateurs

Opérateurs Arithmétiques

Tous les opérateurs arithmétiques de base sont pris en charge.

Formule Résultat
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

Les opérations de division et de modulo ont des alias alphabétiques, div pour / et mod pour %. L'opérateur + peut également être utilisé pour concaténer des chaînes de caractères.

Opérateurs relationnels

Toutes les opérations relationnelles de base sont également prises en charge.

Formule Résultat
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

Tous les opérateurs relationnels ont également des alias alphabétiques. Par exemple, dans les configurations basées sur XML, nous ne pouvons pas utiliser les opérateurs contenant des crochets (<, <=, >, >=). À la place, nous pouvons utiliser lt (inférieur à), le (inférieur ou égal), gt (supérieur à) ou ge (supérieur ou égal).

Opérateurs logiques

SpEL prend en charge toutes les opérations logiques de base :

Formule Résultat
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

Comme pour les opérateurs arithmétiques et relationnels, tous les opérateurs logiques ont également des clones alphabétiques.

Opérateurs conditionnels ou ternaire

Les opérateurs conditionnels sont utilisés pour injecter des valeurs différentes en fonction de certaines conditions :

"Test" ? "valeur si test vrai" : "valeur si test faux"

L'opérateur ternaire est utilisé pour effectuer une logique conditionnelle compacte de type if-then-else à l'intérieur de l'expression. Dans cet exemple, nous avons essayé de vérifier si c'était vrai ou non.

ex: 2 == 1 ? true : false // false

Une autre utilisation courante de l'opérateur ternaire consiste à vérifier si une variable est nulle, puis à renvoyer la valeur de la variable ou une valeur par défaut :

 ex : qty != null ? qty : recipeQtyUsed

L'opérateur Elvis est un moyen de raccourcir la syntaxe de l'opérateur ternaire pour le cas ci-dessus utilisé dans le langage Groovy. Il est également disponible dans SpEL:

ex : qty ?:0d > Injecte 0 si qty est null

Utilisation Regex

L'opérateur de correspondance peut être utilisé pour vérifier si une chaîne de caractères correspond ou non à une expression régulière donnée. | Formule | Résultat | | --------------------------------------------------------- | -------- | | '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

Les variables peuvent être référencées dans l'expression à l'aide de la syntaxe #variableName. Les variables sont définies à l'aide du mot-clé var.

var myVar = "This is a test";
#myVar // This is a test

Les variables #this et #root

La variable #this est toujours définie et fait référence à l'objet d'évaluation actuel (par rapport auquel les références non qualifiées sont résolues). La variable #root est toujours définie et fait référence à l'objet contexte racine. Bien que #this puisse varier au fur et à mesure de l'évaluation des composants d'une expression, #root fait toujours référence à la racine.

Accès aux champs Java

Avec l'aide de SpEL, nous pouvons accéder au contenu de n'importe quel champs Java dans le contexte de la formulation. Par exemple, dans le cas de la formulation d'un produit, nous avons accès à l'objet ProductData :

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;

}

Pour accéder aux champs java dans SpEL, il suffit d'indiquer le nom de la variable :

Formule Résultat
title "nom du title"
erpCode = "0001" 0001
clients[0] nodeRef
compoListView.compoList[0] CompoListDataItem
utList.?[nut.toString() == 'nodeRef'].value Nut list value

Vous pouvez également appeler n'importe quelle méthode disponible dans l'objet ProductData :

isLiquid()
isRawMaterial()
isPackaging()
isPackagingKit()
isSemiFinished()
isLocalSemiFinished()

Sélection de la collection

La sélection est une fonctionnalité puissante du SPEL qui vous permet de transformer une collection de sources en une autre en sélectionnant ses entrées.

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']

La sélection utilise la syntaxe . ?[selectionExpression]. Elle filtre la collection et renvoie une nouvelle collection contenant un sous-ensemble des éléments originaux. Par exemple, la sélection nous permet d'obtenir facilement une liste de CostList d'un élément de la composition :

Formule Résultat
costList.?[cost == 'Cost MP'] Tableau de coûts où coût = 'Cost MP'
costList.?[value<27] Tableau des coûts dont la valeur est < 27

En plus de renvoyer tous les éléments sélectionnés, il est possible de ne récupérer que la première ou la dernière valeur. Pour obtenir la première entrée correspondant à la sélection, la syntaxe est ^[...] tandis que pour obtenir la dernière sélection correspondante, la syntaxe est $[...].

Projection de la collection

La projection permet à une collection de piloter l'évaluation d'une sous-expression et le résultat est une nouvelle collection. La syntaxe de la projection est ![projectionExpression]. L'exemple le plus facile à comprendre est le suivant : supposons que nous ayons une liste de coûts mais que nous souhaitions obtenir la liste des valeurs des coûts. En fait, nous voulons évaluer « valeur » pour chaque entrée de la liste des coûts. Utilisation de la projection :

Formule Résultat
costList.![value] returns [ '5', '25' ]

L'éditeur de formules


L'éditeur de formules se présente sous la forme d'une grande zone de texte, dont les lignes sont numérotées, ainsi que de deux tableaux en dessous.

Généralités sur la structure d'une formule

De manière générale, une formule SPEL va renvoyer un seul résultat, qui sera la dernière expression évaluée. Cela signifie qu'il est possible d'utiliser des variables intermédiaires au sein des calculs, à condition de simplement appeler la variable contenant le résultat final à la fin de la formule.

Par exemple la formule suivante renverra le résultat stocké dans la variable result

    var result = qty;
    var yieldCoeff = (yield/100);
    #result = #result / #yieldCoeff;
    #result;

Cependant, la plupart des formules n'auront qu'une seule expression, et l'utilisation de variables intermédiaires ne se justifiera pas, ainsi la formule précédente peut être raccourcie en :

    qty / (yield/100)

L'une des limitations principales du langage SPEL réside dans l'absence de blocs pour créer des conditions if ... else, ce qui force l'utilisateur à utiliser des structures ternaires qui vont rapidement s'accumuler et rendre la relecture de la formule fastidieuse.

Exemple avec la formule de calcul de la quantité de sel en fonction de la quantité de sodium :

    nut['Sodium'].value == null ? 0d : nut['Sodium'].value * 2.5 / 1000

(A noter qu'ici Sodium est en fait le nodeRef de la caractéristique Sodium, et pas juste la chaîne de caractères "Sodium")

Pour ne pas que notre formule ne donne d'erreur si la caractéristique Sodium n'est pas renseignée, il faut auparavant vérifier qu'elle est bien saisie, auquel cas on effectue le calcul, et sinon on renvoie une valeur par défaut, 0.

La plupart des accès en lecture à des propriétés ou des valeurs de calculs devraient comporter une valeur par défaut, au cas où la valeur n'est pas présente. Cette valeur doit être suffixée par un d (ou D) pour indiquer au système qu'il s'agit d'un nombre à virgule, et pas un caractère (exemple : 1d va représenter le chiffre 1 à virgule, alors que 1 peut représenter le chiffre 1 en entier, ou bien la chaîne de caractères "1").

Les formules faisant des sommes ou multiplications se satisfont d'avoir 0d comme valeur par défaut, alors que les divisions nécessitent 1d, et des formules renvoyant du texte (par exemple pour afficher si un produit rentre dans une catégorie) prendrons "" comme valeur par défaut.

Ces valeurs par défaut seront donc les dernières valeurs lors de longues expressions avec de multiples conditions (si ce n'est pas 1 alors si ce n'est pas 2 alors ... alors 0d)

Helpers SPEL

Le raccourci CTRL + Espace ouvre un menu contextuel d'aide, permettant d'accéder rapidement à des objet et des champs java .

Caractéristiques dynamiques

Les caractéristiques dynamiques peuvent être effectuées sur le produit formulé, ou bien sur chaque ligne de la liste liée. Chaque liste ayant des composants (Composition, Emballages, Process) dispose de caractéristiques dynamiques distinctes.

Pour qu'une formule soit exécutée sur chacun des composants, il suffit de choisir une colonne. Ensuite, le résultat sera affiché dans la nouvelle colonne ajoutée à la liste.

ex : la caractéristique dynamique "Coût (€)" est calculée sur chaque ligne de la composition et s'affiche directement dessus.

Caractéristiques

Les tableaux sous la zone de texte vont permettre à l'utilisateur de sélectionner rapidement des portions de formules à insérer dans la zone de texte, à l'endroit où le curseur se trouve.

Le tableau de gauche affiche les propriétés relatives au type d'entité sélectionné au dessus (sur la capture, le type est Produit Fini, mais il peut s'agir de semi finis, de nutriments, d'ingrédients, coûts..), ou bien des caractéristiques si le type choisi est une liste de caractéristiques (comme les nutriments).

Le tableau de droite va, si l'utilisateur a sélectionné une caractéristique dans le tableau de gauche (voir figure suivante), afficher les propriétés relatives à cette caractéristique.

Avancé


Fonctions personnalisées de beCPG Spel

beCPG définit un ensemble de fonctions accessibles à partir de la formule SPEL.

La syntaxe pour utiliser cette fonction est @beCPG.function

Fonction Description
propValue Affiche une propriété
setValue Attribuer une valeur à une propriété
assocValue Affiche la valeur nodeRef d'une association @
assocValues Affiche les valeurs d'une association multiple dans un tableau
setAssocs Attribuer une ou plusieurs valeurs à une association
assocPropValues Affiche les propriétés des associations extraites
assocAssocValues Affiche les associations à partir des associations extraites
propMLValue Extrait une propriété multilingue dans la locale spécifiée ou toutes les traductions
updateMLText Attribuer une valeur locale à un champ multilingue
copy Copier des associations et/ou des propriétés d'une source vers le produit actuel
applyFormulaToList A Appliquer une formule à une liste, comme une colonne dynamique
filter Sert à filtrer. Est généralement combinée avec d'autres formules, comme min, max, avg ou sum
max Permet de trouver la valeur maximale d'un tableau
min Permet de trouver la valeur minimun d'un tableau
avg Permet de trouver la valeur moyenne d'un tableau
sum Permet d'additionner les valeurs d'un tableau
children Utilisé pour obtenir les enfants de entity, utilisé pour descendre dans les niveaux de composition.
findOne Utilisé pour obtenir le nodeRef de l'objet
sourcesAssocValues Affiche le tableau des cas d'utilisation de nodeRef, sur la base d'une association cible
formatNumber Pour les nombres, il est possible de préciser le nombre de decimale
formatDate Pour les date, il est possible de préciser le format

Attention, si des champs java sont disponibles, les fonctions de type propValue, assocValue, setValue, assocValue, assocValues ne fonctionneront pas ex: @beCPG.propValue(#this,"bcpg:productQty") est équivalent au champs java "qty" Pour plus d'information sur les champs java Cliquez ici

Exemple

  • propValue : Permet d'accéder à une propriété.

on veut extraire le champs custom:name de l'entité avec une caractéristique dynamique fixe

@beCPG.propValue(#this,"custom:name")

on veut extraire le champs custom:code des entités d'une liste avec une caractéristique en colonne

@beCPG.propValue(dataListItemEntity,"custom:code")

on veut extraire la valeur d'une famille hiérarchique (d:noderef). Il faut, dans ce cas, faire deux fois propValue :

@beCPG.propValue(@beCPG.propValue(#this,"custom:productHierarchy1"),"bcpg:lkvValue")
  • setValue : Permet d'attribuer une valeur à un champ. on veut attribuer la valeur "kg" à custom:productUnit:
@beCPG.setValue(#this,"custom:productUnit","kg")

on veut attribuer la valeur de custom:description au champ custom:legalName:

@beCPG.setValue(#this,"custom:legalName",@beCPG.propValue(#this,"custom:description"))
  • assocValue : Renvoie la valeur (nodeRef) d'une association.

on veut extraire l'association "bcpg:trademarkRef" du produit:

@beCPG.assocValue(#this,"bcpg:trademarkRef")
  • assocValues : Renvoie les valeurs (tableau contenant des nodeRef) d'une association multiple.

on veut extraire l'assocation "bcpg:productGeoOrigin" du produit

@beCPG.assocValues(#this,"bcpg:productGeoOrigin")

A noter que extraire le premier élément du tableau avec:

@beCPG.assocValues(#this,"bcpg:productGeoOrigin")[0]

Est similaire à

@beCPG.assocValue(#this,"bcpg:productGeoOrigin")
  • setAssocs : Permet d'attribuer une valeur à une assoc ou plusieurs.

on veut attribuer une usine de nodeRef "workspace://SpacesStore/169a0cf8-2f0a-4011-9b19-dea14676e136" à l'association "custom:plant" :

@beCPG.setAssocs(#this,"custom:plants", "workspace://SpacesStore/169a0cf8-2f0a-4011-9b19-dea14676e136")

on veut ajouter le client de nodeRef "workspace://SpacesStore/844aa160-f344-43be-a659-434d319e584f" à l'association "bcpg:clients" :

var clients = @beCPG.assocValues(#this,"bcpg:clients");
#clients.add("workspace://SpacesStore/844aa160-f344-43be-a659-434d319e584f");
@beCPG.setAssocs(#this,"bcpg:clients", #clients)
  • assocPropValues : Extrait les propriétés d'une association.

on veut extraire les cm:title des clients :

@beCPG.assocPropValues(#this,"bcpg:client","cm:title")
  • assocAssocValues : Extrait les associations d'une association.pace://SpacesStore/d49d02aa-3443-48f8-b6ee-9e66fe2ba

on veut extraire les bcpg:plantCertifications des bcpg:plant du produit :

@beCPG.assocAssocValues(#this,"bcpg:plant","bcpg:plantCertifications")
  • propMLValue : Extrait les propriétés multi langue dans une langue spécifiée.

on veut extraire le bcpg:legalName en anglais US:

@beCPG.propMLValue(#this,"bcpg:legalName","en_US")

on veut extraire toutes les traduction dee bcpg:legalName :

@beCPG.propMLValue(#this,"bcpg:legalName",null)
  • updateMLText : Permet d'attribuer une valeur à un champ multilingue dans une certaine langue.

on veut extraire attribuer la valeur "Finish Legal Name" au legalName en finois:

@beCPG.updateMLText(legalName,"fi","Finish Legal Name")

Attention : cela marche seulement pour les champs multilangues présents dans les helpers

  • copy : Permet de copier des associations et/ou des listes d'une source vers le produit courant.
    @beCPG.copy(sourceNodeRef, assocToCopyList, listToCopyList)
    

on veut faire remonter les associtations "bcpg:trademarkRef","bcpg:subsidiaryRef","bcpg:clients", et "bcpg:suppliers" du premier élément de la composition vers notre produit courant

@beCPG.copy(compoList[0].product,{"bcpg:trademarkRef","bcpg:subsidiaryRef","bcpg:clients","bcpg:suppliers"},"")

on veut copier la liste microbiologique du critère microbiologique vers le produit:

var productMicrobioCriteriaRef = @beCPG.assocValue(#this,"bcpg:productMicrobioCriteriaRef");
@beCPG.copy(#productMicrobioCriteriaRef,"",{"bcpg:microbioList"})

on veut copier des listes et propriétés depuis l'entité parente:

@beCPG.copy(parentEntity,{"bcpg:legalName", "bcpg:productHierarchy1", "bcpg:erpCode"},{"bcpg:compoList","bcpg:packagingList","mpm:processList"})
  • applyFormulaToList : Permet d'appliquer une formule à une liste comme une colonne dynamique.

on veut mettre les valeurs des lignes de la composition à 0 :

@beCPG.applyFormulaToList(getCompoList(),"@beCPG.setValue(dataListItem,'bcpg:compoListQtySubFormula', 0d)")

on veut forcer la méthode de la nutList à 'Formulation' :

@beCPG.applyFormulaToList(nutList,"@beCPG.setValue(dataListItem,'bcpg:nutListMethod', 'Formulation')")

Cela fonctionne pour la plupart des listes standards, comme:

  • getCompoList() ( ou compoList )
  • getPackagingList() ( ou packagingList )
  • getProcessList() ( ou processList )
  • getControlDefList() ( ou controlDefList)
  • priceList
  • costList
  • reqCtrlList
  • filter : Permet de filtrer. Il est généralement combiné à d'autres formules, comme le min, max ou sum.

on veut les éléments de la reqCtrlList ayant une valeur reqMaxQty et n'étant pas du type info

@beCPG.filter(reqCtrlList, "reqMaxQty!=null && reqType.toString() !='Info'")

on veut extraire les éléments de la liste composition de la variante principale ayant une qty supérieure à 0,5kg:

@beCPG.filter(getCompoList(new fr.becpg.repo.variant.filters.VariantFilters(true)), "dataListItem.qty > 0.5")
  • max : Permet de trouver le max d'un tableau de nombre :
@beCPG.max($arrayOfDouble)

on veut trouver le reqMaxQty maximum de la liste reqCtrlList

@beCPG.max(reqCtrlList.![reqMaxQty])

on veut trouver le qty maximum de la liste compoList:

@beCPG.max(compoList.![qty])
  • min : Permet de trouver le min d'un tableau de nombre :
@beCPG.min($arrayOfDouble)

on veut le minimum parmi les éléments de la reqCtrlList ayant une valeur reqMaxQty et n'étant pas du type info.

@beCPG.min(@beCPG.filter(reqCtrlList, "reqMaxQty!=null && reqType.toString() !='Info'").![reqMaxQty])
  • avg Permet de trouver la moyenne d'un tableau de nombre :
@beCPG.avg($arrayOfDouble)
  • sum Permet de sommer des éléments.
@beCPG.sum(range, formula)

Le premier argument, range, sera une liste (un tableau) d'entités, auquel sur chacune d'elle la formule formula du second argument sera appliquée.

Il peut être par exemple utile d'avoir la somme des quantités dans la composition du produit.

@beCPG.sum(getCompoList(new fr.becpg.repo.variant.filters.VariantFilters(true)),dataListItem.qty)

On ajoute en plus un filtre à la composition, qui permet de n'obtenir que les éléments qui ont la variante par défaut. Cependant, on pourrait aller plus loin, et ne prendre les quantités que des composants qui ne sont pas des semi-finis locaux.

@beCPG.sum(
    @beCPG.filter(
        getCompoList(new fr.becpg.repo.variant.filters.VariantFilters(true)), 
        !(dataListItemEntity instanceof T(fr.becpg.repo.product.data.LocalSemiFinishedProductData))
    ),
    dataListItem.qty
)

Les fonctions de isLiquid() à isLocalSemiFinished() sont des raccourcis pour ne pas utiliser le instanceof vu dans la dernière formule.

  • children Permet d'obtenir les composants fils de l'entité entity, utilisés pour descendre dans les différents niveaux de composition.

    @beCPG.children(entity)
    
  • findOne Permet d'obtenir l'entité désignée par la chaîne de caractère nodeRef

@beCPG.findOne(nodeRef)
  • sourcesAssocValues Permet d'obtenir le tableau des nodeRef des cas d'emplois en se basant sur une association.

on veut la liste des nodeRef des projets utilisant mon produit actuel:

@beCPG.sourcesAssocValues(#this,"pjt:projectEntity");

on veut le nombre de produit utilisant dans leur composition mon produit actuel

var sources = @beCPG.sourcesAssocValues(#this,"bcpg:compoListProduct");
var size = #sources.size();
#size;
  • updateDate Permet de modifier un champ date en ajouter un nombre de jour / semaine / mois / année :
@beCPG.updateDate($date, $field, $amount )

on enlève 5 jours à la date du jour :

@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 )

on ajoute 3 mois à la date de modification :

@beCPG.updateDate(@beCPG.propValue(#this, "cm:modified"), T(java.util.Calendar).MONTH, 3 )
  • formatNumber : il est possible de préciser le format pour les champs numériques

on veut afficher le nutriment protéine avec 2 chiffre significatif

@beCPG.formatNumber(nut['workspace://SpacesStore/d49d02aa-3443-48f8-b6ee-9e66fe2ba4c4']?.value, "0.##")
  • formatDate : il est possible de préciser le format des dates.

on veut visualiser le mois de la dernière formulation du produit

@beCPG.formatDate(formulatedDate,"MM/YYYY")
@beCPG.formatDate(formulatedDate,"dd/MM/YYYY")

Format des dates

Nutriscore formula (beCPG 4.0)

Pour extraire les détails du nutriscore, il faut intiailiser le contexte comme variante :

var context = T(fr.becpg.repo.product.formulation.score.NutriScoreContext).parse(nutrientDetails);

Puis, nous pouvons extraire le résumé :

#context.toDisplayValue();

Nous pouvons aussi extraire les détails de chaque nutriment:

  • getNutriScore()
  • getEnergy()
  • getSatFat()
  • getTotalFat()
  • getTotalSugar()
  • getSodium()
  • getPercFruitsAndVetgs()
  • getNspFibre()
  • getAoacFibre()
  • getProtein()

Nous pouvons extraire les détails du nutriscore:

  • getCategory()
  • getNutrientClass()
  • getClassLowerValue()
  • getClassUpperValue()
  • getAScore()
  • getCScore()

Nous pouvons choisir les valeurs à extraire:

  • getLowerValue()
  • getUpperValue()
  • getScore()
  • getValue()

Par exemple:

#context.getProtein().getValue();
#context.getTotalSugar().getLowerValue();
#context.getAScore();
#context.getClassUpperValue();

Accès au type java

L'opérateur spécial 'T' peut être utilisé pour spécifier une instance de java.lang.Class (le « type »). Les méthodes statiques sont également invoquées à l'aide de cet opérateur.

T(java.util.Date)
T(String)
T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR

Constructeurs

Les constructeurs peuvent être invoqués à l'aide de l'opérateur new. Le nom de classe pleinement qualifié doit être utilisé pour tous les types, à l'exception du type primitif et de String (où int, float, etc. peuvent être utilisés).

Modèle d'expression

Essentiellement utilisé dans l'étiquetage de la formule Render. Les modèles d'expression permettent de mélanger du texte littéral avec un ou plusieurs blocs d'évaluation. Chaque bloc d'évaluation est délimité par des caractères préfixes et suffixes que vous pouvez définir, un choix courant étant d'utiliser #{ } comme délimiteurs. Un choix courant est d'utiliser #{ } comme délimiteurs. Par exemple,

"random number is #{T(java.lang.Math).random()}"

Listes en ligne, cartes et construction de tableaux

Les listes peuvent être exprimées directement dans une expression en utilisant la notation {}. Les cartes peuvent également être exprimées directement dans une expression en utilisant la notation {clé:valeur}. Les tableaux peuvent être construits en utilisant la syntaxe Java familière, en fournissant éventuellement un initialisateur pour que le tableau soit peuplé au moment de la construction.

"{1,2,3,4}" // List construction
"{name:'Nikola',dob:'10-July-1856'}" // Map construction
"new int[]{1,2,3}" // Array construction

Exemples de formules


  • Calculer la somme des pourcentages de blé et de farine de blé (visibles dans la liste des ingrédients) dans le produit. A noter que 'Blé' correspond en fait au nodeRef de l'ingrédient blé, qui est remplacé à l'affichage par son nom.
    (ingList.^[ing.toString() == 'Blé']?.qtyPerc != null ? ingList.^[ing.toString() == 'Blé']?.qtyPerc : 0d) +
    (ingList.^[ing.toString() == 'Farine de blé']?.qtyPerc != null ? ingList.^[ing.toString() == 'Blé']?.qtyPerc : 0d)
  • Calcule la somme de la colonne dynamique 2, pour les composants n'étant pas des produits semi-finis locaux
@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')")
  • Avoir la date du jour au format dd/MM/yyyy.

    new java.text.SimpleDateFormat("dd/MM/yyyy").format(new java.util.Date())
    
  • Avoir un format personnalisé sur des nombres.

    new java.text.DecimalFormat("0.#").format(nut['Protéine']?.value)
    
  • Changer le texte "to replace" en "new text" sur le champ cm:description.
@beCPG.propValue($nodeRef,"cm:description").replaceAll("toreplace","new text")
  • Savoir si une allégation est renvendiqué
entity.labelClaim['Kasher']?.isClaimed
  • Extrait le texte du champ cm:name après la première occurence de la chaîne de caractère "test".
var productName = name;
#productName.substring(#productName.indexOf("test"));
  • Tester si la chaîne de caractère initiale et renvoie true / false.
@beCPG.propValue(#this,"cm:name").startsWith("X");

results matching ""

    No results matching ""