← Retour au menu

đŸ—ïž Architecture des endpoints

Le dashboard lettrage appelle deux endpoints distincts selon la situation. Chaque endpoint a ses propres rĂšgles de filtrage — ce n'est pas le mĂȘme code que la page traitement.

1
GET /lettrage/kpis — Chargement initial
Affiché dÚs l'ouverture de la page. Aucun filtre. Compte toutes les lignes de la base sans restriction. Méthode : getKpis()
2
GET /lettrage/dashboard — AprĂšs sĂ©lection de filtres
Déclenché quand l'utilisateur choisit entité / sens / année dans le dashboard. Méthode : getDashboardStats()
3
GET /lettrage/traitement — Page traitement
Méthode getLignes(), avec pagination et filtres complets. Applique en plus un filtre par défaut statutLettrage NOT IN ['exclue'] absent du dashboard.
⚠ RĂ©sumĂ© du problĂšme : les KPIs du dashboard sont calculĂ©s par un code diffĂ©rent de la page traitement, avec des rĂšgles de filtrage diffĂ©rentes. MĂȘme avec des filtres identiques (entitĂ©, sens, annĂ©e), les totaux ne seront pas les mĂȘmes.

🔧 Filtres construits par endpoint

Page traitement — getLignes()

// Filtre par défaut TOUJOURS appliqué cÎté page traitement :
where.statutLettrage = { notIn: ['exclue'] }

// Filtres optionnels (si passés) :
if (entite) → rĂ©solution via LettrageBank → banque: { in: [...] }
if (sens)   → sens: params.sens
if (anneeFiltre) → dateOperation: { gte: 2024-01-01, lt: 2025-01-01 }
if (dateDebut/dateFin) → dateOperation: { gte, lte }
// + filtre 'traite', 'nonLettrable', etc.

getDashboardStats() — Dashboard avec filtres

// Pas de filtre statutLettrage par défaut !
// baseWhere est construit ainsi :
const baseWhere: any = {};
if (banqueFilter) baseWhere.banque = banqueFilter;  // si entite != LES_DEUX
if (params.sens)  baseWhere.sens = params.sens;
if (params.annee) baseWhere.dateOperation = { gte, lt };

// baseWhere inclut donc : exclue, archivee, non_lettre, PARTIAL, traite

const nonLettrWhere = { ...baseWhere, statutLettrage: 'non_lettre', traite: false };
const partielWhere  = { ...baseWhere, statutLettrage: 'PARTIAL',    traite: false };
const traiteWhere   = { ...baseWhere, statutLettrage: 'traite' };

getKpis() — Dashboard chargement initial (global)

// Aucun filtre d'entité / sens / année
// Toute la base, sans restriction de statutLettrage sur les requĂȘtes globales

count({ where: { statutLettrage: 'non_lettre' } })  // PAS de traite:false
count({ where: { statutLettrage: 'PARTIAL' } })
count({ where: { nonLettrable: true, statutLettrage: { in: ['non_lettre', 'PARTIAL'] } } })
count({ where: { reouvert: true } })
count({ where: { premiereVueAt: { gte: dernierImport.finishedAt } } })  // nouvelles
count({ where: { derniereVueAt: { gte: dernierFinishedAt }, premiereVueAt: { lt: dernierFinishedAt } } }) // MAJ

⚡ getKpis() — Les KPIs de la barre du haut

â„č Endpoint : GET /lettrage/kpis. AppelĂ© au chargement initial du dashboard. Ne prend aucun paramĂštre, pas de filtre entitĂ©/sens/annĂ©e.
KPI affiché Champ retourné Condition Prisma Scope
Non lettrées nonLettrées statutLettrage = 'non_lettre' Global (tout statut traite)
Partielles partielles statutLettrage = 'PARTIAL' Global
Non lettrables nonLettrables nonLettrable = true AND statutLettrage IN ('non_lettre','PARTIAL') Global
Réouvertes reouvertes reouvert = true Global
Nouvelles nouvelles premiereVueAt >= dernierImport.finishedAt Global
Mises Ă  jour misesAJour derniereVueAt >= dernierImport.finishedAt AND premiereVueAt < dernierImport.finishedAt Global
🚹 Point critique : nonLettrĂ©es dans getKpis ne filtre PAS sur traite: false. Il compte donc toutes les lignes avec statutLettrage = 'non_lettre', y compris celles qui ont Ă©tĂ© marquĂ©es traitĂ©es (ce qui ne devrait plus arriver depuis le bugfix T8, mais des donnĂ©es historiques peuvent exister).

📊 getDashboardStats() — Le dashboard filtrĂ©

â„č Endpoint : GET /lettrage/dashboard. AppelĂ© quand les filtres du dashboard changent (entitĂ©, sens, annĂ©e). ParamĂštres : entite, sens, annee.

Construction du baseWhere

1
RĂ©solution entitĂ© → banques
Si entite = 'SA' ou 'PRO' (pas LES_DEUX) : requĂȘte sur LettrageBank pour trouver les noms de banques de cette entitĂ©. Le filtre banque: { in: [...] } est appliquĂ©.
2
Filtre sens
Si sens fourni : where.sens = 'DEBIT' ou 'CREDIT'.
3
Filtre année
Si annee fourni : dateOperation: { gte: 2024-01-01, lt: 2025-01-01 }.
✗
Pas de filtre statutLettrage par défaut
Contrairement Ă  la page traitement, baseWhere n'exclut pas les lignes exclue ni les archivee. Tous les statuts sont inclus dans les comptages.

KPIs calculés

KPI Where clause Note
total baseWhere (sans statut) Inclut exclue + archivee
montantTotal / restantTotal baseWhere Inclut exclue + archivee
nonLettrées baseWhere + statutLettrage='non_lettre' + traite=false Correct (traite:false présent)
partielles baseWhere + statutLettrage='PARTIAL' + traite=false Correct
lettrees baseWhere + statutLettrage='traite' Correct
sansContrat baseWhere + (contrat=null OR contrat='') Inclut exclue dans le décompte
sansFacture baseWhere + (facture=null OR facture='') Inclut exclue dans le décompte
restantNegatif baseWhere + restant < 0 Inclut exclue
traitéesAujourdhui traiteWhere + traiteAt >= aujourd'hui 00:00 Correct
nouvelles baseWhere + premiereVueAt >= dernierImport.finishedAt Inclut les lignes exclues nouvelles
doublons dernierImport.nbDoublons Stocké sur l'import, pas filtré

Par banque (agrégation JS)

// Chargement de TOUTES les lignes du baseWhere en mémoire :
parBanque = findMany({ where: baseWhere, select: { banque, statutLettrage, traite, montant, restant } })

// Agrégation en JS (pas en SQL) :
for (const l of parBanque) {
  entry.total++
  if (l.traite)                          entry.lettrees++    // booléen traite
  else if (l.statutLettrage === 'non_lettre') entry.nonLettrées++
  else if (l.statutLettrage === 'PARTIAL')   entry.partielles++
  // exclue / archivee : comptés dans total mais pas dans les 3 catégories
}
⚠ L'agrĂ©gation par banque utilise le boolĂ©en traite (champ SQL) et non statutLettrage='traite'. Ces deux colonnes devraient ĂȘtre synchronisĂ©es depuis les bugfixes, mais les donnĂ©es historiques peuvent avoir des incohĂ©rences.

📈 getDashboard() — Dashboard complet (non utilisĂ© actuellement)

â„č La mĂ©thode getDashboard() existe dans le service mais n'est pas appelĂ©e directement par le frontend — c'est getDashboardStats() qui est utilisĂ©e. Elle contient une logique similaire mais distincte avec un scBaseWhere supplĂ©mentaire.

Différence clé : baseWhere vs scBaseWhere

  • baseWhere : pas de filtre statut (inclut tout)
  • scBaseWhere : exclut archivee ET exclue — utilisĂ© pour sansContrat
  • Mais sansFacture utilise baseWhere (pas scBaseWhere) → incohĂ©rence interne

🚹 Divergence 1 — Lignes exclues comptĂ©es dans le dashboard

CRITIQUE

La page traitement exclut systĂ©matiquement les lignes avec statutLettrage = 'exclue'. Le dashboard (getDashboardStats) n'a aucun filtre par dĂ©faut sur le statut — il compte tout, y compris les exclues.

📄 getLignes() vs getDashboardStats() — filtre statut
−
// Page traitement : exclut 'exclue' par défaut
−
where.statutLettrage = { notIn: ['exclue'] }
+
// Dashboard : baseWhere vide, AUCUN filtre statut
+
const baseWhere: any = {} // inclut exclue, archivee, tout

Impact concret

KPI affecté Impact
total Sur-compté : inclut les lignes exclues et archivées
montantTotal Montant gonflé par les lignes exclues
sansContrat, sansFacture Inclut les lignes exclues sans contrat/facture
nonLettrées, partielles Non affectés (filtrent sur statutLettrage spécifique)

⚠ Divergence 2 — Non lettrĂ©es : traite:false manquant dans getKpis

IMPORTANT

getDashboardStats et la page traitement sont cohérents sur les non lettrées (tous deux utilisent traite: false implicitement via le statut). Mais getKpis() (barre du haut, affichée au chargement) ne filtre PAS sur traite: false.

Non lettrĂ©es — getKpis vs getDashboardStats
−
// getKpis : compte tout statutLettrage='non_lettre'
−
count({ where: { statutLettrage: 'non_lettre' } })
+
// getDashboardStats : exclut les traitées
+
count({ where: { statutLettrage: 'non_lettre', traite: false } })
⚠ En pratique, depuis le bugfix T8, une ligne avec statutLettrage='non_lettre' devrait avoir traite=false. Mais des donnĂ©es historiques incohĂ©rentes peuvent fausser getKpis. Les deux KPIs ont une sĂ©mantique diffĂ©rente, et leur diffĂ©rence peut ĂȘtre source de confusion pour l'utilisateur.

🚹 Divergence 3 — Total lignes diffĂ©rent entre dashboard et traitement

CRITIQUE

Le "total" vu dans le dashboard et le total de lignes dans la page traitement ne comptent pas la mĂȘme chose, mĂȘme avec les mĂȘmes filtres entitĂ©/sens/annĂ©e.

Source Ce qui est compté Statuts inclus
Dashboard — total Toutes les lignes du baseWhere non_lettre + PARTIAL + traite + exclue + archivee
Page traitement — totalCount Lignes avec statut non exclu non_lettre + PARTIAL + traite (pas exclue)
Frontend dashboard — totalCount CalculĂ© en JS : nonLettrĂ©es + partielles Seulement les actives non traitĂ©es
🚹 Le frontend du dashboard calcule son propre "total" en additionnant nonLettrĂ©es + partielles — c'est donc encore diffĂ©rent du champ total retournĂ© par l'API, et diffĂ©rent du total de la page traitement. Trois "totaux" diffĂ©rents pour la mĂȘme donnĂ©e.

⚠ Divergence 4 — Le frontend recalcule son propre total

IMPORTANT

Dans le composant frontend du dashboard, le totalCount affichĂ© n'est pas le champ total de l'API — c'est une addition en JavaScript.

// Frontend — calcul JS du total affichĂ© dans le dashboard :
const totalCount = (stats?.nonLettrées ?? 0) + (stats?.partielles ?? 0)

// Ce totalCount ne comprend PAS :
// - les lignes traitées (lettrees)
// - les lignes exclues
// - les lignes archivées

Conséquence

  • Le "total actif" du dashboard = nonLettrĂ©es + partielles (lignes Ă  traiter)
  • La page traitement affiche le nombre de lignes dans la liste paginĂ©e (excluant exclue)
  • Ces deux valeurs sont fondamentalement diffĂ©rentes par design
  • Mais l'utilisateur peut les comparer et s'attendre Ă  ce qu'elles soient Ă©gales

⚠ Divergence 5 — Scope des filtres entitĂ©/sens/annĂ©e

SECONDAIRE

La page traitement propose des filtres supplĂ©mentaires (dateDebut/dateFin, traite, nonLettrable, contrat, facture
) qui n'ont aucun Ă©quivalent dans le dashboard. Donc mĂȘme avec entitĂ©+sens+annĂ©e identiques, la page traitement peut afficher moins de lignes si d'autres filtres sont actifs.

Filtre Page traitement Dashboard
EntitĂ© (SA/PRO) ✓ via LettrageBank ✓ via LettrageBank
Sens (DEBIT/CREDIT) ✓ ✓
AnnĂ©e ✓ (anneeFiltre) ✓ (annee)
Date dĂ©but/fin ✓ ✗ absent
Traitement (traite=true/false) ✓ ✗ absent
Non lettrable ✓ ✗ absent
Contrat / Facture ✓ ✗ absent
Exclusion 'exclue' ✓ (par dĂ©faut) ✗ absent

📋 Tableau comparatif complet

Pour chaque KPI affichĂ©, d'oĂč vient la valeur et pourquoi elle peut diffĂ©rer de la page traitement.

KPI Endpoint Filtre statut Exclue incluse ? Filtres scope Page traitement ?
Non lettrĂ©es (barre haute) getKpis non_lettre Non Aucun ≠ (global)
Partielles (barre haute) getKpis PARTIAL Non Aucun ≠ (global)
Non lettrĂ©es (dashboard) getDashboardStats non_lettre + traite:false Non entite/sens/annee ≈ comparable
Partielles (dashboard) getDashboardStats PARTIAL + traite:false Non entite/sens/annee ≈ comparable
Total (dashboard) getDashboardStats Aucun OUI entite/sens/annee ≠ (inclut exclue)
Lettrees getDashboardStats traite Non entite/sens/annee ≈ comparable
Sans contrat getDashboardStats Aucun OUI entite/sens/annee ≠ lĂ©ger
Sans facture getDashboardStats Aucun OUI entite/sens/annee ≠ lĂ©ger
Nouvelles getDashboardStats Aucun (premiereVueAt) OUI entite/sens/annee ≠ lĂ©ger
Doublons getDashboardStats nbDoublons sur import Non filtré Global (pas de scope) Sans équivalent

💡 Recommandations

Fixes prioritaires

1
Exclure 'exclue' du baseWhere de getDashboardStats
Ajouter statutLettrage: { notIn: ['exclue', 'archivee'] } dans baseWhere de getDashboardStats(). Cela alignera le total et les KPIs secondaires avec la page traitement.
2
Ajouter traite:false dans getKpis pour les non lettrées
Dans getKpis(), changer { statutLettrage: 'non_lettre' } en { statutLettrage: 'non_lettre', traite: false } pour aligner la sémantique avec getDashboardStats.
3
Documenter la différence de "total" pour l'utilisateur
Le dashboard affiche les lignes "à traiter" (non lettrées + partielles) et non le total brut. Clarifier les libellés dans l'interface : "Lignes à traiter" vs "Total lignes" vs "Toutes lignes".
4
Utiliser scBaseWhere pour sansFacture dans getDashboard
Dans getDashboard (si utilisĂ©), remplacer baseWhere par scBaseWhere pour le calcul de sansFacture — aligner avec sansContrat qui utilise dĂ©jĂ  scBaseWhere.

Ce qui est normal (par design)

â„č
  • getKpis est global par design — c'est une vue rapide de l'Ă©tat de la base entiĂšre
  • Le filtre "traite" de la page traitement n'a pas d'Ă©quivalent dans le dashboard (normal)
  • dateDebut/dateFin n'existe pas dans getDashboardStats — seule l'annĂ©e est disponible
  • Doublons est calculĂ© par import, pas par scope de filtre