Skip to content

Refonte Écarts — Écran, justification et ajustements

Spécification technique pour Heriade Rapport. Formalise la refonte visuelle de l'écran Écarts, du popover de justification, et de la modale Ajustement de gestion, dans la continuité du restyle déjà validé pour les Vues.

0. Point distinct à traiter séparément — bug d'unité

Avant tout travail de design : l'écart affiché sur "706120 · Hébergement temporaire" (+92819,5 %, impact réel gestion affiché à 110 000 000 €) trahit une confusion d'unité, probablement un ajustement saisi en euros bruts plutôt qu'en k€ comme le reste de l'application. Ce n'est pas un défaut de mise en forme — aucun style ne rend un tel écart correct. À faire corriger par Claude Code indépendamment de cette spec, avant ou en parallèle du travail visuel.

1. Écran Écarts — table principale

1.1 Hiérarchie et typographie

Reprendre exactement les principes déjà validés pour les Vues (docs/spec-composants-dataviz.md, discussion sur la refonte des Vues) :

  • Chiffres droits (suppression du style italique actuel), alignés à droite, font-variant-numeric: tabular-nums
  • Poste total en gras, aligné à la marge, avec une bordure de séparation renforcée au-dessus
  • Comptes détaillés indentés, en --text-secondary

1.2 Actions de ligne

Les icônes d'action (Justifier, Créer un ajustement) sont systématiquement regroupées en fin de ligne, dans une colonne dédiée de largeur fixe — jamais positionnées différemment selon le type de ligne (poste total vs compte détaillé) comme c'est le cas actuellement. Chaque icône porte un aria-label explicite ("Justifier", "Créer un ajustement") plutôt qu'une simple info-bulle au survol.

1.3 Carte de synthèse (écart total)

Remplacer le badge de texte brut ("écart 109937 k€") par une mini-carte métrique (libellé au-dessus, valeur en dessous), réutilisant le même pattern que les cartes KPI/Pastille déjà spécifiées — cohérence visuelle avec le reste de l'application plutôt qu'un badge isolé.

1.4 Seuil d'alerte visuelle sur écart aberrant

ts
interface SeuilAlerteEcart {
  seuilPourcentage: number   // ex. 500 (%) — au-delà, l'écart est signalé comme suspect
}

Une ligne dont l'écart en pourcentage dépasse ce seuil reçoit :

  • Un fond légèrement teinté (--bg-danger)
  • Une icône d'alerte (ti-alert-triangle) à côté du libellé de compte, avec aria-label du type "Écart hors norme, à vérifier"

Point de vigilance explicite : ce traitement doit rester rare et réservé aux écarts réellement suspects (seuil élevé, ex. 500 % ou plus), pas à tout écart important en valeur absolue. Un écart de +397 % sur un petit budget peut être parfaitement plausible et ne doit pas recevoir le même traitement visuel qu'une anomalie de donnée — appliquer l'alerte trop largement lui ferait perdre son sens.

2. Popover de justification

Ajustements mineurs par rapport à l'existant :

  • Espacer clairement le libellé du compte, l'écart en valeur, et l'écart en pourcentage (actuellement collés autour d'un point médian)
  • Utiliser la même puce de couleur sémantique (vert/rouge) que le reste de l'application pour l'écart, plutôt qu'un texte simplement coloré sans repère visuel supplémentaire

3. Modale Ajustement de gestion

3.1 Répartition en mini-cartes

Remplacer la ligne dense "Compta X · Ajust. Y · Réel Z" par 3 mini-cartes côte à côte (Compta / Ajustement / Réel gestion), chacune avec son libellé au-dessus et sa valeur en dessous — même pattern que les cartes métriques utilisées ailleurs dans l'application.

3.2 Badge de type d'ajustement — couleur neutre, pas couleur de domaine

Le badge affichant le type d'ajustement (Reclassement, Provision, Rattachement...) utilise une palette neutre dédiée à la typologie d'ajustement, distincte de la couleur par domaine définie dans docs/spec-parametres-mise-en-forme.md. Les deux systèmes de couleur encodent des informations différentes (domaine de la mesure vs nature de l'ajustement) et ne doivent jamais se chevaucher visuellement, au risque de laisser croire à l'utilisateur qu'ils portent la même signification.

3.3 Séparation formulaire / écritures existantes

Le formulaire de saisie d'une nouvelle écriture (montant, type, commentaire) doit être visuellement distinct de la liste des écritures déjà enregistrées — dans la version actuelle, la proximité entre le champ de saisie et l'écriture existante prête à confusion sur ce qui est déjà validé et ce qui est en cours de saisie.

4. Ventilation d'ajustements — deux axes orthogonaux, à ne pas confondre

Correction de cadrage : la version précédente de cette section fusionnait à tort deux notions distinctes sous le mot « Couche ». Ce sont deux axes indépendants, qui ne se dérivent jamais l'un de l'autre :

AxeQuestion poséeChamp
Niveau de validationQui a posé/validé cet ajustement ? (CSDG → Directeur CDG → DAF)level (N1/N2/N3)
Filiation de ventilationCe montant est-il l'éclatement d'un autre ajustement ?ajustementParentId

Un ajustement peut être de niveau N2 sans être une ventilation. Une ventilation enfant n'est pas nécessairement d'un niveau de validation supérieur à son parent — souvent l'inverse (un montant global validé haut, éclaté en détail ensuite). level reste aujourd'hui un champ libre coché à la main, sans lien ni règle avec ajustementParentId : les deux champs doivent le rester, sans dérivation automatique de l'un à partir de l'autre.

4.1 Sens de la ventilation

Parent grossier → enfants fins. Un contrôleur repère d'abord un écart global (ex. « l'intérim dépasse le budget de +300 k€ sur le groupe »), pose un ajustement parent à ce niveau agrégé, puis l'éclate en détail. Le sens inverse (agrégation bas → haut) ne nécessite pas ce mécanisme : c'est une simple consolidation, déjà couverte par l'arbre DimNode existant.

4.2 Règle d'intégrité — tolérance affichée, jamais de blocage

La somme des enfants d'une ventilation devrait boucler sur le montant du parent, mais ce n'est pas une contrainte bloquante : la ventilation peut être progressive dans le temps. Un badge sur la ligne parent reflète l'état :

  • ✓ ventilé 300/300 k€ si la somme des enfants égale le montant parent
  • ⚠ 280/300, reste 20 k€ à ventiler si un solde reste à répartir

Cohérent avec le principe déjà appliqué ailleurs dans le produit (éléments non classés, écarts aberrants) : signaler l'incomplétude plutôt que la masquer ou bloquer l'utilisateur.

4.3 Axe d'éclatement — générique, établissement en premier

L'axe sur lequel on ventile ne doit pas être limité en dur à l'établissement dans le modèle (cohérent avec le principe de rôles génériques du méta-modèle — pas de cas spécial EHPAD) : un ajustement doit pouvoir se ventiler par établissement, par poste analytique, ou tout autre axe existant. La première implémentation peut se limiter à l'établissement (cas le plus courant), le modèle restant ouvert à extension sans redesign.

4.4 Indépendance totale avec la validation

level et ajustementParentId ne se dérivent jamais l'un de l'autre. Une ventilation n'impose aucun niveau de validation particulier à ses enfants — chaque ajustement (parent ou enfant) porte son propre level, indépendamment de sa position dans une éventuelle filiation.

4.5 Cycle de vie

  • Suppression d'un ajustement parent : cascade sur ses enfants (ils perdent leur sens sans le parent qu'ils ventilaient), mais jamais silencieusement — une confirmation explicite affiche le nombre d'enfants impactés avant l'action, sur le même principe que le résumé de confirmation déjà posé pour la restauration de sauvegarde (docs/spec-sauvegardes.md, section 4).
  • Modification du montant d'un ajustement parent déjà ventilé : n'invalide rien automatiquement, mais casse le bouclage — le badge repasse de à avec le nouveau solde recalculé. Signal plutôt que blocage, cohérent avec 4.2.

4.6 Modèle de données

ts
interface AjustementGestion {
  id: string
  perimetre: { axis: string, code: string }
  montant: number
  level: 'N1' | 'N2' | 'N3'                 // niveau de validation — indépendant de la filiation
  ajustementParentId?: string                // filiation de ventilation — indépendant du niveau
  axeEclatement?: string                      // axe sur lequel la ventilation se répartit (ex. etablissement), pertinent seulement si ajustementParentId ventile ce parent
}

Interface : sur une ligne enfant, un renvoi visuel cliquable vers son parent (ouvre ou surligne la ligne parente). Sur une ligne parent ventilée, le badge de bouclage (4.2). Formulaire : un mode « Ventiler » explicite (choisir le parent à ventiler, répartir le montant), plutôt qu'un champ ajustementParentId renseigné sans accompagnement.

5. Phasage suggéré

  1. Étape 1 : correction du bug d'unité (section 0) — indépendant du reste, à traiter en priorité.
  2. Étape 2 : refonte de la table Écarts (section 1) — hiérarchie, actions groupées, carte de synthèse, seuil d'alerte.
  3. Étape 3 : refonte du popover de justification (section 2) et de la modale Ajustement (section 3).
  4. Étape 4 : ventilation d'ajustements (section 4) — modèle AjustementGestion avec level et ajustementParentId explicitement indépendants (4.6), badge de bouclage (4.2), mode « Ventiler » dans le formulaire, cascade de suppression avec confirmation (4.5). Limiter l'axe d'éclatement à l'établissement pour cette première implémentation (4.3).

6. Point de vigilance

Le seuil d'alerte (section 1.4) est un paramètre, pas une valeur codée en dur — un client avec des montants naturellement très volatils (ex. un établissement en démarrage d'activité) pourrait déclencher l'alerte en permanence sur des écarts pourtant normaux pour son contexte. Envisager de le rendre configurable par entreprise plutôt que fixe pour tous les clients, si ce cas se présente en usage réel.