WebHooks Surcharges
- 1 Surcharge des définitions
- 1.1 Squelette de la surcharge
- 1.2 Liste des champs à exclure pour affichage de type étendu
- 1.3 Liste des champs pour affichage de type simple
- 1.4 Liste des tables liées pour affichage de type étendu relationnel
- 1.5 Liste des tables autorisées pour mise à jour additionnelle
- 1.6 Liste des tables pour suppression en cascade
- 1.7 Champs
- 1.7.1 Syntaxe
- 1.7.2 Description syntaxe
- 1.7.3 Exemples impactant la structure du json
- 1.7.4 Exemples impactant le réponse dans le bloc "data": [...]
- 1.7.5 Exemples impactant le réponse dans le bloc "fieldList": [...]
- 1.7.6 Exemples impactant l'ajout et la mise à jour
- 1.7.7 Exemples impactant la définition
- 1.8 Filtres
- 1.8.1 Syntaxe
- 1.8.2 Description syntaxe
- 1.8.3 Exemples
- 1.9 Tris
- 1.9.1 Syntaxe
- 1.9.2 Description syntaxe
- 1.9.3 Exemples
- 2 Surcharge des règles métier
- 3 Surcharge avant exécution webhook
- 4 Surcharge après exécution webhook
Surcharge des définitions
Correspond à la définition du webhook.
Toutes les surcharges seront dans les scripts « WebhookDefinitionOverload_xxxx ».
Pour le mode LIST et GET, permet :
d’exclure des champs que l’on ne veut jamais retourner.
de définir la valeur à retourner dans le champ
Pour le mode LIST, permet :
de définir des filtres spécifiques
de définir des tris spécifiques
Pour le mode PUT, permet :
de donner des noms plus explicites au champs à renseigner
de rendre obligatoire un champ et sa valeur
de ne pas insérer/mettre à jour la valeur d'un champ
de définir un champ comme obsolète
Pour le mode DEFINITION, permet :
de lister les valeurs d'un champ de type foreign
d'ajouter des champs additionnels en plus du code et du libellé
Par défaut, tous les champs sont présents dans le webhook, donc également les champs surchargés.
Squelette de la surcharge
WebhookDefinitionOverload_xxx
// WebhookDefinitionOverload_xxx
// @Description => Overload webhook definition
languageCode = VARGET_SHELL( "languageCode", "WebhookDefinition" )
specificOverload = TRANSLATE( "
{
'extendedFieldsListToExclude': '',
'simpleFieldsList': '',
'relatedTablesCount': '',
'relatedTables': [],
'authorizedTables': [],
'cascadeDeleteTables': [],
'fieldsList': {
},
strictFilters: [
],
'strictOrderBy': [
],
'outOfMainTableFields': [
]
}
" )
RETURN( "<varscript>specificOverload</varscript>" )
Liste des champs à exclure pour affichage de type étendu
Permet de lister les champs étendus à exclure pour les webhooks en mode LIST et GET (par défaut on retourne tous les champs).
Syntaxe
'extendedFieldsListToExclude': 'field1,field2,field3,...'
Exemple
Je ne veux pas retourner les champs serialnumber et location
'extendedFieldsListToExclude': 'serialnumber,location'
Liste des champs pour affichage de type simple
Permet de lister les champs pour le type simple (resultType=simple) pour les webhooks en mode LIST et GET.
Par défaut en standard sont listés les champs jugés comme essentiels.
Dans la surcharge il faudra reprendre les champs du standard si vous voulez les conserver, complété par vos champs.
Cette liste devra contenir au minimum deux champs : généralement le code et le libellé.
Cette propriété n’est pas présente par défaut dans le squelette de la surcharge, il faudra l’ajouter manuellement.
Syntaxe
'simpleFieldsList': 'field1,field2,field3,...'
Exemple
Le standard liste les champs codecustomer, name et postalCode
La liste du standard ne me convient pas et je veux afficher les champs codecustomer, name, city et final_pricecategory_ID_tarCod.
'simpleFieldsList': 'codecustomer,name,city,final_pricecategory_ID_tarCod'
Liste des tables liées pour affichage de type étendu relationnel
Permet de lister les champs de type foreignkey dont on veut afficher les valeurs de la table liée pour le type étendu relationnel (resultType=extendedRelated) pour les webhooks en mode GET.
Dans la surcharge il faudra reprendre la liste des champs du standard si vous voulez les conserver, complété par vos champs.
Il faudra également que la fonction de définition de la table liée correspondante existe (WebhookDefinition_xxxx) ainsi que sa surcharge (WebhookDefinitionOverload_xxxx).
Cette propriété n’est pas présente par défaut dans le squelette de la surcharge, il faudra l’ajouter manuellement.
Il faudra également fournir le nombre de tables liées.
Syntaxe
'relatedTablesCount': xx,
'relatedTables': ['fieldFK1', 'fieldFK2', '...']
Exemple
Le standard liste les champs generictype_ID_customerFamily, baseuser_ID et generictype_ID_country.
Je veux compléter cette liste pour afficher les valeurs de la table liée au champ final_pricecategory_ID_tarCod.
'relatedTablesCount': 4,
'relatedTables': ['generictype_ID_customerFamily', 'baseuser_ID', 'generictype_ID_country', 'final_pricecategory_ID_tarCod']
La liste proposée par le standard ne me convient pas et je ne veux afficher que les valeurs liées pour generictype_ID_customerFamily complété par le champ final_pricecategory_ID_tarCod.
'relatedTablesCount': 2,
'relatedTables': ['generictype_ID_customerFamily', 'final_pricecategory_ID_tarCod']
Liste des tables autorisées pour mise à jour additionnelle
Permet de lister les tables autorisées en mise à jour additionnelle pour les webhooks en mode PUT.
Dans la surcharge il faudra reprendre la liste des tables autorisées du standard si vous voulez les conserver, complété par vos tables.
Il faudra également que la fonction de définition de la table autorisée correspondante existe (WebhookDefinition_xxxx) ainsi que sa surcharge (WebhookDefinitionOverload_xxxx).
Cette propriété n’est pas présente par défaut dans le squelette de la surcharge, il faudra l’ajouter manuellement.
Syntaxe
'authorizedTables': [{'tableName':'mainTableName'}, {'tableName':'additionalTableName1'}, {'tableName':'additionalTableName2'}, {'tableName':'....'}]
Exemple
Pour le webhook des interventions, le standard autorise les table suivantes : intervention, interventionequipment et interventionpart.
Je veux compléter cette liste pour gérer la table des opérations interventionoperationrange.
'authorizedTables': [{'tableName':'intervention'}, {'tableName':'interventionequipment'}, {'tableName':'interventionpart'}, {'tableName':'interventionoperationrange'}]
Liste des tables pour suppression en cascade
Permet de lister les tables qui peuvent être supprimées en cascade pour les webhooks en mode DELETE.
Dans la surcharge il faudra reprendre la liste des tables du standard si vous voulez les conserver, complété par vos tables.
Cette propriété n’est pas présente par défaut dans le squelette de la surcharge, il faudra l’ajouter manuellement.
Attention, on ne permet qu’un seul niveau de suppression en cascade.
La suppression d’un enregistrement d’une table principale permet de supprimer en cascade un enregistrement d’une sous-table. Mais si l’enregistrement de cette sous-table est utiilisé dans une autre table, la suppression sera interdite.
Syntaxe
'cascadeDeleteTables': [{'tableName':'tableName1'}, {'tableName':'tableName2'}, {'tableName':'...'}]
Exemple
Pour le webhook des interventions, le standard permet la suppression en cascade des tables suivantes : interventionequipment et interventionpart.
Je veux compléter cette liste pour gérer la table des opérations interventionoperationrange.
'cascadeDeleteTables': [{'tableName':'interventionequipment'}, {'tableName':'interventionpart'}, {'tableName':'interventionoperationrange'}],
Champs
Permet de lister des champs qui demandent un traitement spécial pour être affichés en mode LIST et GET ou pour la mise à jour en mode PUT. Par défaut pour les clés étrangères spécifiques on retourne toujours le code de la table étrangère. Il n’y a donc pas besoin de traitement spécifique pour ces cas si le code suffit.
Syntaxe
'fieldsList': {
'field1': {
'type': '…',
'length': '…',
'label': '…',
'targetTableName': '…',
'targetFieldName': '…',
'fieldNameToShow': '…',
'alias': '…',
'mandatory': 0 or 1,
'cannotBeNull': 0 or 1,
'canGet': 0 or 1,
'canInsert': 0 or 1,
'canUpdate': 0 or 1,
'deprecated': 0 or 1,
'foreignParent': '…'
},
'field2': {
'type': '…',
'length': '…',
'label': '…',
'targetTableName': '…',
'targetFieldName': '…',
'fieldNameToShow': '…',
'alias': '…',
'mandatory': 0 or 1,
'cannotBeNull': 0 or 1,
'canGet': 0 or 1,
'canInsert': 0 or 1,
'canUpdate': 0 or 1,
'deprecated': 0 or 1,
'foreignParent': '…'
},
'field3': {
'type': '…',
'length': '…',
'label': '…',
'targetTableName': '…',
'targetFieldName': '…',
'fieldNameToShow': '…',
'alias': '…',
'mandatory': 0 or 1,
'cannotBeNull': 0 or 1,
'canGet': 0 or 1,
'canInsert': 0 or 1,
'canUpdate': 0 or 1,
'deprecated': 0 or 1,
'foreignParent': '…'
}
…
},
'outOfMainTableFields': [
{
'fieldName': '…',
'tableName': '…'
}
],
Description syntaxe
Propriété | Mode utilisation | Description |
---|---|---|
fieldsList | ||
type | LIST, GET | Type du champ dans le résultat (bloc "fieldList"). En standard on a : varchar, text, int, double, bool, datetime, timestamp, foreignkey, mediumblob. |
length | LIST, GET | Longueur du champ dans le résultat (bloc "fieldList)". En standard renseigné pour varchar, text. |
label | LIST, GET | Libellé du champ dans le résultat (bloc "fieldList)". En standard on retourne la clé de traduction en fonction du code langue du json. |
targetTableName | LIST, GET | Nom de la table liée pour les clés étrangères. A utiliser uniquement pour un champ spécifique qui pointe vers une table standard. |
targetFieldName | LIST, GET | Nom du champ à afficher pour les clés étrangères. |
fieldNameToShow | LIST, GET | Permet de changer le nom du champ en retour au lieu d’avoir le nom du champ en base de données. Ex : baseuser_ID -> Utilisateur du client |
alias | PUT | Permet de changer le du nom du champ à renseigner afin qu’il soit plus explicite. L’alias ne doit pas comporter d’espaces. Permet de se détacher de la base de données. Ex : city -> Ville |
mandatory | PUT | Permet de rendre un champ obligatoire dans le json. Valeurs possibles : 0, 1 (Par défaut on aura : 0 - Champ non obligatoire) |
cannotBeNull | PUT | Permet de rendre la valeur d'un champ obligatoire dans le json (c'est-à-dire que la valeur ne peut être à chaine vide ou null). Valeurs possibles : 0, 1 (Par défaut on aura : 0 - Valeur du champ non obligatoire) |
canGet | LIST, GET | Permet d'exclure un champ. Valeurs possibles : 0, 1 (Par défaut on aura : 1 - Champ retourné). |
canInsert | PUT | Permet de ne pas insérer la valeur d'un champ lors d'un ajout. Valeurs possibles : 0, 1 (Par défaut on aura : 1 - Insérer valeur) |
canUpdate | PUT | Permet de ne pas mettre à jour la valeur d'un champ lors d'une mise à jour. Valeurs possibles : 0, 1 (Par défaut on aura : 1 - Mettre à jour valeur) |
deprecated | PUT | Permet de ne plus prendre en compte un champ devenu obsolète. Il ne sera ni vérifié, ni inséré, ni mis à jour. Valeurs possibles : 0, 1 (Par défaut on aura : 0 - Non déprécié) |
checkForeign | PUT | Permet de ne pas contrôler la valeur d'une foreignkey. Valeurs possibles : 0, 1 (Par défaut on aura : 1 : Contrôler la foreignkey) |
canGetDefinitionList | DEFINITION | Permet de lister les valeurs d'une foreignkey. Valeurs possibles : 0, 1 (Par défaut on aura : 0 - Ne pas lister les valeurs) |
definitionLabelFieldName | DEFINITION | Permet de donner le champ à utiliser pour afficher le libellé dans la liste. (Si non renseigné on utilisera le champ "label" s'il existe dans la table). Possibilité d'utiliser des fonctions mysql pour par exemple concaténer le nom et le prénom dans la liste des utilisateurs : 'definitionLabelFieldName': 'CONCAT_WS(' ',lastName,firstName)' |
definitionFilter | DEFINITION | Permet de filtrer les enregistrements retournés. Ex pour ne pas afficher les ressources matérielles et les équipes dans la liste des utilisateurs : 'definitionFilter': 'resourcetype_ID IS NULL AND teamtype_ID IS NULL' |
definitionExtraFields | DEFINITION | Permet d'ajouter des champs additionnels en plus du code et du libellé |
dataType | DEFINITION, PUT | Permet de donner le type de data pour une foreignkey pointant vers une table generictype afin de filtrer et contrôler les données |
foreignParent | LIST, GET | Nom de la clé étrangère dans la table principale. Cas très particulier où on veut pointer vers une table de niveau +2. Sera combiné avec « outOfMainTableFields » Ex d’utilisation en standard : sw_data_intervention -> sw_datacustomeraddress -> sw_data_customer (ici « customeraddress_ID » pour notre exemple) Permet donc dans notre exemple de retourner une info de la table « sw_data_customer » |
outOfMainTableFields (utilisé uniquement en combinaison avec foreignParent) | ||
fieldname | LIST, GET | Nom de la clé étrangère dans la table de niveau +1 (ici « customer_ID » pour notre exemple) |
tableName | LIST, GET | Nom de la table liée de niveau +1 (ici « sw_data_customeraddress » pour notre exemple) |
Pour rappel en mode LIST et GET la réponse du webhook prend la forme ci-dessous :
"response":
{
"data":
[{ depends of the webhook}],
"fieldList":
[{
"entityName": "value", // name of the table (ie intervention)
"fields":
[{
"fieldName": "value", // name of the field (ie codeintervention, customer_ID, …)
"fieldType": "value", // type of the field : varchar, text, int, double, bool, datetime, timestamp, foreignkey
"fieldLength": "value", // length of the field : informed for varchar, text
"fieldLabel": "value" // label of the field : depends on the value of the languageCode from the header (default : FR)
},
{
"fieldName": "value",
"fieldType": "value",
"fieldLength": "value",
"fieldLabel": "value"
},
{
...
}],
"related":
[{
"entityName": "value",
"fields":
[{
...
}]
},
{
"entityName": "value",
"fields":
[{
...
}]
},
{
...
}]
}]
Exemples impactant la structure du json
J’ai un champ spécifique « Catégorie tarif » (final_pricecategory_ID_tarCod) que je veux rendre obligatoire dans le json et dont la valeur doit être renseignée :
'fieldsList': {
'final_pricecategory_ID_tarCod': {
'mandatory': 1,
'cannotBeNull': 1
}
}
-> Résultat :
Si le champ n'est pas présent dans le json :
"result": {
"common" : {
"resultCode": "1",
"errorMessage": "Data Error: final_pricecategory_ID_tarCod is required"
}
}
Si la valeur du champ n'est pas renseignée dans le json :
"result": {
"common" : {
"resultCode": "1",
"errorMessage": "Data Error: Value of final_pricecategory_ID_tarCod cannot be null"
}
}
Exemples impactant le réponse dans le bloc "data": [...]
Ce bloc contient la valeur des champs retournés.
Je ne veux pas retourner les champs serialnumber et location
On pourrait considérer que ceci est redondant avec 'extendedFieldsListToExclude', mais il s'agit ici d'une autre façon d'arriver à la même chose mais en plus il permet d'exclure les champs pour le 'resultType' en 'simple'.
'fieldsList': {
'serialnumber': {
'canGet': 0
},
'location': {
'canGet': 0
}
}
Pour les champs « codecustomer », « name » et « baseuser_ID », je veux afficher « Code du client », « Nom du client » et « Utilisateur lié au client » :
'fieldsList': {
'CodeCustomer': {
'fieldNameToShow': 'Code du client'
},
'name': {
'fieldNameToShow': 'Nom du client'
},
'baseuser_ID': {
'fieldNameToShow': 'Utilisateur lié au client'
}
}
-> Résultat :
Avant :
"customer": {
"codecustomer" : "C0000001",
"name": "ENTREPRISE DIJON GUICHARD"
"baseuser_ID" : "DUPREZ - Secteur Ouest GRO",
Après :
"customer": {
"Code du client" : "C0000001",
"Nom du client": "ENTREPRISE DIJON GUICHARD"
"Utilisateur lié au client" : "DUPREZ - Secteur Ouest GRO",
J’ai un champ standard « Utilisateur » (baseuser_ID) qui pointe vers une table standard (sw_data_baseuser) et qui affiche par défaut le « nom » (lastName) et je veux le changer pour afficher le « code » (CodeBaseuser) :
'fieldsList': {
'baseuser_ID': {
'targetFieldName': 'codebaseuser'
}
}
Si je veux afficher les « initiales » (initials) :
'fieldsList': {
'baseuser_ID': {
'targetFieldName': 'initials'
}
}
-> Résultat :
Avant :
"baseuser_ID" : "DUPREZ - Secteur Ouest GRO",
Après avec code :
"baseuser_ID" : "GRO-OUEST",
Après avec initiales :
"baseuser_ID" : "DEMO",
J’ai un champ spécifique « Catégorie tarif » (final_pricecategory_ID_tarCod) qui pointe vers une table standard (sw_data_pricecategory) pour lequel je veux afficher le « code » de cette table (CodePricecategory) :
'fieldsList': {
'final_pricecategory_ID_tarCod': {
'targetTableName': 'sw_data_pricecategory',
'targetFieldName': 'CodePricecategory'
}
}
Si je veux afficher le « libellé » (label) :
'fieldsList': {
'final_pricecategory_ID_tarCod': {
'targetTableName': 'sw_data_pricecategory',
'targetFieldName': 'label'
}
}
-> Résultat :
Avec code :
"final_pricecategory_ID_tarCod" : "TC",
Avec libellé :
"final_pricecategory_ID_tarCod" : "Prix public TTC",
J’ai un champ spécifique « Code remise » (final_t035_ID) qui pointe vers une table spécifique (sw_data_final_t035) pour lequel je veux afficher le « libellé » de cette table (final_lib) :
'fieldsList': {
'final_t035_ID': {
'targetFieldName': 'final_lib'
}
}
Si je veux afficher le code (codefinal_t035) : je n’ai rien à faire en spécifique, il s’affichera tout seul (si jamais je ne veux pas l’afficher il faudra l’exclure dans « extendedFieldsListToExclude »)
'extendedFieldsListToExclude': "divinf_t035_ID"
-> Résultat :
Avec code (automatique) :
"final_t035_ID" : "RG",
Avec libellé :
"final_t035_ID" : "Remise grossiste générale",
Exemple 1 du cas particulier de la clé étrangère de niveau + 2 (cas traité en standard)
Depuis la table intervention, je veux remonter vers la table des adresses client puis vers le client pour afficher son code :
'fieldsList': {
'customer_ID': {
'foreignParent': 'customeraddress_ID',
'targetFieldName': 'codecustomer'
},
},
'outOfMainTableFields': [
{
'fieldName': 'customer_ID',
'tableName': 'sw_data_customeraddress'
}
]
-> Résultat :
"customer_ID" : "C0000001",
Exemple 2 du cas particulier de la clé étrangère de niveau + 2
Depuis la table client, je veux remonter vers la table des groupes clients puis vers le client principal du groupe pour afficher son siret et renommer l’intitulé :
sw_data_customer.customergroup_ID -> sw_data_customergroup
sw_data_customergroup.customer_ID -> sw_data_customer
'fieldsList': {
'customer_ID': {
'foreignParent': 'customergroup_ID',
'targetFieldName': 'siretNumber',
'fieldNameToShow' : 'N° siret client principal'
},
},
'outOfMainTableFields': [
{
'fieldName': 'customer_ID',
'tableName': 'sw_data_customergroup'
}
]
-> Résultat :
"N° Siret du client principal" : "1234567899988",
Exemples impactant le réponse dans le bloc "fieldList": [...]
Ce bloc contient la description des champs retournés.
Pour rappel il n'y a aucun contrôle de cohérence. A vous de définir vos besoins selon l'interprétation que vous voulez en faire.
Ce bloc "fieldList" peut être utile pour construire une grille d'affichage en utilisant le label pour le titre de la colonne, le type pour formater la colonne et length pour définir la largeur de la colonne.
Je veux changer le type, la longueur et le libellé du champ « name »
'fieldsList': {
'name': {
'type': 'Char',
'length': '111',
'label': 'Le nom de mon client'
}
-> Résultat :
Avant :
"fields": [
{
"fieldName" : "name",
"fieldType": "varchar"
"fieldLength" : "255",
"fieldLabel" : "Nom",
}
]
Après :
"fields": [
{
"fieldName" : "name",
"fieldType": "char"
"fieldLength" : "111",
"fieldLabel" : "Le nom de mon client",
}
]
Je veux changer le libellé du champ « name » en fonction d'une clé de traduction par rapport à la langue courante du json :
'fieldsList': {
'name': {
'label': '<lng>sw_data_customer_contactName|<varscript>languageCode</varscript></lng>'
}
-> Résultat :
"fields": [
{
"fieldName" : "name",
"fieldType": "varchar"
"fieldLength" : "255",
"fieldLabel" : "Nom du contact",
}
]
Je veux changer le libellé du champ « name » en fonction d'une clé de traduction en allemand :
'fieldsList': {
'name': {
'label': '<lng>sw_data_customer_contactName|DE</lng>'
}
-> Résultat :
"fields": [
{
"fieldName" : "name",
"fieldType": "varchar"
"fieldLength" : "255",
"fieldLabel" : "Kontaktname",
}
]
Je veux définir un nouveau type et la vrai longueur pour le champ « generictype_ID_country », qui en standard est une foreignkey forcément sans longueur :
'fieldsList': {
'generictype_ID_country': {
'type': 'foreignkey-varchar',
'length': '255'
}
-> Résultat :
Avant :
"fields": [
{
"fieldName" : "generictype_ID_country",
"fieldType": "foreignkey"
"fieldLength" : "",
"fieldLabel" : "Pays",
}
]
Après :
"fields": [
{
"fieldName" : "generictype_ID_country",
"fieldType": "foreignkey-varchar"
"fieldLength" : "255",
"fieldLabel" : "Pays",
}
]
Exemples impactant l'ajout et la mise à jour
En ajout/mise à jour je veux donner des noms plus explicites pour certains champs :
'fieldsList': {
'name': {
'alias': 'Nom_du_client'
},
'postalCode': {
'alias': 'Code_Postal'
},
'city': {
'alias': 'Ville'
},
'phoneNumber': {
'alias': 'Num_Telephone'
}
}
-> Résultat :
Avant :
"action":
[
"verb": "PUT"
},
"data":
{
"customer" :
{
"name" : "ENTREPRISE DIJON GUICHARD",
"postalCode": "21000"
"city" : "DIJON",
...
Après :
"action":
[
"verb": "PUT"
},
"data":
{
"customer" :
{
"Nom_du_client" : "ENTREPRISE DIJON GUICHARD",
"Code_Postal": "21000"
"city" : "DIJON",
...
J’ai un champ spécifique « Catégorie tarif interne » (final_pricecategory_ID_tarCodInternal) pour lequel je veux interdire toute mise à jour que ce soit en ajout ou en mise à jour d'enregistrement même si une valeur est passée dans le json car il est géré en interne :
'fieldsList': {
'final_pricecategory_ID_tarCodInternal': {
'canInsert': 0,
'canUpdate': 0
}
}
J'ai un champ spécifique « Code test » (final_test_ID), que je ne veux plus gérer car il est obsolète :
'fieldsList': {
'final_test_ID': {
'deprecated': 1
}
}
Exemples impactant la définition
J’ai un champ spécifique « Catégorie tarif interne » (final_pricecategory_ID_tarCodInternal) pour lequel je veux lister toutes les valeurs (code et libellé de la table sw_data_pricecategory) :
'fieldsList': {
'final_pricecategory_ID_tarCodInternal': {
'canGetDefinitionList': 1,
}
}
-> Résultat :
"final_pricecategory_ID_tarCodeInternal": [
{
"code": "99",
"label": "Prix particulier"
},
{
"code": "GR",
"label": "Grossiste"
},
En plus du code et du libellé, je veux savoir s'il s'agit d'un tarif particulier (champ tarifPart de la table sw_data_pricecategory) et je veux lui donner un nom lisible "tarif particulier" :
'fieldsList': {
'final_pricecategory_ID_tarCodInternal': {
'canGetDefinitionList': 1,
'definitionExtraFields': [
{
'fieldName': 'tarifPart',
'fieldNameToShow': 'tarif particulier'
}
]
}
}
-> Résultat :
"final_pricecategory_ID_tarCodeInternal": [
{
"code": "99",
"label": "Prix particulier",
"tarif particulier": "1"
},
{
"code": "GR",
"label": "Grossiste",
"tarif particulier": "0"
},
En complément, je veux également retourner la valeur d'un champ (originalCode) qui correspond à une foreign (champ generictype_ID_tarifType de la table sw_data_pricecategory) :
'fieldsList': {
'final_pricecategory_ID_tarCodInternal': {
'canGetDefinitionList': 1,
'definitionExtraFields': [
{
'fieldName': 'tarifPart',
'fieldNameToShow': 'tarif particulier'
},
{
'fieldName': 'generictype_ID_tarifType',
'targetFieldName': 'originalCode'
}
]
}
}
-> Résultat :
"final_pricecategory_ID_tarCodeInternal": [
{
"code": "99",
"label": "Prix particulier",
"tarif particulier": "1",
"generictype_ID_tarifType": "PART"
},
{
"code": "GR",
"label": "Grossiste",
"tarif particulier": "0",
"generictype_ID_tarifType": "STD"
},
Si je veux filtrer les valeurs retournées uniquement sur les tarifs dont le champ « Tarif particulier » (tarifPart) est à 0 :
'fieldsList': {
'final_pricecategory_ID_tarCodInternal': {
'canGetDefinitionList': 1,
'definitionFilter': 'tarifPart = 0'
}
}
Filtres
Permet de rajouter des filtres spécifiques pour interroger les données en mode LIST.
Syntaxe
'strictFilters': [
{
'fieldName': '…',
'operator': '…',
'targetField': '…',
'query': '…'
},
{
'fieldName': '…',
'operator': '…'
'targetField': '…',
'query': '…'
},
{
...
}
]
Description syntaxe
fieldName : nom du champ sur lequel on veut filtrer des données
operator : opérateur de filtre
Les opérateurs disponibles sont :
Opérateur | Correspondance MySql |
---|---|
Null | IS NULL |
NotNull | IS NOT NULL |
Equal | = |
NotEqual | <> |
Greater | > |
GreaterOrEqual | >= |
Less | < |
LessOrEqual | <= |
Contains | LIKE %...% |
In | IN |
Between | BETWEEN |
StartsWith | LIKE ...% |
EndsWith | LIKE %... |
Pour un filtre sur une clé étrangère l’opérateur doit toujours avoir la valeur « In »
targetField : ID de la table principale. A utiliser uniquement pour les filtres avec clé étrangère.
query : requête de filtre avec jointure vers la table étrangère qui doit retourner l’ID de la table principale. A utiliser uniquement pour les filtres avec clé étrangère.
Exemples
Je veux filtrer mes clients sur un champ standard « N° de téléphone » (phoneNumber) :
'strictFilters': [
{
'fieldName': 'phoneNumber',
'operator': 'Equal'
}
]
-> Appel webhook avec le filtre :
"filters":
{
"phoneNumber": "02 31 14 50 00"
}
Je veux filtrer mes clients sur un champ standard « Date de fin des congés » (vacationEnd) sur une date précise ou une plage de dates :
'strictFilters': [
{
'fieldName': 'vacationEnd',
'operator' : Equal,
},
{
'fieldName': 'vacationEndBetween',
'operator' : 'Between',
'targetField': 'vacationEnd'
}
]
Remarque : on peut voir ici la manière de cumuler plusieurs filtres sur le même champ en utilisant la propriété "targetField" pour le 2eme filtre.
-> Appel webhook avec le filtre sur une date :
"filters":
{
"vacationEnd": "2020-08-30"
}
-> Appel webhook avec le filtre sur une plage de dates :
"filters":
{
"vacationEndBetween": "2020-08-01|2020-08-30"
}
Je veux filtrer mes clients sur un champ spécifique « Catégorie tarif » (final_pricecategory_ID_tarCod) qui pointe vers une table standard (sw_data_pricecategory) :
strictFilters: [
{
'fieldName': 'final_pricecategory_ID_tarCod',
'operator': 'In',
'targetField': 'customer_ID',
'query': '( SELECT customer_ID
FROM sw_data_customer AS c
INNER JOIN sw_data_pricecategory AS pc
ON pc.pricecategory_ID = c.final_pricecategory_ID_tarCod
WHERE pc.codepricecategory = %valueToBeReplaced% )'
}
]
-> Appel webhook avec le filtre :
"filters":
{
"final_pricecategory_ID_tarCod": "TC"
}
Je veux filtrer mes clients sur un champ spécifique « Code remise » (final_t035_ID) qui pointe vers une table spécifique (sw_data_final_t035) :
strictFilters: [
{
'fieldName': 'final_t035_ID',
'operator': 'In',
'targetField': 'customer_ID',
'query': '( SELECT customer_ID
FROM sw_data_customer AS c
INNER JOIN sw_data_final_t035 AS t035
ON t035.final_t035_ID = c.final_t035_ID
WHERE t035.codefinal_t035 = %valueToBeReplaced% )'
}
]
-> Appel webhook avec le filtre :
"filters":
{
"final_t035_ID": "RG"
}
Je veux filtrer mes clients sur un champ personnalisé « Capital (Keuros) » (v_A101) avec l’opérateur >= :
strictFilters: [
{
'fieldName': 'v_A101',
'operator': 'GreaterOrEqual'
}
]
-> Appel webhook avec le filtre :
"filters":
{
"v_A101": "1000"
}
Tris
Permet de rajouter des tris spécifiques pour interroger les données en mode LIST.
Syntaxe
'strictOrderBy': [
{
'name': '...',
'fields': [
{
'table': '...'
'field': '...',
'order': '...'
},
{
'table': '...'
'field': '...',
'order': '...'
},
{
...
}]
},
{
'name': '...',
'fields': [
{
'table': '...'
'field': '...',
'order': '...'
},
{
...
}]
}]
Description syntaxe
name : nom du tri, il doit être unique, il peut être intéressant de reprendre le nom des champs et l'ordre du tri comme mnémonique (non obligatoire)
field : nom du champ sur lequel on veut trier les données
order : ordre de tri
Les ordres de tris disponibles sont :
Ordre | Description |
---|---|
asc | tri ascendant |
desc | tri descendant |
table (optionnel) : nom de la table liée sur laquelle on veut faire le tri. Ne pas utiliser si le tri est effectué sur un champ de la table principale. Ne peut être utilisé que si un champ est déjà lu dans la table liée (selon type “simple” ou “extended”). Pour le type “simple”, les tris sur des tables liées ne seront donc pas forcément tous disponibles.
Exemples
Je veux trier mes clients sur les champs standards « Code postal » (postalCode) et « Ville » (city) de manière ascendante :
'strictOrderBy': [
{
'name': 'postalCodeAscCityAsc',
'fields': [
{
'field': 'postalCode',
'order': 'asc'
},
{
'field': 'city',
'order': 'asc'
}]
}]
-> Appel webhook avec le tri :
"orderBy": "postalCodeAscCityAsc"
Je veux trier mes clients sur les champs standards « Date de fin des congés » (vacationEnd) descendante et sur le « Nom » (name) ascendant :
'strictOrderBy': [
{
'name': 'vacationEndDescNameASc',
'fields': [
{
'field': 'vacationEnd',
'order': 'desc'
},
{
'field': 'name',
'order': 'asc'
}]
}]
-> Appel webhook avec le tri :
"orderBy": "vacationEndDescNameASc"
Je veux trier mes clients sur un champ personnalisé « Capital (Keuros) » (v_A101) de manière descendante :
'strictOrderBy': [
{
'name': 'CapitalDesc',
'fields': [
{
'field': 'v_A101',
'order': 'desc'
}]
}]
-> Appel webhook avec le filtre :
"orderBy": "CapitalDesc"
Je veux trier mes clients sur un champ d’une autre table « Catégorie client » (codepricecategory) ascendant et sur le « Nom » (name) ascendant :
Attention ici ce tri ne peut être utilisé que si l’interrogation est effectué pour un type “extended”
Pour une utilisation pour un type “simple”, il faudrait ajouter le champ “pricecategory_ID” dans la propriété simpleFieldsList
'strictOrderBy': [
{
'name': 'codePriceCategoryAscNameAsc',
'fields': [
{
'table': 'pricecategory',
'field': 'codepricecategory',
'order': 'asc'
},
{
'field': 'name',
'order': 'asc'
}]
}
-> Appel webhook avec le filtre :
"orderBy": "codePriceCategoryAscNameAsc"
Je veux trier mes clients sur champ d’une autre table « Type générique - Pays » (originalCode) ascendant et sur le « Nom » (name) ascendant :
'strictOrderBy': [
{
'name': 'codeCountryAscNameAsc',
'fields': [
{
'table': 'generictype_country',
'field': 'originalCode',
'order': 'asc'
},
{
'field': 'name',
'order': 'asc'
}]
}
-> Appel webhook avec le filtre :
"orderBy": "codeCountryAscNameAsc"
Surcharge des règles métier
Correspond au contrôle des règles métiers, qui par exemple peut interdire un ajout/mise à jour si certaines conditions ne sont pas respectées.
Cette surcharge permet également d'initialiser des champs selon le besoin.
Toutes les surcharges seront dans les scripts « WebhookRules_xxxx ».
Squelette de la surcharge
// WebhookRulesOverload_xxxx
// @description => Overload rules with yours. Return a response and a errorMessage if needed.
//
// @code => Row code. Not exploited at the moment.
// @verb => Method used. Can be PUT | GET | LIST | DELETE
// @dataInput => Depending of the method. Contains json for the PUT method. Only PUT contain data.
// @ruleFields => Empty Object
// Can be filled like this : DOCUMENT_ADD_PROPERTY_TO_OBJECT( ruleFields, "nameOfDataInput", yourNewValue )
//
// @returns => Response JSON document with two properties :
// response [ required ] => 1 if all rules are valid or 0 IF at least one rule is not valid
// errorMessage [ optional ] => if response equal to 0, THEN we display your errorMessage
code = TRANSLATE( "<varscript>arg1</varscript>" )
verb = TRANSLATE( "<varscript>arg2</varscript>" )
dataInput = TRANSLATE( "<varscript>arg3</varscript>" )
ruleFields = TRANSLATE( "<varscript>arg4</varscript>" )
jsonResponse = DOCUMENT_CREATE_DOCUMENT()
// To complete
…
response = DOCUMENT_SERIALIZE( jsonResponse, "json" )
RETURN( response )
Description des paramètres
En entrée :
arg1 : code (pas exploité pour l’instant)
arg2 : méthode : PUT, GET, LIST, DELETE
arg3 : json de l’entité concernée (jsonCustomer, jsonContract, …)
arg4 : objet vide, qui permet d'initialiser des champs
En sortie :
Si toutes les règles spécifiques mise en place sont valides alors le script doit retourner « reponse » avec la valeur « 1 » (se fait de manière implicite si non précisé).
DOCUMENT_ADD_PROPERTY_TO_OBJECT( jsonResponse, "response", "1" )
response = DOCUMENT_SERIALIZE( response, "json" )
RETURN( response )
Si au moins une règle spécifique mise en place n’est pas valide alors le script doit retourner « response » avec la valeur « 0 » et un message d’erreur « errorMessage ».
DOCUMENT_ADD_PROPERTY_TO_OBJECT( jsonResponse, "response", "0" )
DOCUMENT_ADD_PROPERTY_TO_OBJECT( jsonResponse, "errorMessage", "Specific rule error: …" )
response = DOCUMENT_SERIALIZE( jsonResponse, "json" )
RETURN( response )
Exemples pour le contrôle des règles métier
Je ne veux autoriser l’ajout/mise à jour d’un enregistrement dans « sw_data_customer », uniquement s’il s’agit d’un prospect :
IF( EQUALS( method, "PUT" ) )THEN
jsonCustomer = JSON_GET_FROM_PATH( dataInput, "customer" )
IF ( JSON_EXISTS_FROM_PATH( jsonCustomer, "generictype_ID_customerType" ) ) THEN
generictype_ID_customerType = JSON_GET_FROM_PATH( jsonCustomer, "generictype_ID_customerType" )
IF( EQUALS( generictype_ID_customerType , "CUSTOMER_TYPE-CTP" ) == 0 )THEN
DOCUMENT_ADD_PROPERTY_TO_OBJECT( jsonResponse, "response", "0" )
DOCUMENT_ADD_PROPERTY_TO_OBJECT( jsonResponse, "errorMessage", "Specific rule error: Le type doit être prospect (Valeur envoyée : <varscript>generictype_ID_customerType</varscript>)" )
response = DOCUMENT_SERIALIZE( jsonResponse, "json" )
RETURN( response )
ENDIF
ENDIF
ENDIF
-> Résultat en retour si la règle n’est pas respectée :
"result": {
"common": {
"resultCode": "1",
"errorMessage": "Specific rule error: Le type doit être prospect (Valeur envoyée : CUSTOMER_TYPE-CTC)"
},
Je ne veux autoriser l’ajout/mise à jour d’un enregistrement dans « sw_data_customer », uniquement si un champ personnalisé « Note administrative » (CLI_50) est >= 8.
Pour rappel les champs personnalisés, peuvent être mis à jour par le bloc json « dynamicFields ».
"data": {
"customer": {
"codecustomer": "C0000001",
"name": "ENTREPRISE DIJON GUICHARD",
"dynamicfields":
[{
"fieldname":"CLI40",
"fieldvalue": "8"
},
{
"fieldname":"CLI50",
"fieldvalue": "6"
}]
}
}
IF( EQUALS( method, "PUT" ) )THEN
jsonCustomer = JSON_GET_FROM_PATH( dataInput, "customer" )
IF ( JSON_EXISTS_FROM_PATH( jsonCustomer, "dynamicfields" ) ) THEN
jsonDynamicFields = JSON_GET_FROM_PATH( jsonCustomer, "dynamicfields" )
VARSET_SHELL( "errorMessage", "", "FINAL_RulesDynamicFields" )
EXECUTE_SCRIPT_FOREACH_ROW( "FINAL_WebhookRulesDynamicFields_customer", "json", jsonDynamicFields )
errorMessage = VARGET_SHELL( "errorMessage", "FINAL_RulesDynamicFields" )
IF (LEN( "<varscript>errorMessage</varscript>" ) > 0) THEN
error = 1
DOCUMENT_ADD_PROPERTY_TO_OBJECT( jsonResponse, "response", "0" )
DOCUMENT_ADD_PROPERTY_TO_OBJECT( jsonResponse, "errorMessage", "Specific rule error: <varscript>errorMessage</varscript>" )
ENDIF
VARDELGROUP_SHELL( "FINAL_RulesDynamicFields" )
IF EQUALS( "<varscript>error</varscript>", "1" ) THEN
response = DOCUMENT_SERIALIZE( jsonResponse, "json" )
RETURN( response )
ENDIF
ENDIF
ENDIF
Avec le script complémentaire « FINAL_WebhookRulesDynamicFields_customer » utilisé dans « EXECUTE_SCRIPT_FOREACH_ROW » pour boucler sur tous les dynamicfields :
IF EQUALS( "<varscript>fieldName</varscript>", "CLI50" )THEN
IF( EQUALS( "<sql>SELECT '<varscript>fieldValue</varscript>' < 8 </sql>", "1" ) ) THEN
VARSET_SHELL( "errorMessage", "La note administrative du client est inférieure à 8 (champ : <varscript>fieldName</varscript>, valeur envoyée : <varscript>fieldValue</varscript>)", "FINAL_RulesDynamicFields" )
ENDIF
RETURN("breakforeach")
ENDIF
-> Résultat en retour si la règle n’est pas respectée :
"result": {
"common": {
"resultCode": "1",
"errorMessage": "Specific rule error: La note administrative du cl, valeur envoyée 6)""
},
Exemples pour l’initialisation des champs
Je veux mettre un commentaire dans le champ « comment » de « sw_data_customer » si certaines informations sont incomplètes.
IF( EQUALS( method, "PUT" ) )THEN
jsonCustomer = JSON_GET_FROM_PATH( dataInput, "customer" )
VARINITIALIZE_SCRIPT_BYJSON( jsonCustomer )
msg = ""
IF( LEN( "<varscript>apeCode</varscript>" ) == 0 )THEN
msg = CONCAT( msg, "*** Code APE" )
ENDIF
IF( LEN( "<varscript>phoneNumber</varscript>" ) == 0 )THEN
msg = CONCAT( msg, "*** Téléphone" )
ENDIF
IF( LEN( "<varscript>generictype_ID_country</varscript>" ) == 0 )THEN
msg = CONCAT( msg, "*** Pays" )
ENDIF
IF( LEN( "<varscript>msg</varscript>" ) > 0 )THEN
msg = CONCAT ( "Informations nécessaires à renseigner dès que possible : ", msg )
ENDIF
DOCUMENT_ADD_PROPERTY_TO_OBJECT( ruleFields, "comment", msg)
END IF
Je veux initialiser un champ spécifique « final_contract_ID_default » de « sw_data_customer ».
contractID = CALL_SCRIPT( "FINAL_GetContract4Customer", "<varscript>customer_ID</varscript>" ) // script for example who return a contract id
contractCode = CALL_SCRIPT( "Func_GetCodeFromID", "sw_data_contract", "<varscript>contractID</varscript>" )
DOCUMENT_ADD_PROPERTY_TO_OBJECT( ruleFields, "final_contract_ID_default", "<varscript>contractCode</varscript>" )
Pour initialiser un champ de type foreignKey, il est impératif de passer le code de l’enregistrement et pas son ID.
Surcharge avant exécution webhook
Cette surcharge sera appelée entre le contrôle de validité et des règles du webhook et son exécution.
Elle sera dans le script « WebhookOverload_Before ».
Squelette de la surcharge
Squelette de la surcharge :
// WebhookOverload_Before
// @Description => A script to use before webhook execution
//
// @tableName => Name on wich table webhook will proceed
// @code => Row code. Can be empty depending of the method. It is empty for LIST || PUT.
// GET: contains code link to the row to be returned
// DELETE: contains code link to the row to be deleted
// @method => Method used. Can be GET || LIST || PUT || DELETE
// @headerInput => Header input
// @dataInput => Data input. Can be empty depending of the method. It is empty for GET || LIST || DELETE.
// PUT: contains data to be inserted
// @resultCode => If webhook execution will fail, resultCode is equal to 0. Otherwise, 1.
// @errorMessage => Error message associated to the code
// @verifiedData => An object that contains all data. IF others arguments are not enough
// @entity => To be used to differenciate between webhooks using the same table
//
// @Returns => Void
tableName = TRANSLATE( "<varscript>arg1</varscript>" )
code = TRANSLATE( "<varscript>arg2</varscript>" )
method = TRANSLATE( "<varscript>arg3</varscript>" )
headerInput = TRANSLATE( "<varscript>arg4</varscript>" )
dataInput = TRANSLATE( "<varscript>arg5</varscript>" )
resultCode = TRANSLATE( "<varscript>arg6</varscript>" )
errorMessage = TRANSLATE( "<varscript>arg7</varscript>" )
verifiedData = TRANSLATE( "<varscript>arg8</varscript>" )
entity = TRANSLATE( "<varscript>arg9</varscript>" )
// For specific case, markForExport property can be changed here.
// To do it, a variable is available
// VARSET_SHELL( "markForExport", "Can be 0, 1, 2", "WebhookHeader" )
// IF resultCode = 0, THEN all data are empty, except errorMessage.
IF( EQUALS( resultCode, "1" ) )THEN
ENDIF
Description des paramètres
En entrée :
arg1 : table principale du webhook
arg2 : code de l’enregistrement pour les méthodes GET et DELETE (non utilisé pour les méthodes LIST et PUT)
arg3 : nom de la méthode (GET, LIST, PUT, DELETE)
arg4 : json du header
arg5 : json de l’entité concernée (jsonCustomer, jsonContract, …) pour la méthode PUT (non utilisé pour les méthodes GET, LIST et DELETE)
arg6 : code résultat du contrôle de validité (0 : Echec / 1 : Ok)
arg7 : message d’erreur si le contrôle de validité a échoué
arg8 : objet contenant toutes les données, si les autres arguments ne suffisent pas
arg9 : entité qui permet de différencier des webhooks différents qui utilisent la même table
En sortie : /
Exemple d'utilisation
Je veux loger les informations dans une table spécifique « sw_data_final_log » :
dataInput = REPLACE("<varscript>dataInput</varscript>",";",",")
msg = "message="
msg = CONCAT( msg, "tableName = <varscript>tableName</varscript>-" )
msg = CONCAT( msg, "code = <varscript>code</varscript>-" )
msg = CONCAT( msg, "method = <varscript>method</varscript>-" )
msg = CONCAT( msg, "headerinput = <varscript>headerinput</varscript>-" )
msg = CONCAT( msg, "datainput = <varscript>datainput</varscript>-" )
msg = CONCAT( msg, "resultcode = <varscript>resultcode</varscript>-" )
msg = CONCAT( msg, "errormessage = <varscript>errormessage</varscript>" )
msg = CONCAT( msg, "entity= <varscript>entity</varscript>" )
VARADD_DB( "sw_data_final_log", msg )
Surcharge après exécution webhook
Cette surcharge sera appelée après l’exécution d’un webhook.
Elle sera dans le script « WebhookOverload_After ».
Squelette de la surcharge
// WebhookOverload_After
// @Description => A script to use after webhook execution
//
// @tableName => Name on wich table webhook proceeded
// @code => Row code. Can be empty depending of the method. It is empty for LIST || PUT.
// GET: contains code link to the returned row
// DELETE: contains code link to the deleted row
// @method => Method used. Can be GET || LIST || PUT || DELETE
// @dataInput => Data input. Can be empty depending of the method. It is empty for GET || LIST || DELETE.
// PUT: contains data inserted
// @dataOuput => Data ouput. Can be empty depending of the method. It is empty for PUT || DELETE.
// GET || LIST: contains data returned
// @resultCode => IF webhook execution failed, resultCode is equal to 1. Otherwise, 0.
// @errorMessage => Error message associated to the code
// @action => insert / update / select / delete. Useful to differantiate insert and update
// @verifiedData => An object that contains all data. If others arguments are not enough
// @entity => To be used to differenciate between webhooks using the same table
//
// @Returns => Void
tableName = TRANSLATE( "<varscript>arg1</varscript>" )
code = TRANSLATE( "<varscript>arg2</varscript>" )
method = TRANSLATE( "<varscript>arg3</varscript>" )
dataInput = TRANSLATE( "<varscript>arg4</varscript>" )
dataOutput = TRANSLATE( "<varscript>arg5</varscript>" )
resultCode = TRANSLATE( "<varscript>arg6</varscript>" )
errorMessage = TRANSLATE( "<varscript>arg7</varscript>" )
action = TRANSLATE( "<varscript>arg8</varscript>" )
verifiedData = TRANSLATE( "<varscript>arg9</varscript>" )
entity = TRANSLATE( "<varscript>arg10</varscript>" )
Description des paramètres
En entrée :
arg1 : table principale du webhook
arg2 : code de l’enregistrement pour les méthodes GET et DELETE (non utilisé pour les méthodes LIST et PUT)
arg3 : nom de la méthode (GET, LIST, PUT, DELETE)
arg4 : json de l’entité concernée (jsonCustomer, jsonContract, …) pour la méthode PUT (non utilisé pour les méthodes GET, LIST et DELETE)
arg5 : json en sortie pour les méthodes GET et LIST (non utilisé pour les méthodes PUT et DELETE)
arg6 : code résultat de l’exécution du webhook (0 : OK / 1 : Echec)
arg7 : message d’erreur si l’exécution du webhook a échoué
arg8 : action effectuée (insert / update / select / delete)
arg9 : objet contenant toutes les données, si les autres arguments ne suffisent pas
arg10 : entité qui permet de différencier des webhooks différents qui utilisent la même table
En sortie : /
Exemple d'utilisation
Je veux loger les informations dans une table spécifique « sw_data_final_log »
dataInput = REPLACE("<varscript>dataInput</varscript>",";",",")
dataOutput = REPLACE("<varscript>dataOutput</varscript>",";",",")
msg = "message="
msg = CONCAT( msg, "tableName = <varscript>tableName</varscript>-" )
msg = CONCAT( msg, "code = <varscript>code</varscript>-" )
msg = CONCAT( msg, "method = <varscript>method</varscript>-" )
msg = CONCAT( msg, "datainput = <varscript>datainput</varscript>-" )
msg = CONCAT( msg, "dataoutput = <varscript>dataoutput</varscript>-" )
msg = CONCAT( msg, "resultcode = <varscript>resultcode</varscript>-" )
msg = CONCAT( msg, "errormessage = <varscript>errormessage</varscript>" )
msg = CONCAT( msg, "action= <varscript>action</varscript>" )
msg = CONCAT( msg, "entity= <varscript>entity</varscript>" )
VARADD_DB( "sw_data_final_log", msg )