# Formules

Le champ formule permet d'obtenir un résultat à partir d'une opération effectuée sur les valeurs d'autres champs. Une fois votre champ formule ajouté au formulaire, dans la case « Formule », ajoutez une formule JavaScript avec les champs du formulaire comme décrit dans l'infobulle.

![](/files/zlGxFTVbcXjLmtbKsMTQ)

## Options du champ

Voici les différentes options que vous pouvez configurer sur un champ formule.

### **Type**

Le champ formule est *polymorphe*, ce qui signifie qu'il possède un type de résultat, défini comme sa première option. Ce type est l'un des autres types de données Daxium-Air pris en charge par le champ formule. À la date d'écriture, les types de retour possibles sont : Texte, Nombre, Booléen, Date, Durée, Liste et Localisation. Voir l'article [Champs simples](/user-documentation/documentation-utilisateur/build/building-forms/fields/simple-fields.md) pour une définition de ces types.

Pour certains des types pris en charge, vous pouvez avoir des options de personnalisation de l'affichage de la donnée. Celles-ci sont identiques à celles du type Daxium-Air sélectionné.

![](/files/pSKPXghhNgfpDuBRgjDQ)

### **Formule**

Cet éditeur permet l'édition de la fonction JavaScript telle qu'elle sera exécutée par les différents moteurs de nos plateformes. L'éditeur propose plusieurs fonctionnalités pour faciliter l'écriture du code :

* annuler/rétablir
* autocomplétion (limitée aux méthodes JavaScript natives) ;
* édition en mode plein écran ou fenêtre flottante ;
* détection des erreurs affichée dans la gouttière (uniquement en anglais).

### **Règles de calcul**

Les règles de calcul suivantes sont possibles :

* **Calcul unique** : dès que la formule est calculée avec succès une fois, elle n'est plus recalculée, quel que soit l'événement ultérieur (même si un champ lié est modifié) ;
* **Rafraîchissement automatique** : la formule est automatiquement calculée chaque fois que cela est pertinent (lorsqu'un champ lié est modifié, lorsque le formulaire est ouvert ou enregistré, etc.) ;
* **Manuel** : les utilisateurs doivent cliquer sur un bouton pour que la formule soit calculée.

## Écrire une formule — accéder aux données de la fiche

Plusieurs règles d'écriture d'une formule doivent être respectées pour qu'elle fonctionne. Cette première partie se concentre sur la manière de lire les valeurs des différents champs et les métadonnées de la fiche contenant la formule.

### **Le contexte JavaScript d'une formule**

Le code de la formule est encapsulé dans une fonction JavaScript qui est exécutée avec une liste de paramètres injectés par Daxium-Air. L'un de ces paramètres est *items*, un objet JavaScript qui contient les valeurs de la fiche utilisées dans la formule. Un autre est *metadata*, qui contient les données de la fiche qui ne sont pas une valeur de champ interne.

Une chose importante à noter : pour pouvoir renseigner correctement l'objet *items* et le limiter au strict nécessaire, la formule est analysée afin de détecter les valeurs utilisées par l'objet. Par exemple, lorsque vous écrivez *items\['monBooleen']* dans une formule, cela est interprété comme *« injecter la valeur du champ monBooleen dans l'objet items »* par nos moteurs. Cela signifie que **vous devez toujours utiliser** la syntaxe *items\['systemName']* pour déclarer une variable dans votre formule.

Les implications sont que :

* la syntaxe *items.systemName* n'est **pas** acceptée (la variable *systemName* ne sera pas injectée dans la formule) ;
* vous ne pouvez pas écrire votre nom système comme une variable dynamique.

Par exemple, supposons que vous ayez 3 champs *systemName1*, *systemName2*, *systemName3*.

```
var systemName;
var fieldValue;
for (var i=0; i=2; i++) {
	systemName = 'systemName' + i.toString();
	fieldValue = items[systemName];
	// perform instructions using fieldValue variable
}
return ...
```

Cette manière d'accéder aux noms système ne fonctionnera pas, car lorsque l'interpréteur JavaScript lit la formule, il ne comprend pas que les champs *systemName1*, *systemName2*, *systemName3* sont impliqués dans cette formule. Les noms système doivent être écrits en toutes lettres au moins une fois quelque part dans le code, même en commentaire. Le code ci-dessous fonctionnerait par exemple :

```
/*
items['systemName1']
items['systemName2']
items['systemName3']
*/
var systemName;
var fieldValue;
for (var i=0; i=2; i++){
	systemName = 'systemName'+i.toString();
	fieldValue = items[systemName];
	// perform instructions using fieldValue variable
}
return ...
```

### **Considérations de syntaxe et de configuration**

Comme un champ peut tout à fait ne pas être rempli, il peut ne pas être injecté dans l'objet *items*. Il est considéré comme une bonne pratique de vérifier sa valeur avec :

```
if (items['price_tax']) {
	return items['price_tax'] * ( 1 + 20/100);
} else {
	return 0;
}
```

Pour les mêmes raisons architecturales liées à la construction des formules dans Daxium-Air, il n'est pas possible d'utiliser un opérateur de commentaire de ligne (//) à la fin de la formule (après le mot-clé *return* de clôture). N'utilisez que des commentaires en bloc (/\* ... \*/) à cet endroit.

Enfin, veillez à ne pas créer de cycles de dépendance dans vos formules. Comme une formule peut être considérée comme un type Daxium-Air à part entière, elle peut être injectée et utilisée dans une autre formule. Mais il n'est pas possible de créer un cycle à travers ces formules, faute de quoi le calcul ne se terminerait jamais.

### **Accéder aux valeurs des champs**

Selon le type Daxium-Air des éléments accédés, le format des valeurs dans l'objet *items* diffère.

#### Types simples

Les champs Texte, E-mail, Téléphone (norme RFC 3966), Nombre et Durée (en millisecondes) (ainsi que leurs variantes formule typées, le cas échéant) sont récupérés via les types primitifs JavaScript correspondants (String ou Number). L'objet *items* avec ces types ressemblerait à :

```
var items = {
    "textSystemName": "Hello",
    "emailSystemName": "john.doe@company.com",
    "phoneSystemName": "+33123456789",
    "numberSystemName": 68,
    "durationSystemName": 42000,
}
```

#### **Date**

Les dates sont stockées et retournées au format [Unix timestamp](https://en.wikipedia.org/wiki/Unix_time) (nombre de secondes entre la date voulue et le 1ᵉʳ janvier 1970 minuit UTC) :

```
var items = {
	"dateSystemName": 1466437174
}
```

Par défaut, les dates sont traitées selon le fuseau horaire système de votre appareil. Il est possible de contourner cela et d'imposer UTC en faisant :

```
var date = new Date();
return (date.getTime() + date.getTimezoneOffset() * 60 * 1000) / 1000;
```

#### **Booléen**

Une variable booléenne ne peut avoir que deux valeurs : true ou false.

```
var items = {
	"booleanSystemName": true
}
```

Pour vérifier la valeur d'un booléen, nous vous demandons de ne pas utiliser l'opérateur :

```
===
```

et d'utiliser à la place :

```
==
```

car le premier produit des comportements différents selon nos plateformes (web, Android, iOS).

De plus, dans Daxium-Air, un champ booléen (dès lors qu'il n'est **pas obligatoire**) peut aussi valoir null. Cela se traduit dans le back office par « Non défini », ou par ni « Oui » ni « Non » sélectionné sur mobile.

![](/files/T6UtcgnwRndgg8Y4Fv0J)

Dans une formule, il est possible de vérifier si un champ booléen est null en utilisant :

```
items['booleanSystemName'] == null
```

#### **Champs de type Object et Array**

Lorsque vous écrivez *items\['systemName']* pour ces champs, vous récupérez soit un [Object](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Object), soit un [Array](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Array) d'objets. Vous pouvez accéder à n'importe quelle propriété en utilisant la syntaxe :

```
items['systemName'].key
```

ou, si c'est un Array, où « i » est la position de l'élément dans le tableau :

```
items['systemName'][i].key
```

Pour tous les champs qui sont des objets JavaScript, il est nécessaire de vérifier qu'ils ne sont pas null **avant de les assigner à une variable**. Lorsque le champ est un Array d'objets, vous devez également vérifier que le nombre d'éléments du tableau n'est pas 0.

Ce qu'il **NE FAUT PAS** écrire :

```
var listField = items['listSystemName'];
if (listField[0].caption == "A text value") {
	...
}
```

Mais plutôt :

```
if (items['listSystemName'] && items['listSystemName'].length != 0){
	var listField = items['listSystemName'];
}
if (listField[0].caption == "A text value") {
	...
}
```

**Localisation**

Un champ Localisation est composé d'une adresse textuelle (exemple : Cluster R - Jumeirah Lake Towers - Dubai, UAE) et de deux coordonnées GPS [WGS84](https://fr.wikipedia.org/wiki/WGS_84) (exemple : lat 25.0768995, long 55.1472642). Voici un exemple de retour d'une représentation textuelle d'un champ Localisation de nom système *address* :

```
var result = "";
if (items['address']) {
	if (items['address'].address) {
		result = "Address : " + items['address'].address;
	}
	if (items['address'].lat && items['address'].lng) {
		result = result + "\nlat: " + items['address'].lat; 
		result = result + ", long: " + items['address'].lng;
	}
}
return result;
```

Ce qui donne en sortie :

```
Address: Cluster R - Jumeirah Lake Towers - Dubai, UAE
lat: 25.0768995, long: 55.1472642
```

À noter : les formules de type Localisation ne sont pas utilisables sur l'application Android V1.

**Fichier, Image, Signature**

Un champ Fichier, Image ou Signature peut contenir plusieurs éléments sélectionnés. L'élément correspondant dans une formule est donc un Array JavaScript d'objets. Un tel objet possède 5 propriétés :

* *size* ; type entier Ex : 13554
* *name* ; type string Ex : "test formules.pdf"
* *extension* ; type string Ex : pdf
* *mimeType* ; type string Ex : "application/pdf"
* *comment* ; type string Ex : "Ceci est un commentaire"

Par exemple, pour récupérer le nom du fichier, utilisez :

```
return items['imageSystemName'][0].name;
```

**Utilisateur**

Un champ Utilisateur peut contenir plusieurs éléments sélectionnés. L'élément correspondant dans une formule est donc un Array JavaScript d'objets. Un tel objet possède trois propriétés String explicites :

* *email* ;
* *first\_name* ;
* *last\_name*.

Par exemple, pour récupérer l'adresse e-mail de l'utilisateur, utilisez :

```
return items['userSystemName'][0].email;
```

Pour retourner la concaténation du prénom et du nom d'un utilisateur dans un champ utilisateur non multiple, utilisez :

```
if (items['userSystemName'] && items['userSystemName'].length > 0) {
	var user = items['userSystemName'][0];
	return user.first_name + ' ' + user.last_name;
} else {
	return "No user selected";
}
```

À noter : l'accès à ces propriétés dans un champ Formule est indépendant de l'option d'affichage du champ Utilisateur, elles sont toujours disponibles.

**Liste**

Comme pour le champ Utilisateur, un champ Liste dans une formule Daxium-Air est un Array JavaScript d'objets. Il se définit ainsi (dans le cas où deux éléments de liste sont sélectionnés dans le champ de la fiche) :

```
var items = {
	"listSystemName": [ 
		{
			"caption": "My list label 1",
			"external_id": "My external id 1",
			"id": 104839483, // Daxium System ID
			"parent_id": 104839483 // For list with multiple levels
		},
		{
			"caption": "My list label 2",
			"external_id": "My external id 2",
			"id": 104839484, 
			"parent_id": 104839485 
		}
	]
}
```

Vous ne pouvez récupérer que les propriétés :

* *caption* ;
* *external\_id* ;
* *parents*, pour accéder à la hiérarchie des éléments.

La syntaxe pour récupérer « My list label 1 » sera alors :

```
items['listSystemName'][0].caption
```

Pour apprendre à accéder à la hiérarchie d'un champ Liste, prenons un exemple avec cette liste :

* Animal
  * Félin
    * Chat (external\_id : cat)
    * Tigre
* Oiseau
  * Mouette
  * Pigeon

Pour écrire nos formules, disons que nous avons sélectionné les éléments *Chat* et Mouette dans le champ correspondant appelé animal\_list.

Pour obtenir le nom du premier élément sélectionné :

```
items['animal_list'][0].caption
```

retourne

```
Chat
```

Pour obtenir l'identifiant externe du premier élément sélectionné :

```
items['animal_list'][0].external_id
```

retourne

```
cat
```

Pour obtenir la racine de la liste :

```
items['animal_list'][0].parents[0].caption
```

retourne

```
Animal
```

Pour obtenir le parent direct du premier élément sélectionné :

```
items['animal_list'][0].parents[ items['animal_list'][0].parents.length – 1 ].caption
```

retourne

```
Félin
```

Pour obtenir la liste de tous les choix sélectionnés :

```
if (items['list_animal']) {
	return items['list_animal'].reduce(function(result, element) {
    	return result + " - " + element.caption + ".";
  }, "");
} else {
	return "No items selected in list.";
}
```

retourne

```
- Chat. - Mouette.
```

**Relation**

Un champ Relation est un Array de fiches. Si l'option « Multiple » n'est pas sélectionnée sur le champ, le tableau ne contiendra toujours qu'un seul élément. Si l'option « Comptable » est sélectionnée, une propriété *count* supplémentaire de type Number est présente.

Pour accéder aux données contenues dans un tel champ Relation, utilisez l'objet *relations* injecté dans l'objet *items*. Il contient deux propriétés :

* *children*, qui contient toutes les fiches liées à la fiche courante via les champs Relation qu'elle contient ;
* *parents*, qui contient toutes les fiches liées via un champ Relation contenu par un autre formulaire.

Il est important de noter que la position d'une fiche dans le tableau n'est définie par rien et n'est pas relative à la date de création de la fiche enfant. Par conséquent, l'hypothèse que *relations.children.system\_name.submissions\[0]* est la première fiche enfant créée est **fausse**.

Pour éviter les erreurs d'exécution, avant d'appeler une donnée dans une fiche liée (enfant ou parent), vous devez tester l'existence de la relation et le fait qu'elle ne soit pas vide (au moins une fiche liée).

Si le champ Relation se trouve dans le formulaire courant avec le nom système `system_name`, vous pouvez accéder aux fiches liées avec :

```
relations.children.system_name.submissions
```

Pour récupérer ensuite un champ nommé *title* :

```
relations.children.system_name.submissions[0].items['title'];
```

Si le champ Relation se trouve dans un formulaire parent, la syntaxe est similaire, mais en utilisant *parents* au lieu de *children* :

```
relations.parents.system_name.submissions[0].items['title'];
```

Pour lister tous les titres des fiches qui ont établi une relation avec la fiche courante via le champ Relation *relationSystemName* (chacune devant donc posséder un champ Texte *title*) :

```
if (relations.parents.relationSystemName && relations.parents.relationSystemName.submissions) {
	return relations.parents.relationSystemName.submissions.reduce(
		function(result,element) {
		    return result + " - " + element.items['title'] + ".";
  		}, ""
	);
} else {
	return "No parent submission found";}
```

**Champs Relation comptables**

On dit qu'un champ Relation est comptable lorsque l'option du même nom est activée. Cela ajoute des attributs à chaque fiche liée, exploitables dans les formules.

Pour accéder à ces attributs, vous devez utiliser l'objet *relationshipProperties* de la relation concernée. Par exemple, si la relation a pour nom système *products* et que nous cherchons la quantité associée (paramètre *count*), cela donne :

```
relations.children.product.submissions.relationProperties.count
```

Cas pratique : le formulaire parent est une commande avec les produits liés via un champ Relation comptable (nom système : *product*). Le prix de chaque produit apparaît dans le champ Nombre *price* de certains produits. Pour obtenir le prix total de ma commande, vous pouvez faire :

```
var price = 0;
if (relations && 
	relations.children && relations.children.products && 
	relations.children.products.submissions && 
	relations.children.products.submissions.length > 0) {

	relations.children.products.submissions.forEach(
		function(prod) {
			var quant = 1;
			if(prod.relationProperties.count && 
				prod.relationProperties.count) {
      			quant = prod.relationProperties.count
			}
			if (prod.items && prod.items['price']) {
				price += prod.items['price'] * quant;
			}
		}
	);
}
return price;
```

Règle générale : soyez particulièrement vigilant lorsque vous manipulez de l'argent en [arithmétique à virgule flottante](https://fr.wikipedia.org/wiki/IEEE_754) (en JavaScript ici, mais aussi dans d'autres langages). Il est recommandé de ne manipuler que des entiers et de les convertir en flottants au moment de l'affichage, comme dans l'exemple précédent.

### **Accéder aux données de la fiche**

Il est possible d'accéder à certaines données système de la fiche via l'objet JavaScript **metadata**, qui contient les propriétés suivantes :

* **uuid**, un [UUID](https://fr.wikipedia.org/wiki/Universally_unique_identifier) 128 bits qui identifie de manière unique la fiche, représenté sous forme de String ;
* **created\_at**, la date de création de la fiche au format Unix timestamp ;
* **updated\_at**, la date de dernière mise à jour de la fiche au format Unix timestamp ;
* **number**, un numéro unique identifiant la fiche (Number) ;
* **number\_in\_structure**, un numéro unique identifiant la fiche, limité à sa structure (Number) ;
* **longitude**, la longitude de la position native de la fiche
* **latitude**, la latitude de la position native de la fiche
* **user**, un objet contenant l'e-mail, le prénom et le nom de l'utilisateur qui a **créé** la fiche.
* **update\_user**, un objet contenant l'e-mail, le prénom et le nom de l'utilisateur qui a **effectué la dernière mise à jour** de la fiche.

Voici un exemple d'un tel objet :

```
var metadata = {
	"number": 63930,
	"number_in_structure" : 12,
	"uuid": "38c6d76e-5442-4cbd-baef-cb9e5d6c4daa",
	"created_at": 1550242752,
	"updated_at": 1550242752,
	"longitude": 50.894873194158,
	"latitude":-72.735993862152,
	"user": {
		"email": "jd@company.com",
		"last_name": "Doe",
		"first_name": "John"
	},
	"update_user": {
		"email": "rm@company.com",
		"last_name": "Moore",
		"first_name": "Roger"
	}
}
```

Il peut s'utiliser ainsi :

```
return metadata['number'];
```

À noter : *number*, *number\_in\_structure* et *created\_at* ne sont disponibles qu'après le premier enregistrement de la fiche.

## Écrire une formule — retourner des valeurs de différents types de données

Selon le type de retour défini sur le champ formule, vous devez retourner la valeur correspondante depuis la fonction formule. Pour cela, utilisez toujours le mot-clé JavaScript *return*.

### **Champs simples**

Les champs simples comme Texte, Nombre, Booléen, Date (sous forme de timestamp Unix UTC) et Durée (en millisecondes) peuvent être directement retournés par la valeur primitive JavaScript correspondante (type String ou Number).

### **Champ Liste**

Un Array JavaScript d'objets Liste complets doit être retourné. Pour le formatage, il est fortement recommandé d'utiliser la fonction *searchList* détaillée ci-dessous.

Vous ne devez pas utiliser de caractères spéciaux dans l'identifiant externe d'un élément de liste (exemple : le tiret bas « \_ »)

#### **Retourner des champs liste avec la fonction&#x20;*****searchList***

En utilisant la fonction *services.searchList()*, vous pouvez directement rechercher des éléments d'une liste Daxium-Air liée à la formule. C'est la seule option du champ formule de type Liste. Elle peut être utilisée avec une liste qui n'est liée à aucun champ liste de la fiche. Elle prend un objet JavaScript comme unique paramètre, avec quatre valeurs :

* **type** (obligatoire) : recherche via soit le *name*, soit l'*external\_id*, soit l'*id* ; c'est une String ;
* **value** (obligatoire) : la valeur sur laquelle effectuer la recherche ; le type de la valeur dépend du type :
  * les types *name* et *external\_id* doivent avoir une valeur de type String ;
  * le type *id* doit avoir une valeur de type Number ;
* **operator** (obligatoire) :
  * IS\_EQUAL\_TO : recherche spécifique, non sensible à la casse (majuscule=minuscule). À privilégier, notamment pour les ids
  * CONTAINS : recherche si « value » est contenue dans le résultat. Ex : « Luke » sera trouvé dans « Luke Smith ». Attention, CONTAINS rend la formule plus lente.
  * Compatibilité :

|               | name       | external\_id | id         |
| ------------- | ---------- | ------------ | ---------- |
| IS\_EQUAL\_TO | ✅          | ✅            | ✅ (défaut) |
| CONTAINS      | ✅ (défaut) | ✅ (défaut)   | ❌          |

* **parent** (optionnel) : vous pouvez saisir l'id du nœud de votre liste hiérarchique pour restreindre la recherche aux sous-éléments en dessous de ce nœud.

Voici un exemple d'utilisation :

```
return services.searchList({
	"type": "name",
	"value": "My list element"
});
```

En voici un autre :

```
return services.searchList({
	"type": "id",
	"value": 12345,
	"parent": 345
});
```

Cette fonction retourne un Array d'objets Liste complets :

```
[
	{
		"id": 123,
		"external_id": null,
		"caption": "Name of the element",
		"color": null,
		"imageId": "504d4a60-0db2-4bed-b234-a6d75e9950f4"
	},
	{
		"id": 124,
		"external_id": "456",
		"caption": "Other element",
		"color": null,
		"imageId": null
	}
]
```

### Formule de type : Localisation

![](/files/T6UtcgnwRndgg8Y4Fv0J)

Une formule de type Localisation retourne un résultat avec adresse et coordonnées.

Elle peut être utilisée, par exemple, pour récupérer la localisation contenue dans une fiche parente ou enfant (comme dans l'exemple ci-dessus).

Vous pouvez également créer votre propre objet de localisation à retourner ainsi :

```
return {address:"Blabla",lat:21.45,lng:51.545};
```

{% hint style="info" %}
**Le saviez-vous ?** L'avantage des formules typées est qu'elles se comportent comme un champ du même type. Dans le cas de la formule de type Localisation, le champ formule peut être utilisé comme paramètre de localisation par défaut pour la [vue carte](/user-documentation/documentation-utilisateur/use/views/map.md).
{% endhint %}

![](/files/Uw30irkWKB55l0wu4pO0)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://www.center.daxium-air.com/user-documentation/documentation-utilisateur/build/building-forms/fields/formulas.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
