> For the complete documentation index, see [llms.txt](https://ksaar.gitbook.io/ksaar-documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ksaar.gitbook.io/ksaar-documentation/les-elements/les-formules/bibliotheque-de-formules-classiques.md).

# Bibliothèque de formules classiques

Vous pouvez trouver sur cette page des exemples typiques de formules à utiliser dans Ksaar. N'oubliez pas d'adapter les variables à l'architecture de votre application.

{% hint style="success" %}
Les fonctions sur les formules sont disponibles dans chacune des sous-pages dédiées :&#x20;

* [Opérateurs](/ksaar-documentation/les-elements/les-formules/operateurs.md)
* [Fonctions de texte](/ksaar-documentation/les-elements/les-formules/fonctions-de-texte.md)
* [Fonctions mathématiques](/ksaar-documentation/les-elements/les-formules/fonctions-mathematiques.md)
* [Fonctions de date](/ksaar-documentation/les-elements/les-formules/fonctions-de-date.md)
* [Fonctions de formatage](/ksaar-documentation/les-elements/les-formules/fonctions-de-formatage.md)
* [Fonctions de liste](/ksaar-documentation/les-elements/les-formules/fonctions-de-liste.md)
* [Fonctions sur des champs Ksaar](/ksaar-documentation/les-elements/les-formules/fonctions-sur-des-champs-ksaar.md)
* [Fonctions de Logique et autres](/ksaar-documentation/les-elements/les-formules/fonctions-de-logique-et-autres.md)

Des exemples de formules prêtes à l'emploi se trouvent ici : [Bibliothèque de formules classiques](/ksaar-documentation/les-elements/les-formules/bibliotheque-de-formules-classiques.md)
{% endhint %}

## 🔤 Champs Texte

<details>

<summary>Liste de champs manquants</summary>

**Formule de type : Texte**

Cette formule vérifie si certains champs sont vides (comme le nom et la date de début de l'évènement ici). Si c’est le cas, elle affiche un message listant les champs manquants, sinon elle ne renvoie rien.

Vous pouvez évidemment modifier la liste `champs` pour ajouter ou supprimer des éléments.

```javascript
LET(
  champs, [
    IF(IS_EMPTY({txt_nom_event}), "Nom de l'événement", ""),
    IF(IS_EMPTY({dateH_debut_Event}), "Date de début", "")
  ],
  comment, "ajoutez les champs à tester",
    
  champsManquants, FILTER(champs, x => x != ""),
  
  IF(
    IS_EMPTY(champsManquants),
    "",
    "Attention, vous devez remplir " & JOIN(", ", champsManquants) & " pour continuer."
  )
)
```

Exemple de rendu :&#x20;

<figure><img src="/files/AwGFc8p4AKhdXLlTJscC" alt="" width="563"><figcaption></figcaption></figure>

</details>

<details>

<summary>Extraire l’extension d’un fichier</summary>

**Formule de type : Texte**

Cette formule extrait l’**extension d’un fichier** à partir d’un champ fichier `{fichier}`, puis la convertit en **minuscules**.

```javascript
 LET(
	 filename, FILENAME({fichier}), 
	 
	 LOWER(REGEX_EXTRACT(filename, "\.([^.]+)$"))
 )
```

Exemple de rendu :&#x20;

<figure><img src="/files/ApRbcl5VrprH4Q4Fe8re" alt="" width="375"><figcaption><p>À gauche : un fichier téléversé, à droite : l'extension utilisée</p></figcaption></figure>

</details>

<details>

<summary>Extraire le prénom ou le nom d'un champ qui réunit les deux</summary>

**Formules de type : Texte**

Cette formule extrait le prénom ou le nom à partir d’un champ `{prenom nom}` contenant le prénom et le nom concaténés, en inversant d’abord la chaîne, puis en récupérant la partie correspondant au prénom (qui se retrouve à droite après inversion), avant de la ré-inverser pour obtenir le prénom original.

Le procédé est similaire pour extraire le nom.

{% hint style="info" %}
Ces formules partent du principe que le prénom ne contient pas d'espaces, mais prennent en charge les noms à particule.
{% endhint %}

{% code title="Pour extraire le prénom" %}

```javascript
LET(
  txt, {prenom nom},
  rev, JOIN("", REVERSE(SPLIT(txt, ""))),
  pos, FIND(" ", rev),
  prenom_rev, LEFT(rev, pos - 1),

  prenom, JOIN("", REVERSE(SPLIT(prenom_rev, ""))),
  prenom
)
```

{% endcode %}

{% code title="Pour extraire le nom" %}

```javascript
LET(
  txt, {prenom nom},
  rev, JOIN("", REVERSE(SPLIT(txt, ""))),
  pos, FIND(" ", rev),
  nom_rev, RIGHT(rev, LEN(rev) - pos),
  
  nom, JOIN("", REVERSE(SPLIT(nom_rev, ""))),
  nom
)
```

{% endcode %}

Il existe aussi une option alternative :&#x20;

{% code title="Pour extraire le prénom" %}

```javascript
LET(
    prenom, {prenom nom},
    liste, SPLIT(prenom, " "),
    
    prenom, liste[0], 
    prenom
)
```

{% endcode %}

{% code title="Pour extraire le nom" %}

```javascript
LET(
  nom, {prenom nom},
  liste, SPLIT(nom, " "),
  liste_sliced, SLICE(liste, 2, LEN(liste) + 1),
  
  nom, JOIN(" ", liste_sliced),
  nom
)
```

{% endcode %}

Exemple d'application de la formule :&#x20;

<figure><img src="/files/4OL8eVG0TLN5tOZVPQm7" alt="" width="375"><figcaption><p>À gauche : le nom complet, à droite : le retour des formules</p></figcaption></figure>

</details>

<details>

<summary>Transformer un nombre ou prix en texte </summary>

**Formule de type : Texte**

Cette formule convertit un tarif mensuel en lettres (`{nb_prix_contrat}`) jusqu'à 99 999 €, en français, avec les règles spécifiques de la langue.

```javascript

LET(
  dizaineMillier, FLOOR({nb_prix_contrat} / 10000),
  millier, FLOOR( {nb_prix_contrat}/ 1000) % 10,
  resteMilliers, {nb_prix_contrat}  % 1000,
  centaine, FLOOR(resteMilliers / 100),
  resteCentaines, resteMilliers % 100,
  dizaine, FLOOR(resteCentaines / 10),
  unité, resteCentaines % 10,

  dizaineMillierTexte, 
    IF(dizaineMillier == 0, "", 
      IF(dizaineMillier == 1, 
        CHOOSE(millier, "ONZE MILLE", "DOUZE MILLE", "TREIZE MILLE", "QUATORZE MILLE", "QUINZE MILLE", "SEIZE MILLE", "DIX-SEPT MILLE", "DIX-HUIT MILLE", "DIX-NEUF MILLE") & " ", 
        CHOOSE(dizaineMillier, "DIX", "VINGT", "TRENTE", "QUARANTE", "CINQUANTE", "SOIXANTE", "SOIXANTE-DIX", "QUATRE-VINGT", "QUATRE-VINGT-DIX") & IF(millier == 0, " MILLE ", " ")
      )
    ),

  millierTexte, 
    IF(millier == 0 || dizaineMillier == 1, "", 
      IF(millier == 1, "MILLE ", 
        CHOOSE(millier,"", "DEUX MILLE", "TROIS MILLE", "QUATRE MILLE", "CINQ MILLE", "SIX MILLE", "SEPT MILLE", "HUIT MILLE", "NEUF MILLE") & " "
      )
    ),

  centaineTexte, 
    IF(centaine == 0, "", 
      IF(centaine == 1 && resteCentaines == 0, "CENT ", 
        CHOOSE(centaine, "CENT", "DEUX CENT", "TROIS CENT", "QUATRE CENT", "CINQ CENT", "SIX CENT", "SEPT CENT", "HUIT CENT", "NEUF CENT") & " "
      )
    ),

  dizaineTexte, 
    IF(dizaine == 0, "", 
      IF(dizaine == 1, 
        CHOOSE(unité, "DIX", "ONZE", "DOUZE", "TREIZE", "QUATORZE", "QUINZE", "SEIZE", "DIX-SEPT", "DIX-HUIT", "DIX-NEUF") & " ", 
        CHOOSE(dizaine, "DIX", "VINGT", "TRENTE", "QUARANTE", "CINQUANTE", "SOIXANTE", "SOIXANTE-DIX", "QUATRE-VINGT", "QUATRE-VINGT-DIX") & IF(unité > 0 && dizaine != 7 && dizaine != 9, "-", " ")
      )
    ),

  unitéTexte, 
    IF(dizaine == 1 || dizaine == 7 || dizaine == 9, "", 
      CHOOSE(unité, "UN", "DEUX", "TROIS", "QUATRE", "CINQ", "SIX", "SEPT", "HUIT", "NEUF") & " "
    ),

  texteFinal, TRIM(dizaineMillierTexte & millierTexte & centaineTexte & dizaineTexte & unitéTexte),

  IF(
    OR(
      RIGHT(texteFinal, 12) = "QUATRE-VINGT",
      RIGHT(texteFinal, 9) = "DEUX CENT", 
      RIGHT(texteFinal, 10) = "TROIS CENT", 
      RIGHT(texteFinal, 11) = "QUATRE CENT", 
      RIGHT(texteFinal, 9) = "CINQ CENT", 
      RIGHT(texteFinal, 8) = "SIX CENT", 
      RIGHT(texteFinal, 9) = "SEPT CENT", 
      RIGHT(texteFinal, 9) = "HUIT CENT", 
      RIGHT(texteFinal, 9) = "NEUF CENT"
    ),
    texteFinal & "S",
    texteFinal
  )
)
```

Exemple de rendu :&#x20;

<figure><img src="/files/r74cMyzGiUuWUihLwaG9" alt="" width="300"><figcaption><p>À gauche : le chiffre en entrée, à droite : le résultat de la formule</p></figcaption></figure>

Une autre formulation du même principe est la suivante. Cette formule transforme un chiffre en texte avec la dénomination appropriée et prend en compte les chiffres jusqu'aux trilliards.

```javascript
LET (
  Nombre, {nb_prix_contrat},
  absNombre, ABS(Nombre),
  estNegatif, Nombre < 0,
  estNul, Nombre = 0,
  separateur, "-",
  espaceInsecable, CHAR(8239),

  parts, SPLIT(FORMAT_CURRENCY(absNombre,"fr-FR","EUR"),","),

  centimes, "0" & LEFT(parts[1],2),
  blocs, FLATTEN([SPLIT(parts[0],espaceInsecable), centimes]),
  
  texte, {
    dénominations: ["centime", "Euro", "mille", "million", "milliard", "billion", "billiard", "trillion", "trilliard"],
    chiffres:  ["","un","deux","trois","quatre","cinq","six","sept","huit","neuf"],
    dixvingt: ["dix","onze","douze","treize","quatorze","quinze","seize","dix-sept","dix-huit","dix-neuf"],
    dizaines:  ["","dix","vingt","trente","quarante","cinquante","soixante","soixante","quatre-vingt","quatre-vingt"]
  },
  enLettre, LAMBDA(c, d, u,
    LET(
      Htxt, IF( c, IF(c>1 , texte.chiffres[c] & separateur & "cent", "cent"), ""),
      Ttxt, IF( d>1, texte.dizaines[d] & IF( u>0, "", "" ), "" ) & IF(d=8 && u=0,"s",""),
      Utxt, IF( d+u,  IF(OR(d=1,d=7,d=9),texte.dixvingt[u], IF(u=1 && d>0 && d<8, "et" & separateur, "") & texte.chiffres[u]), ""),
      JOIN(separateur, FILTER([Htxt,Ttxt,Utxt],x=>x))
    )),
  converted, REVERSE(
    MAP(blocs, LAMBDA(bloc, 
      LET(
        numbers, MAP(
          FLATTEN([REVERSE(SPLIT(bloc,"")),0,0]),
          LAMBDA(x, TO_NUMBER(x))
          ),
        centaine, numbers[2],
        dizaine, numbers[1],
        unites, numbers[0],
        { lettres: enLettre(centaine, dizaine, unites), chiffres: TO_NUMBER(bloc) }
      )
    )
  )),
  parts, MAP([0,1,2,3,4,5,6,7,8],i => 
  LET(last,LEN(converted)-1,
    IF(
      i<=last,
      LET(
        number, converted[i].chiffres,
        txtNum, converted[i].lettres,
        preparateur, IF(
            AND(i>=1, i<last, number), 
            separateur,
            IF(AND(i=0, number, i<last), " ", "")),
        quantite, IF(AND(number=1, i=2), "", txtNum),
        pluriel, IF(AND(quantite, i=1, absNombre % 100 = 0, absNombre > 100, absNombre % 1000 <>0),"s",""),
        separation, IF(i<=1," ",IF(AND(quantite, number>1 || i=last), separateur, "")),
        denomination, IF(
          number || AND(i=1, absNombre>=1),
          texte.dénominations[i] & IF(
            AND(i=0 || i>2, number>1) || AND(i=1, absNombre>1),
            "s", ""),
          ""
        ),
        preparateur & quantite & pluriel & separation & denomination
      )
      ,"" 
    )
  )),
  resultat, JOIN("",REVERSE(parts)),
  IFS(estNul, "zéro Euro", estNegatif, "moins " & resultat, true, resultat)
)
```

**Exemple de rendu :**&#x20;

<figure><img src="/files/ZvXysNvaWA8eUSWsweeW" alt="" width="375"><figcaption><p>À gauche : le chiffre en entrée, à droite : le prix au format texte avec denominations</p></figcaption></figure>

</details>

## 💻 HTML

<details>

<summary>Notification formatée en HTML</summary>

**Formule de type : HTML**

Cette formule construit un texte HTML formaté affichant le nom du créateur, la date de création, les agents notifiés (avec `@`) et le contenu du commentaire. Elle est utilisée pour présenter un commentaire dans l’interface Ksaar.

{% hint style="info" %}
La fonction `MAP` a besoin d'une liste en premier paramètre, d'où l'utilisation d'un liaison multiple.
{% endhint %}

```javascript
LET(
  createur, "<h6 style = 'margin-bottom :0px'>" & {nom du createur du commentaire} & " - " & FORMAT_DATE({Créé le}, "dddd DD/MM/YY HH:mm") & "</h6>",

  listeNotifies, MAP({liaison multiple vers les noms des agents notifies}, x => "@" & x),
  notifiesTexte, JOIN(", ", listeNotifies)

  notifies, IF(
    IS_EMPTY(notifiesTexte), 
    "", 
    "<h6 style = 'color : #896853'>" & notifiesTexte & "</h6>"
  ),

  contenu, {contenu du commentaire},

  createur & notifies & contenu
)
```

Exemple de retour :&#x20;

<figure><img src="/files/mL345n7hsa2iEk7LPg30" alt="" width="563"><figcaption><p>À gauche : l'association et son produit vendu, à droite : le commentaire stylisé avec du HTML</p></figcaption></figure>

</details>

<details>

<summary>Liaison multiple sous forme de pastilles</summary>

**Formule de type : HTML**

Cette formule génère un bloc HTML stylisé contenant des pastilles (badges) pour chaque prénom d’enfant listé dans le champ `{lnkM_enfant_parent → txt_prenom_enfant}`, en les séparant par des virgules et en les affichant verticalement dans un conteneur avec un style personnalisé.

```javascript
LET(

  style, "<style> #pastilles_uniformes .badge-grey "
  &" {display: inline-block;background-color: #6c757d;color: #fff;padding: 0.25rem 0.75rem;margin-bottom: 0.5rem;border-radius: 1rem;font-size: 0.875rem;text-align: center;width: 100%;}"
  &"#pastilles_uniformes .d-flex {gap: 0.5rem;}</style>",


  tags, SPLIT(TO_TEXT({lnkM_enfant_parent → txt_prenom_enfant}), ","),
  spans, JOIN("", MAP(tags, tag => "<span class='badge-grey'>" & TRIM(tag) & "</span>")),
  style & "<div id='pastilles_uniformes' class='container my-3' style='max-width: 200px;'><div class='d-flex flex-column'>" & spans & "</div></div>"
)
```

Exemple de rendu :&#x20;

<figure><img src="/files/rzixLDHdHp7nRQkmlK57" alt="" width="375"><figcaption></figcaption></figure>

</details>

## 🗓️ Dates

<details>

<summary>Récupérer la date la plus récente </summary>

**Formule de type : Date**

Cette formule retourne la date la plus récente dans la liste `{listeDate}` en triant les dates par ordre décroissant, puis en prenant la première. Elle est utile pour identifier la dernière date d’un ensemble.

```javascript
LET(
  listeDate, {listeDate},
  INDEX(SORT(listeDate,(a,b) => DATE_DIFF(b,a)),1)
)
```

Exemple de rendu :&#x20;

<figure><img src="/files/93g48BvKMIu4C6URJ29r" alt="" width="563"><figcaption><p>Dans les 3 premières colonnes : un choix de date, dans la dernière colonne : la date la plus récente des 3</p></figcaption></figure>

</details>

<details>

<summary>Récupérer le numéro du jour</summary>

**Formule de type : Nombre**

Cette formule transforme le résultat de `WEEKDAY(date)` pour obtenir le jour de la semaine au format ISO (lundi = 1, dimanche = 7).

```javascript
LET(
  date, {Date},
  jour, IF(WEEKDAY(date)=1,7,WEEKDAY(date)-1)
)
```

Exemple de retour :&#x20;

<figure><img src="/files/83Q20qW42ifU0Jh8ood4" alt="" width="563"><figcaption><p>À gauche : une date, à droite : le numéro du jour de la semaine</p></figcaption></figure>

</details>

<details>

<summary>Formatage de date de début et de fin</summary>

**Formule de type : Texte**

Cette formule formate une plage horaire lisible à partir des champs `{dateH_debut_event}` et `{dateH_fin_event}`, en affichant les dates et heures de début et de fin avec des transitions adaptées, tout en gérant les cas où la date est vide ou où les deux dates sont le même jour.

```javascript
LET(
    dateDebut, {dateH_debut_event},
    dateFin, {dateH_fin_event},
    
    txtDateDebut, FORMAT_DATE(dateDebut,"fr-FR, dateStyle=full")& " ",
    txtHeureDebut, FORMAT_DATE(dateDebut, "HH[h]mm") & " ",

    noDate, IF(
        IS_EMPTY(dateDebut),
        true,
        false
    ),

    sameDay, IF(
        FORMAT_DATE(dateDebut,"DD/MM/YYYY")==FORMAT_DATE(dateFin,"DD/MM/YYYY"),
        true, 
        false
    ),

    txtTransitionUn, IF(
        noDate,
        "",
        " de "
    ),


    txtTransitionDeux, 
        IF(
            noDate, 
            " ",
            IF(
                sameDay,
                " à ",
                " au "
            )
        ),

    txtDateFin, IF(
        sameDay,
        " ",
        FORMAT_DATE(dateFin,"fr-FR, dateStyle=full") & " à " & " "
    ),

    txtHeureFin, FORMAT_DATE(dateFin, "HH[h]mm") & " ",

    return, TRIM(txtDateDebut & txtTransitionUn &  txtHeureDebut & txtTransitionDeux & txtDateFin & txtHeureFin),
    return
)
```

Exemple de rendu : &#x20;

<figure><img src="/files/F7Oqth5x7TFMX4pk2E0h" alt="" width="563"><figcaption><p>À gauche : les deux dates et heures, à droite : la plage sous format texte</p></figcaption></figure>

</details>

<details>

<summary>Vérifier le chevauchement de 2 plages de dates</summary>

**Formule de type : Booléen**

Cette formule vérifie si deux plages de dates `{debutPlageA} {finPlageA}` et `{debutPlageB} {finPlageB}`, se chevauchent. Elle renvoie True si les deux se chevauchent et False sinon.

```javascript
LET(
    debutA, {debutPlageA},
    finA, {finPlageA},
    debutB, {debutPlageB},
    finB, {finPlageB},
    IF(
        debutA <= finB && finA >= debutB,
        true,
        false
    )
)
```

Exemple de rendu :&#x20;

<figure><img src="/files/OZyAMCVbrg3bHwFdhgZO" alt="" width="563"><figcaption><p>Dans les colonnes de gauche : les bornes des 2 plages de dates, à droite : la validation de chevauchement</p></figcaption></figure>

</details>

## 🔗 Liaisons

<details>

<summary>Filtrer une liste issue d'une liaison multiple</summary>

**Formule de type : Liaison** (multiple, utilisateur en fonction de vos besoins)

Cette formule filtre les éléments d’une liste `liaisonAFiltrer` en se basant sur une deuxième liste parallèle `liaisonUtiliseePourFiltre`, en appliquant une condition à chaque paire d’éléments de même position. Elle est utile pour garder uniquement certains éléments d’une liaison multiple selon des critères associés (exemple : statut, valeur, date).

Veillez à compléter les `...` par la condition qui vous intéresse. Par exemple si vous voulez garder tous les éléments supérieurs à 100 il suffit d'écrire `INDEX(liaisonUtiliseePourFiltre,i+1) > 100 )` ou si vous ne voulez garder que les éléments qui ne sont pas nuls `!IS_EMPTY(INDEX(liaisonUtiliseePourFiltre, i+1))`.

```javascript
LET(
    liaisonAFiltrer, {LM_nourritureFavorite_contact},
    liaisonUtiliseePourFiltre, {LM_nourritureFavorite_contact → Prix},
    FILTER(liaisonAFiltrer, (x, i) => INDEX(liaisonUtiliseePourFiltre, i+1) ...)
)
```

Exemple de retour :&#x20;

<figure><img src="/files/4dQnWP9PUMpua3TuNgOn" alt="" width="563"><figcaption><p>À gauche : des contacts, au milieu : les articles choisis, à droite : les articles au-dessus d'un certain prix</p></figcaption></figure>

</details>

<details>

<summary>Récupérer les enregistrements d'une liaison multiple à travers une liaison multiple</summary>

Dans cet exemple nous utilisons deux formules afin de récupérer les enregistrements d'une liaison multiple en passant par une autre liaison multiple.

Nous avons 3 tables :&#x20;

* table élèves (contient leur nom)
* table classe (contient le nom de la classe, une liaison multiple vers les élèves de la classe `{lnkM_eleve_classe}` et la première formule)
* table prof (contient leur nom, une liaison multiple vers les classes du prof `{lnkM_classe_prof}`, et la deuxième formule)

Dans la table prof, nous allons passer à travers une liaison multiple de la table classe pour récupérer les enregistrements de la table élèves.&#x20;

**Première formule de type : JSON** (`fml_json_eleve_classe` )

```javascript
{lnkM_eleve_classe}
```

**Deuxième formule de type : Liaison multiple**&#x20;

Liée à la table : élèves ; Champ : txt\_nom\_eleve

```javascript
FLATTEN({lnkM_classe_prof → fml_json_eleve_classe})
```

Exemple de rendu :&#x20;

<figure><img src="/files/9OuLkY9iTO9acNqeE81i" alt=""><figcaption><p>De gauche à droite : le nom du prof, ses classes attitrées, tous les élèves que le prof enseigne sous forme de texte, tous les élèves que le prof enseigne sous forme de pastille</p></figcaption></figure>

Ici le résultat de la formule de liaison a été passée dans une formule de texte pour plus de clarté

</details>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://ksaar.gitbook.io/ksaar-documentation/les-elements/les-formules/bibliotheque-de-formules-classiques.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
