Aller au contenu principal

🧮 Marqueurs avancé - Formules et conditions dans les modèles

Ce guide avancé s'adresse à celles et ceux qui veulent aller plus loin que la simple insertion de variables : mettre en forme une date ou un montant, afficher un texte selon une condition, gérer plusieurs cas ou répéter un bloc.

Tout ce qui est décrit ici fonctionne avec le moteur de modèles téo (téo edit), donc dans :

  • les modèles d'emails et leur objet ;
  • les modèles de SMS ;
  • les modèles de documents ;
  • les automatisations (envois automatiques).
Avant de commencer

Si vous débutez avec les variables, lisez d'abord Modèles de messages et documents. Cette page suppose que vous savez déjà insérer une variable comme {d.benef.prenom}.


1. Rappel : les variables (marqueurs)

Une variable, ou marqueur, est un emplacement que téo remplace automatiquement par la vraie donnée.

Syntaxe : {d.categorie.champ}

Le préfixe d. signifie « données de téo », suivi du chemin vers l'information.

VariableRésultat
{d.benef.prenom}Prénom du bénéficiaire
{d.benef.nom}Nom du bénéficiaire
{d.dossier.numero}Numéro du dossier
{d.seance.date}Date de la séance
{d.conseiller.nom}Nom du conseiller
{d.centre.nom}Nom de votre structure
{d.global.date}Date du jour de génération
Le bouton « Variables »

Dans l'éditeur de modèle, le bouton Variables ouvre la liste complète des marqueurs et les insère en un clic. La référence exhaustive est aussi sur edit.teoapp.fr.

Les données dépendent du contexte

Un modèle est toujours utilisé dans un contexte (un dossier, une facture, une séance, une personne…). Seules les variables de ce contexte sont remplies.

Contexte du modèleFamilles de variables disponibles
Dossierdossier, benef, conseiller, prestation, financeur, convention, seances, global
Séanceseance, dossier, benef, conseiller, global
Facture / Devisfacture (ou devis), banque, dossier ou groupe, global
Groupegroupe, benefs, conseiller, prestation, seances, global
Personne / Sociétépersonne ou societe, global
Erreur fréquente

Une variable de séance ({d.seance.date}) ne fonctionne pas dans un modèle utilisé depuis une personne : la donnée n'existe pas dans ce contexte et la variable reste vide.


2. Le principe des formules

Une formule enchaîne une variable avec une ou plusieurs instructions, séparées par le caractère deux-points :.

{d.maVariable:instruction1:instruction2}

Chaque instruction transforme le résultat de la précédente. On lit la formule de gauche à droite.

{d.benef.nom:upperCase}

→ prend le nom du bénéficiaire, puis le passe en majuscules.

Règle d'or : pas d'espaces « libres » à l'intérieur des accolades

téo supprime les espaces situés à l'intérieur d'un marqueur {…}. Conséquences, à retenir dès maintenant :

  • un texte à afficher doit être placé entre guillemets simples : show('Sur place') (et non show(Sur place), qui donnerait Surplace) ;
  • une date avec des espaces ne s'écrit pas en un seul format : utilisez formatD(LL) ou plusieurs marqueurs séparés par des espaces (voir §3.1).

Les espaces de votre phrase, en dehors des accolades, sont évidemment conservés.

Les sections suivantes couvrent les trois familles de formules : mettre en forme, afficher selon une condition, répéter.


3. Mettre en forme une valeur

3.1 Dates et heures — formatD

Par défaut, une date s'affiche dans un format technique (2025-03-15). formatD(...) la présente proprement, en indiquant le format de sortie.

{d.seance.date:formatD(DD/MM/YYYY)}

15/03/2025

Codes de format les plus utiles :

CodeSignificationExemple
DDJour sur 2 chiffres15
ddddJour de la semaine en toutes lettressamedi
MMMois sur 2 chiffres03
MMMMMois en toutes lettresmars
YYYYAnnée sur 4 chiffres2025
LLDate longue localisée15 mars 2025

Dates « longues » : LL ou plusieurs marqueurs

Comme les espaces sont supprimés à l'intérieur d'un marqueur, on n'écrit pas formatD(dddd DD MMMM YYYY) (cela donnerait samedi15mars2025). Deux solutions correctes :

Le {d.seance.date:formatD(LL)}

Le 15 mars 2025

Le {d.seance.date:formatD(dddd)} {d.seance.date:formatD(LL)}

Le samedi 15 mars 2025

(ici l'espace est placé entre les deux marqueurs, donc conservé.)

Heures : préciser le format d'entrée

Les heures sont stockées sous la forme 09:00:00. Pour les afficher en 9h00, ajoutez un second paramètre : le format dans lequel l'heure est stockée.

{d.seance.debut:formatD(H[h]mm, hh:mm:ss)}

9h00

  • H[h]mm = format de sortie (les crochets [h] affichent le « h » littéral) ;
  • hh:mm:ss = format d'entrée (placé après la virgule).

3.2 Montants — formatC

formatC() met en forme un montant (séparateurs, symbole monétaire).

Total à régler : {d.facture.totalTTC:formatC()}

Total à régler : 1 800,00 €

Utilisez-le pour tous les montants : {d.facture.totalHT:formatC()}, {d.dossier.montant.previ:formatC()}

Les montants négatifs (avoirs) sont gérés (-250,50 €). Pour des euros entiers sans décimales, indiquez la précision 0 :

{d.facture.totalHT:formatC(0)}

1 500 €

3.3 Nombres — formatN

{d.dossier.dureePrevi:formatN()} heures prévues

3.4 Texte

FormuleEffetExemple
:upperCaseTOUT EN MAJUSCULESDupontDUPONT
:lowerCasetout en minusculesDUPONTdupont
:ucFirstPremière lettre en majusculemarieMarie
:ucWordsPremière Lettre De Chaque Motmarie curieMarie Curie
:unaccentSupprime les accentsTéléphoneTelephone

Exemple :

{d.benef.civilite} {d.benef.nom:upperCase} {d.benef.prenom:ucFirst}

Madame EXEMPLE Marie

3.5 Valeur par défaut (champ vide)

Si une donnée peut être absente, prévoyez un texte de remplacement avec ifEM (« si vide »).

{d.benef.tel.mobile:ifEM:show('Non renseigné')}

→ affiche Non renseigné quand le mobile est vide, sinon le numéro.

Pourquoi c'est important

Sans valeur par défaut, un champ vide laisse un trou dans la phrase (« Votre conseiller vous contactera »).

3.6 Petits calculs — add sub mul div

Vous pouvez calculer une valeur à partir d'un nombre : addition (add), soustraction (sub), multiplication (mul), division (div).

Montant TTC estimé : {d.facture.totalHT:mul(1.2):formatC()}

→ multiplie le HT par 1,2 puis met en forme le montant.


4. Conditions : afficher un texte selon les données

C'est le cœur de cette page : afficher un contenu différent selon la valeur d'une donnée.

4.1 Si / Sinon — ifEQ + show + elseShow

La formule de base se lit : « si la donnée est égale à une valeur, afficher un texte, sinon afficher un autre texte ».

{d.seance.typeContact:ifEQ('Présentiel'):show('Merci de venir sur place'):elseShow('Un lien de connexion vous sera transmis')}
ÉlémentRôle
ifEQ('Présentiel')Si la valeur est égale à Présentiel
show('…')afficher ce texte
elseShow('…')Sinon, afficher cet autre texte

elseShow est facultatif. Le texte est toujours entre guillemets simples.

Pas d'apostrophe dans le texte de show / elseShow

L'apostrophe ' délimite le texte : show('l'entretien') casse la formule. Reformulez sans apostrophe (« la séance » plutôt que « l'entretien »), ou utilisez (apostrophe typographique).

4.2 Tous les tests disponibles

ifEQ n'est qu'un test parmi d'autres. Chaque test est suivi d'un show (et éventuellement elseShow).

TestSignification
ifEQ(valeur)égal à la valeur
ifNE(valeur)différent de la valeur
ifGT(valeur)strictement supérieur à
ifGTE(valeur)supérieur ou égal à
ifLT(valeur)strictement inférieur à
ifLTE(valeur)inférieur ou égal à
ifEMla donnée est vide
ifNEMla donnée n'est pas vide
ifIN(valeur)la donnée contient la valeur
ifNIN(valeur)la donnée ne contient pas la valeur
ifIN : repérer un mot dans un texte

ifIN teste si un champ contient la valeur indiquée — pratique pour réagir à un mot présent dans un texte libre :

{d.dossier.objectif:ifIN('reconversion'):show('Parcours de reconversion'):elseShow('Autre parcours')}

Pour afficher un texte selon la valeur exacte d'un champ (une modalité parmi plusieurs, par exemple), voir §4.4.

Exemple avec un nombre :

{d.dossier.nbJours:ifGT(90):show('Accompagnement longue durée'):elseShow('Accompagnement court')}

Champ « oui / non » (booléen)

Pour un champ vrai/faux (par exemple « ce document est-il un avoir ? »), testez true ou false :

{d.facture.avoir:ifEQ(true):show('AVOIR'):elseShow('FACTURE')}
On ne compare pas des dates avec ifGT / ifLT

Les comparaisons ifGT, ifLT… ne fonctionnent que sur des nombres. Sur une date (2025-03-15), elles ne donnent pas le bon résultat. Pour réagir à une date, comparez plutôt un nombre déjà calculé par téo (ex. {d.dossier.nbJours:ifGT(90)…}) ou un champ texte d'état.

4.3 Choix multiple (« switch »)

Pour afficher un texte différent par valeur, enchaînez plusieurs ifEQ:show. Dès qu'une condition est vraie, le reste est ignoré ; elseShow couvre tous les autres cas.

{d.benef.sexe:ifEQ('H'):show('Cher Monsieur'):ifEQ('F'):show('Chère Madame'):elseShow('Bonjour')}

4.4 Comparer à plusieurs valeurs

Besoin le plus courant : « plusieurs modalités donnent le même texte » (ex. 3 types de contact sur 4 mènent au même message).

Méthode recommandée — enchaîner ifEQ avec le même show

On réutilise le principe du switch, en répétant le même show pour chaque valeur qui doit donner le même résultat :

{d.seance.typeContact:ifEQ('Téléphone'):show('Votre rendez-vous se fera à distance.'):ifEQ('Visio'):show('Votre rendez-vous se fera à distance.'):ifEQ('Distanciel'):show('Votre rendez-vous se fera à distance.'):elseShow('Votre rendez-vous se fera en présentiel.')}

Simple et fiable : aucune notion avancée, on lit la formule de gauche à droite.

Méthode compacte — or (OU) et and (ET)

On peut aussi combiner des tests :

  • or = OU (il suffit qu'un test soit vrai) ;
  • and = ET (il faut que tous les tests soient vrais).

Pour rappeler une donnée après or / and, on indique le nom du champ précédé d'un point (.champ). ⚠️ Ce chemin est relatif à l'objet de votre première variable : pour {d.seance.typeContact…}, on réécrit simplement .typeContact (le champ frère), pas .seance.typeContact.

OU sur la même donnée :

{d.seance.typeContact:ifEQ('Téléphone'):or(.typeContact):ifEQ('Visio'):show('À distance'):elseShow('Sur place')}

ET entre deux champs du même objet (ici deux champs de dossier) :

{d.dossier.nbJours:ifGT(90):and(.montant.previ):ifGT(2000):show('Dossier majeur'):elseShow('Dossier standard')}
Quelle méthode choisir ?
  • Pour « une seule donnée, plusieurs valeurs possibles » (votre cas le plus fréquent), la méthode recommandée (enchaîner les ifEQ:show) est la plus lisible.
  • or / and deviennent utiles pour combiner deux champs (ET / OU), mais demandent d'écrire le bon chemin relatif (.champFrère).

4.5 Afficher ou masquer un bloc entier

Pour conditionner un paragraphe, une ligne ou un tableau — et notamment n'afficher la valeur d'un marqueur que si une condition est vraie — utilisez les bornes de bloc :

FormuleEffet si la condition est vraie
showBeginshowEndAffiche la zone entre les deux bornes
hideBeginhideEndMasque la zone entre les deux bornes

Exemple — n'afficher l'adresse que pour un rendez-vous en présentiel :

{d.seance.typeContact:ifEQ('Présentiel'):showBegin}
Lieu : {d.seance.lieu.nom}, {d.seance.lieu.adresse} {d.seance.lieu.cp} {d.seance.lieu.ville}
{d.seance.typeContact:showEnd}

Tout ce qui est compris entre showBegin et showEnd (texte et marqueurs) n'apparaît que si la condition est vraie. Les deux bornes utilisent le même chemin de variable.

Afficher la valeur d'un champ dans une condition

Pour insérer la valeur d'un marqueur quand une condition est remplie, passez toujours par un bloc showBegin/showEnd (comme ci-dessus). show() ne sert qu'à afficher un texte fixe entre guillemets.


5. Répéter un bloc (listes)

Certaines données sont des listes : les séances d'un dossier, les lignes d'une facture, les bénéficiaires d'un groupe… On les reconnaît à leur nom au pluriel : seances, lignes, benefs.

5.1 La boucle avec [i] et [i+1]

Pour répéter un bloc pour chaque élément d'une liste, il faut deux lignes :

  1. une ligne modèle qui utilise l'indice [i] (« l'élément courant ») — c'est elle qui est répétée ;
  2. une seconde ligne qui reprend au moins un marqueur de la liste avec [i+1]. Cette ligne indique où s'arrête le motif et n'apparaît pas dans le résultat.
La 2ᵉ ligne [i+1] est obligatoire

Sans la ligne [i+1], téo ne sait pas quelle zone répéter : la liste ne se répète pas. Un seul marqueur [i+1] suffit (inutile de recopier toute la ligne), par exemple {d.dossier.seances[i+1].date}.

Dans un tableau Word, créez deux lignes de tableau :

DateActionDurée
{d.dossier.seances[i].date:formatD(DD/MM/YYYY)}{d.dossier.seances[i].action}{d.dossier.seances[i].duree:formatD(H[h]mm, hh:mm:ss)}
{d.dossier.seances[i+1].date}

téo répète la 1ʳᵉ ligne pour chaque séance, puis supprime la ligne [i+1].

Exemple sur les lignes d'une facture (toujours 2 lignes de tableau) :

DésignationQtéPU HTMontant HT
{d.facture.lignes[i].description}{d.facture.lignes[i].quantite}{d.facture.lignes[i].puHT:formatC()}{d.facture.lignes[i].montantHT:formatC()}
{d.facture.lignes[i+1].description}
Comment ça marche

téo prend la 1ʳᵉ ligne ([i]) comme modèle et la répète pour chaque élément, quel que soit leur nombre. La 2ᵉ ligne ([i+1]) n'est qu'un repère de fin : elle disparaît à la génération.

5.2 Cibler un élément précis avec [i=0]

Pour viser un élément, indiquez son rang. La numérotation commence à 0 : le premier élément est [i=0], le deuxième [i=1], etc.

Exemple — informations du premier rendez-vous du dossier (les espaces sont entre les marqueurs) :

Votre premier rendez-vous : {d.dossier.seances[i=0].date:formatD(LL)} à {d.dossier.seances[i=0].debut:formatD(H[h]mm, hh:mm:ss)}, {d.dossier.seances[i=0].lieu.nom}.

5.3 Ne répéter que certains éléments (filtre)

Vous pouvez filtrer une boucle pour ne garder que les éléments correspondant à un critère, en ajoutant , champ='valeur' dans les crochets. Le filtre doit être répété à l'identique sur la ligne [i+1].

Exemple — ne lister que les séances en présentiel :

DateAction
{d.dossier.seances[i, typeContact='Présentiel'].date:formatD(DD/MM/YYYY)}{d.dossier.seances[i, typeContact='Présentiel'].action}
{d.dossier.seances[i+1, typeContact='Présentiel'].date}

5.4 Compter et gérer une liste vide

Compter les éléments d'une liste avec len :

Ce dossier comporte {d.dossier.seances:len} séance(s).

Masquer un tableau entier quand la liste est vide : encadrez-le entre ifEM:hideBegin et hideEnd (le tableau disparaît s'il n'y a aucun élément).

{d.dossier.seances:ifEM:hideBegin}
… votre tableau de séances ici …
{d.dossier.seances:hideEnd}

5.5 Lister des valeurs simples — arrayJoin

Pour une liste de valeurs simples (texte/nombres), arrayJoin les assemble en une seule ligne :

Mots-clés : {d.dossier.motsCles:arrayJoin(', ')}

Mots-clés : VAE, Bilan, CEP

Liste d'éléments « complexes »

arrayJoin ne fonctionne que sur des listes de valeurs simples. Pour une liste d'objets (bénéficiaires, séances, lignes de facture…), utilisez une boucle [i] / [i+1] (§5.1).

5.6 Boucles imbriquées

Une boucle peut en contenir une autre (par exemple : pour chaque groupe, lister ses bénéficiaires). Chaque niveau a son couple [i] / [i+1].

{d.groupes[i].nom} : {d.groupes[i].benefs[i].prenom}{d.groupes[i].benefs[i+1].prenom}
{d.groupes[i+1].nom}

6. Exemple complet commenté

Un extrait de convocation par email combinant variables, mise en forme et conditions :

Bonjour {d.benef.prenom:ucFirst},

Nous confirmons votre rendez-vous « {d.seance.action} » le {d.seance.date:formatD(dddd)} {d.seance.date:formatD(LL)} à {d.seance.debut:formatD(H[h]mm, hh:mm:ss)}.

{d.seance.typeContact:ifEQ('Téléphone'):show('Ce rendez-vous se fera à distance ; un lien vous sera transmis.'):ifEQ('Visio'):show('Ce rendez-vous se fera à distance ; un lien vous sera transmis.'):elseShow('Ce rendez-vous se déroulera sur place :')}

{d.seance.typeContact:ifEQ('Présentiel'):showBegin}
{d.seance.lieu.nom}, {d.seance.lieu.adresse} {d.seance.lieu.cp} {d.seance.lieu.ville}
{d.seance.typeContact:showEnd}

Votre conseiller : {d.conseiller.prenom:ucFirst} {d.conseiller.nom:upperCase}

{d.centre.nom}

7. Dépannage

« La condition affiche toujours le “sinon”, jamais le bon texte »

C'est l'erreur la plus fréquente. La comparaison ifEQ est exacte : elle distingue les majuscules/minuscules et les accents.

ifEQ('téléphone') ne correspond pas à une donnée enregistrée Téléphone (T majuscule). Résultat : la condition est toujours fausse, et seul le elseShow s'affiche.

Solutions :

  1. Recopiez le libellé exact tel qu'il apparaît dans votre paramétrage (mêmes majuscules, mêmes accents). Pour les modalités de séance : Admin > types de contact.
  2. Pour ignorer la casse et les accents, normalisez la valeur avant de comparer, et comparez à une valeur elle aussi en minuscules sans accent :
{d.seance.typeContact:unaccent:lowerCase:ifEQ('telephone'):show('…'):elseShow('…')}
Le bon réflexe

Avant d'écrire une condition sur une donnée « à choix » (modalité, civilité, état…), vérifiez les libellés exacts dans le paramètre correspondant et copiez-les à l'identique.

« Mon texte est collé / il manque des espaces »

Les espaces à l'intérieur d'un marqueur sont supprimés. Mettez le texte de show / elseShow entre guillemets (show('Sur place')), et composez les dates longues avec formatD(LL) ou plusieurs marqueurs (voir §3.1).

« Ma liste ne se répète pas (un seul élément s'affiche) »

Il manque la 2ᵉ ligne de la boucle : il faut une ligne avec [i] et une seconde ligne contenant au moins un marqueur [i+1] (voir §5.1). Évitez aussi de mélanger deux listes dans la même boucle.

« Ma variable n'est pas remplacée et reste affichée telle quelle »

  • Vérifiez la syntaxe exacte : accolades { }, points, et le préfixe d..
  • Vérifiez que la variable existe (bouton Variables de l'éditeur).
  • Vérifiez le contexte : une variable séance ne fonctionne pas depuis un modèle « personne » (voir §1).

« Que se passe-t-il si une donnée est absente ? »

Pas de panique : un marqueur dont la donnée est vide ou manquante (par exemple un lieu non renseigné) s'affiche simplement en blanc — il ne provoque pas d'erreur et ne bloque pas la génération. Pour remplacer ce blanc par un texte, voir la valeur par défaut.

Tester avant de diffuser

Le bouton Tester d'un modèle le génère avec un dossier réel. Testez systématiquement vos conditions avec plusieurs cas (par exemple une séance présentielle et une séance à distance) pour vérifier les deux branches.


8. Aide-mémoire

BesoinFormule type
Date courte{d.seance.date:formatD(DD/MM/YYYY)}
Date longue{d.seance.date:formatD(LL)}
Jour + date{d.seance.date:formatD(dddd)} {d.seance.date:formatD(LL)}
Heure « 9h00 »{d.seance.debut:formatD(H[h]mm, hh:mm:ss)}
Montant en €{d.facture.totalTTC:formatC()}
Majuscules{d.benef.nom:upperCase}
Initiale en majuscule{d.benef.prenom:ucFirst}
Valeur par défaut{d.benef.tel.mobile:ifEM:show('Non renseigné')}
Petit calcul{d.facture.totalHT:mul(1.2):formatC()}
Si / sinon{d.x:ifEQ('A'):show('…'):elseShow('…')}
Oui / non (booléen){d.facture.avoir:ifEQ(true):show('Avoir'):elseShow('Facture')}
Choix multiple{d.x:ifEQ('A'):show('…'):ifEQ('B'):show('…'):elseShow('…')}
Bloc conditionnel{d.x:ifEQ('A'):showBegin}{d.x:showEnd}
Répéter une liste (2 lignes)ligne 1 {d.dossier.seances[i].date} + ligne 2 {d.dossier.seances[i+1].date}
Filtrer une liste{d.dossier.seances[i, typeContact='Présentiel'].action}
Compter une liste{d.dossier.seances:len}
Premier élément{d.dossier.seances[i=0].date}

Voir aussi

Cet article vous a été utile ?