États personnalisés
Construire sa propre spécification pas à pas — source, colonnes, colonnes calculées, filtres, période, tri, regroupements et options — avec des exemples complets.
L'idée
Un état personnalisé est une spécification que vous envoyez à preview ou generate (ou que vous
enregistrez comme modèle). Elle décrit :
{
"sourceCode": "RECEIPTS",
"columns": [ ... ],
"filters": [ ... ],
"scope": { ... },
"grouping": [ ... ],
"sorting": { ... },
"options": { ... }
}Vous pouvez partir de zéro (sourceCode + ce que vous voulez) ou partir d'un état du catalogue
en donnant definitionCode : tout ce que vous omettez prend alors la valeur de l'état, tout ce que vous
donnez la remplace. Le chemin le plus sûr est de lire defaultSpec avec
GET /provider/reports/catalog/{code} et de le modifier.
Règles de fusion avec definitionCode : columns vide → colonnes de l'état ; filters absent →
filtres de l'état, filters: [] → aucun filtre ; grouping absent → regroupement de l'état,
grouping: [] → regroupement désactivé ; sorting absent → tri de l'état.
La source — sourceCode
Une source = une table de données avec son grain. Choisissez-la selon la question posée :
| Vous voulez une ligne par… | Source |
|---|---|
| contrat | CONTRACTS |
| avenant (tout acte, émis ou non) | ENDORSEMENTS |
| attestation | CERTIFICATES |
| quittance (avenant émis), avec primes, taxes, encaissement et répartition | RECEIPTS |
| règlement non confirmé (impayé) | UNPAID |
| règlement confirmé (encaissement) | COLLECTIONS |
| part de compagnie sur un avenant (apéritrice, coassureur, Pool) | COINSURANCE |
| séjour d'un véhicule sur un contrat | FLEET |
| souscripteur | SUBSCRIBERS |
La liste des champs de chaque source est dans Sources et champs et
dans GET /provider/reports/sources/{code}/fields.
Les colonnes — columns
Chaque colonne est soit un champ natif de la source, soit une colonne calculée par formule.
"columns": [
{ "type": "NATIVE", "fieldName": "receiptRef" },
{ "type": "NATIVE", "fieldName": "subscriberName", "label": "Client" },
{ "type": "NATIVE", "fieldName": "netPremium" },
{ "type": "NATIVE", "fieldName": "totalTaxes" },
{ "type": "CALCULATED", "name": "tauxTaxes", "label": "Taxes / prime nette (%)",
"definition": "(totalTaxes / netPremium) * 100" }
]| Champ | Colonne native | Colonne calculée |
|---|---|---|
type | NATIVE (défaut) | CALCULATED |
fieldName | clé du champ, obligatoire | — |
name | — | clé de sortie, obligatoire, unique |
definition | — | formule, obligatoire |
label | libellé affiché (défaut : libellé du champ) | libellé affiché (défaut : name) |
Règles :
- Si
columnsest vide, les champsdefaultSelectedde la source sont utilisés. - Les champs
requiredde la source (ex.policyNumber,receiptRef) sont toujours ajoutés en tête, même si vous ne les demandez pas. - L'ordre des colonnes dans le fichier est l'ordre de
columns.
Formules des colonnes calculées
La formule est une expression arithmétique évaluée ligne par ligne, dont les variables sont les clés des colonnes présentes dans la ligne (natives ou calculées placées avant). Le résultat est un nombre arrondi à 2 décimales.
| Élément | Exemple |
|---|---|
| Opérateurs | + - * / ^ % |
| Parenthèses | (netPremium + accessories) * 0.1 |
| Fonctions usuelles | abs(x), sqrt(x), log(x), exp(x), floor(x), ceil(x), min/max non disponibles : utilisez une colonne native déjà agrégée |
| Constantes | 0.6, 100 |
Points d'attention :
- Une variable non numérique ou absente vaut 0. Une division par zéro ou une erreur de syntaxe donne une cellule vide (null), jamais une erreur HTTP.
- Référencez des colonnes que vous avez demandées dans
columns: une formule qui cite un champ non sélectionné le voit à 0. - En mode récapitulatif, la formule s'applique sur les sommes du groupe (ex. un taux moyen pondéré
se calcule naturellement :
(commission / netPremium) * 100). - Les colonnes calculées ne sont ni filtrables, ni triables, ni groupables.
Exemples :
{ "type": "CALCULATED", "name": "partPool", "label": "Part Pool recalculée (10 %)", "definition": "netPremium * 0.10" }
{ "type": "CALCULATED", "name": "ecartFG", "label": "Écart FG", "definition": "fraisGestion - netPremium * 0.10" }
{ "type": "CALCULATED", "name": "resteDu", "label": "Reste dû", "definition": "totalTtc - amountPaid" }
{ "type": "CALCULATED", "name": "tauxCommission", "label": "Commission (%)", "definition": "(commission / netPremium) * 100" }Les filtres — filters
"filters": [
{ "fieldName": "paymentStatus", "operator": "EQUALS", "values": ["CONFIRMED"] },
{ "fieldName": "netPremium", "operator": "BETWEEN", "values": ["10000", "100000"] },
{ "fieldName": "companyLabel", "operator": "CONTAINS", "values": ["wafa"] },
{ "fieldName": "attestationNumber", "operator": "IS_NOT_EMPTY" }
]Les filtres se cumulent (ET). Le champ doit être filterable.
| Opérateur | Valeurs | Sens |
|---|---|---|
EQUALS / NOT_EQUALS | 1 (ou plusieurs : devient IN / NOT_IN) | égalité |
IN / NOT_IN | ≥ 1 | appartenance |
GREATER_THAN, GREATER_OR_EQUAL, LESS_THAN, LESS_OR_EQUAL | 1 | comparaison |
BETWEEN | exactement 2 (basse, haute), bornes incluses | intervalle |
CONTAINS | 1 | contient, insensible à la casse |
IS_EMPTY / IS_NOT_EMPTY | 0 | null ou vide / renseigné |
Les valeurs sont toujours des chaînes, converties selon le type du champ :
| Type du champ | Format attendu | Exemple |
|---|---|---|
INTEGER | entier | "5" |
DECIMAL, MONEY, PERCENT | décimal, point | "14697.00" |
BOOLEAN | true / false | "true" |
DATE | yyyy-MM-dd | "2026-10-01" |
DATETIME | ISO 8601, ou yyyy-MM-dd (= début de journée) | "2026-10-01T00:00:00" |
ENUM | code de l'énumération | "ISSUED", "ACTIVE" |
STRING | texte | "COMAR" |
Une valeur inconvertible renvoie 400 « Valeur de filtre invalide pour … ».
La période — scope
"scope": {
"basedOn": "issuedAt",
"relativeRange": "PREVIOUS_MONTH",
"timezone": "Africa/Abidjan"
}ou, en bornes absolues :
"scope": {
"basedOn": "collectedDate",
"dateRange": { "from": "2026-10-01T00:00:00", "to": "2026-10-28T23:59:59" }
}| Champ | Description |
|---|---|
basedOn | Ancre : le champ de date qui est borné. Chaque source a ses ancres (ex. RECEIPTS : issuedAt défaut, collectedDate, effectiveDate). Absent = ancre par défaut de la source |
relativeRange | Période relative résolue au moment de l'exécution : TODAY, YESTERDAY, CURRENT_WEEK, PREVIOUS_WEEK, CURRENT_MONTH, PREVIOUS_MONTH, CURRENT_QUARTER, PREVIOUS_QUARTER, CURRENT_YEAR, PREVIOUS_YEAR, LAST_7_DAYS, LAST_30_DAYS, LAST_90_DAYS |
dateRange.from, dateRange.to | Bornes absolues, inclusives. Si dateRange est présent il prime sur relativeRange. Une borne manquante = ouverte |
timezone | Fuseau de résolution des périodes relatives (défaut Africa/Abidjan) |
Sans scope du tout, aucune borne de date n'est appliquée (toute l'historique). Les bornes réellement
appliquées sont renvoyées dans resolvedFrom / resolvedTo.
Changer d'ancre change le résultat. « Quittancement du mois d'octobre » basé sur issuedAt (émission)
et basé sur collectedDate (encaissement) ne donnent pas le même total. Dans une planification
mensuelle, préférez PREVIOUS_MONTH : le mois complet est clos au moment de l'envoi.
Le tri — sorting
"sorting": { "fieldName": "issuedAt", "direction": "DESC" }Un seul champ, sortable, direction = ASC ou DESC (défaut ASC). Absent = tri par défaut de
la source. Avec un regroupement, le tri s'applique à l'intérieur de chaque groupe (les groupes
sont triés par leur dimension).
Les regroupements — grouping et options.aggregated
"grouping": ["companyLabel"],
"options": { "aggregated": true }Deux modes selon options.aggregated :
| Mode | aggregated | Résultat |
|---|---|---|
| Récapitulatif | true (défaut dès qu'un grouping est donné) | Une ligne par groupe : les dimensions, une colonne rowCount (« Nombre »), puis uniquement les colonnes totalisables (Σ) et les colonnes calculées |
| Détail groupé | false | Toutes les lignes de détail, triées par groupe, avec ruptures et sous-totaux par groupe dans le fichier |
Plusieurs dimensions sont possibles (["brokerName", "companyLabel"]). Les champs doivent être
groupable. aggregated: true sans grouping renvoie 400.
Les options — options
| Option | Défaut | Effet |
|---|---|---|
hideSensitiveData | true | Masque les champs sensibles (téléphone, e-mail, adresse, pièce d'identité) par •••••• |
includeTotals | true | Ligne de totaux (et sous-totaux) dans le fichier |
includeLogo | true | Logo en en-tête du PDF |
aggregated | null | Force le mode récapitulatif (true) ou détail groupé (false) ; sinon déduit de grouping |
Le format, le titre, le nom de fichier (génération)
Propres à POST /provider/reports/generate :
| Champ | Description |
|---|---|
format | PDF, CSV, XLSX. Avec definitionCode, doit appartenir à formats de l'état |
title | Titre imprimé en en-tête (défaut : titre de l'état ou du modèle) |
fileName | Nom de fichier sans extension (défaut généré à partir du titre et de la période) |
scopeMode | GLOBAL (défaut) ou PER_COMPANY (comptes Pool : un fichier par compagnie) |
companyId | Comptes Pool : restreindre à une compagnie |
recipientsTo, recipientsCc | Adresses e-mail qui recevront le fichier en pièce jointe |
Exemples complets
A. Quittances encaissées du mois, triées par montant
{
"sourceCode": "RECEIPTS",
"columns": [
{ "fieldName": "receiptRef" }, { "fieldName": "companyLabel" }, { "fieldName": "subscriberName" },
{ "fieldName": "netPremium" }, { "fieldName": "totalTtc" }, { "fieldName": "amountPaid" },
{ "fieldName": "collectedDate" }, { "fieldName": "paymentReference" }
],
"filters": [ { "fieldName": "paymentStatus", "operator": "EQUALS", "values": ["CONFIRMED"] } ],
"scope": { "basedOn": "collectedDate", "relativeRange": "CURRENT_MONTH" },
"sorting": { "fieldName": "totalTtc", "direction": "DESC" },
"format": "XLSX",
"title": "Quittances encaissées — mois en cours"
}B. Contrôle des frais de gestion Pool par compagnie
Part de prime nette attendue (10 %) comparée aux frais de gestion réellement répartis.
{
"sourceCode": "RECEIPTS",
"columns": [
{ "fieldName": "netPremium" },
{ "fieldName": "fraisGestion" },
{ "type": "CALCULATED", "name": "fgAttendu", "label": "FG attendus (10 %)", "definition": "netPremium * 0.10" },
{ "type": "CALCULATED", "name": "ecart", "label": "Écart", "definition": "fraisGestion - netPremium * 0.10" }
],
"grouping": ["companyLabel"],
"options": { "aggregated": true },
"scope": { "basedOn": "issuedAt", "dateRange": { "from": "2026-10-01T00:00:00", "to": "2026-10-28T23:59:59" } },
"format": "XLSX"
}C. Impayés de plus de 30 jours, par intermédiaire, avec le détail
{
"definitionCode": "IMPAYES",
"filters": [ { "fieldName": "daysOverdue", "operator": "GREATER_THAN", "values": ["30"] } ],
"grouping": ["brokerName"],
"options": { "aggregated": false, "includeTotals": true },
"scope": { "basedOn": "dueDate", "relativeRange": "LAST_90_DAYS" },
"format": "PDF"
}D. Récapitulatif de production du mois précédent, pour une seule compagnie (compte Pool)
{
"definitionCode": "RECAP_PROD_COMPAGNIE",
"scope": { "relativeRange": "PREVIOUS_MONTH" },
"companyId": "5647d861-5a4b-493e-8043-17fb60958720",
"format": "PDF"
}E. Bordereau de cession, un fichier par compagnie (compte Pool)
{
"definitionCode": "BORDEREAU_CESSION",
"scope": { "relativeRange": "PREVIOUS_MONTH" },
"scopeMode": "PER_COMPANY",
"format": "PDF",
"recipientsTo": ["reversements@pooltpv.ci"]
}Check-list avant d'envoyer
-
sourceCodeoudefinitionCodeprésent - chaque
fieldNameexiste dans la source (…/sources/{code}/fields) - les filtres portent sur des champs
filterable, avec le bon nombre de valeurs et le bon format - les regroupements portent sur des champs
groupable - le tri porte sur un champ
sortable - le
scopea une ancre connue et une période (sinon toute l'historique) - le
formatest autorisé par l'état et, en PDF, le volume reste sous 25 000 lignes - vous avez prévisualisé avec
size: 1pour vérifiertotaletestimate