Chaque transaction du relevé devient une ligne « à trier » : type autre,
répartition vide (liseré rouge existant via field_ecart), tag field_flag
« IMP AAMMJJ » (liseré ambre + filtre existants), client rapproché par
mots (ClientMatcher : séquence contiguë, sinon mot significatif unique --
jamais en sous-chaîne, jamais auto-créé).
- src/Import/ : CsvReleveParser (ISO-8859-1 confirmé, en-tête strict,
fgetcsv avec escape '' explicite -- dépréciation PHP 8.4, rejets
propres avec n° de ligne), ReleveImportBatch (Batch API par lots de
25, dédoublonnage COMPTÉ par empreinte field_import_fitid -- max(0,
k−m) importe les vrais doublons légitimes et dédoublonne à travers
des fichiers qui se chevauchent --, totaux de contrôle au centime
sur la page de résultat, résumé en tempstore privé).
- ReleveUploadForm : upload private://releves (fichier conservé +
usage, hors de portée du cron), parse en validateForm(), batch,
redirection vers la page de résultat.
- field_montant_releve : référence bancaire immuable, écrite une fois
à l'import et jamais par presave ; affichée en TEXTE sous Montant
TTC (widget remplacé par un #type item -- un item ne soumet rien et
extractFormValues() saute le champ sans valeur soumise, la valeur
survit donc à chaque save) ; masquée sur les lignes sans montant.
- SkipValidationContext : contournement du contrôle de répartition
requête-scopé (ferme le trou de concurrence de l'ancien state
global, AUDIT-2026-09-09 §2.2) -- presave honore le service (state
gardé pour compat), updateType/updateField basculent dessus.
- Permissions (AUDIT §2.2 priorité 1) : access figli ledger sur toutes
les routes du module + autocomplete (RouteSubscriber), import
réservé Éditeur/Admin, access content retiré du rôle Authenticated
(config/sync re-exportée pour les 4 rôles).
- Gin : hook_gin_ignore_sticky_form_actions() -- sans ça, le bouton
Importer partait dans la barre sticky du chrome masqué.
- install : 8011 champs, 8012 index sur les empreintes, 8013/8014
montant_releve sur le formulaire sous le TTC (poids renumérotés).
- /lignes : boutons + Ajouter / Importer / Historique dans le footer
sticky (compacts), footer colspan dès la première colonne, badges de
signalement qui reviennent à la ligne au lieu de déborder.
- Content type "Ligne comptable" with Paragraphs "Répartition" (Compte + Montant)
- Taxonomies: Compte (9 comptes) and Client (unified client list)
- hook_node_presave + form validate: sum(répartition) must equal montant HT
- /dashboard route (progressive decoupling): Vue 3 app fetching JSON:API,
computing solde par compte / par client client-side
- "Ajouter une ligne" opens the real Drupal node form in an AJAX modal
- Gin as default + admin theme
- 9 opening-balance lines seeded from suivi_compta_SASFigli2026_v2.ods
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>