feat: signatures de banques et detection de derive de format #330

Closed
opened 2026-08-12 20:14:33 +00:00 by maximus · 0 comments
Owner

Refs: spec-decisions-import-csv-format.md + spec-plan-import-csv-format.md (racine du repo).

Contexte

Deux besoins lies, tous deux fondes sur la ligne d'en-tete.

Signatures de banques. Reconnaitre un format connu sans demander a l'utilisateur de choisir sa banque. Desjardins exporte Date, Description, Montant, Solde en point-virgule avec virgule decimale, en-tetes FR ou EN, et certains exports de cartes de credit n'ont pas de ligne d'en-tete. preprocessQuotedCSV (csvAutoDetect.ts:32-51) traite deja le cas Desjardins de la ligne entiere entre guillemets — le besoin est demontre.

Derive de format. Quand la banque change son export, le mapping memorise devient faux et l'import passe sans bruit. C'est le mode d'echec le plus couteux sur plusieurs annees, parce qu'il se decouvre longtemps apres.

Taches

  • src/utils/bankSignatures.ts : signatures declaratives (libelles normalises, delimiteur, particularites de preambule) pour Desjardins, RBC, BNC, Tangerine
  • Signatures evaluees avant le dictionnaire generique ; un fichier inconnu retombe sur le generique
  • Bandeau « Format Desjardins reconnu » quand une signature matche
  • header_signature memorisee sur la source a chaque import reussi
  • Au re-import : comparer la ligne d'en-tete a la signature memorisee ; si elle differe, relancer la detection et presenter l'ecart colonne par colonne (« Montant : 3 -> 4 ») dans un FormatDriftPanel, avec deux issues — adopter la config re-detectee ou conserver l'ancienne
  • Cles i18n FR et EN
  • Tests : une fixture par banque, plus un cas de derive

Points d'attention

  • Les signatures sont ecrites sans releves reels, a partir des formats documentes. L'echec d'une signature degrade vers le generique, il ne casse rien.
  • Un fichier sans ligne d'en-tete n'a pas de signature a memoriser : header_signature reste nulle et la derive est inoperante sur ces sources. Comportement documente, pas contourne — inventer une signature sur les donnees produirait un faux positif a chaque changement de contenu.

Criteres d'acceptation

  • Un fichier Desjardins est reconnu par signature et annonce comme tel
  • Un fichier inconnu retombe sur le dictionnaire generique sans erreur
  • Un en-tete modifie depuis le dernier import declenche le panneau d'ecart

Depends on #329


Revision /review-spec — 2026-08-13

Corrections a appliquer, issues de la revue 3 experts :

  • Enoncer le chemin de reparation sur dans le panneau de derive et dans l'apercu. findDuplicates (transactionService.ts:164) apparie sur date AND description AND amount : les corrections de #324 et #325 changent les montants qu'une source produit, donc un re-import « corrige » ne reconnaitra pas les lignes fautives deja ecrites et les ajoutera en double — une inversion de signe produisant en prime des paires miroir qui se compensent a ~0 au lieu de sauter aux yeux.
  • Le seul chemin sur est deleteImportWithTransactions sur l'import fautif avant de le rejouer. Cela doit apparaitre dans l'interface, pas seulement dans le tableau des risques du plan.

Fichiers concernes

  • src/utils/bankSignatures.tscreer : signatures declaratives
  • src/components/import/FormatDriftPanel.tsxcreer : panneau d'ecart
  • src/utils/csvAutoDetect.ts — evaluation des signatures avant le dictionnaire generique
  • src/hooks/useImportWizard.ts — memorisation et comparaison de header_signature
  • src/services/importSourceService.ts
  • src/i18n/locales/fr.json + en.json

Decisions prises en planification

  • Emplacement du code partage : le codec formatToRow/formatFromRow et mapRow vivent dans src/utils/importFormat.ts — meme dossier que amountParser, dateParser et csvAutoDetect, qui portent deja la logique pure du domaine. Les types restent dans src/shared/types/.

  • Aucune correction retroactive des transactions deja importees a l'envers : la voie de reparation est deleteImportWithTransactions puis re-import. Ne jamais muter des montants deja ecrits.

  • L'import reste entierement en edition Free — aucun gating a ajouter.

  • Les specs spec-decisions-import-csv-format.md et spec-plan-import-csv-format.md sont committees a la racine (force-add, precedent PR #295) : elles sont lisibles depuis un worktree.

  • header_signature stocke un tableau JSON des libelles normalises par normalizeHeaderCell, ex. ["date","description","montant","solde"]. Pas un hash : le panneau d'ecart doit pouvoir nommer les colonnes qui ont bouge (« Montant : 3 -> 4 »), ce qu'un hash interdit.

  • Une source dont le fichier n'a pas de ligne d'en-tete garde header_signature NULL : la derive est inoperante sur ces sources, comportement documente et assume (inventer une signature sur les donnees produirait un faux positif a chaque changement de contenu).

Spec source

spec-plan-import-csv-format.md + spec-decisions-import-csv-format.md (racine du repo, committees).

Refs: `spec-decisions-import-csv-format.md` + `spec-plan-import-csv-format.md` (racine du repo). ## Contexte Deux besoins lies, tous deux fondes sur la ligne d'en-tete. **Signatures de banques.** Reconnaitre un format connu sans demander a l'utilisateur de choisir sa banque. Desjardins exporte Date, Description, Montant, Solde en point-virgule avec virgule decimale, en-tetes FR ou EN, et certains exports de cartes de credit n'ont pas de ligne d'en-tete. `preprocessQuotedCSV` (`csvAutoDetect.ts:32-51`) traite deja le cas Desjardins de la ligne entiere entre guillemets — le besoin est demontre. **Derive de format.** Quand la banque change son export, le mapping memorise devient faux et l'import passe sans bruit. C'est le mode d'echec le plus couteux sur plusieurs annees, parce qu'il se decouvre longtemps apres. ## Taches - [ ] `src/utils/bankSignatures.ts` : signatures declaratives (libelles normalises, delimiteur, particularites de preambule) pour Desjardins, RBC, BNC, Tangerine - [ ] Signatures evaluees **avant** le dictionnaire generique ; un fichier inconnu retombe sur le generique - [ ] Bandeau « Format Desjardins reconnu » quand une signature matche - [ ] `header_signature` memorisee sur la source a chaque import reussi - [ ] Au re-import : comparer la ligne d'en-tete a la signature memorisee ; si elle differe, relancer la detection et presenter l'ecart colonne par colonne (« Montant : 3 -> 4 ») dans un `FormatDriftPanel`, avec deux issues — adopter la config re-detectee ou conserver l'ancienne - [ ] Cles i18n FR et EN - [ ] Tests : une fixture par banque, plus un cas de derive ## Points d'attention - Les signatures sont ecrites sans releves reels, a partir des formats documentes. L'echec d'une signature **degrade** vers le generique, il ne casse rien. - Un fichier **sans ligne d'en-tete** n'a pas de signature a memoriser : `header_signature` reste nulle et la derive est inoperante sur ces sources. Comportement documente, pas contourne — inventer une signature sur les donnees produirait un faux positif a chaque changement de contenu. ## Criteres d'acceptation - [ ] Un fichier Desjardins est reconnu par signature et annonce comme tel - [ ] Un fichier inconnu retombe sur le dictionnaire generique sans erreur - [ ] Un en-tete modifie depuis le dernier import declenche le panneau d'ecart Depends on #329 --- ## Revision /review-spec — 2026-08-13 Corrections a appliquer, issues de la revue 3 experts : - **Enoncer le chemin de reparation sur dans le panneau de derive et dans l'apercu.** `findDuplicates` (`transactionService.ts:164`) apparie sur `date AND description AND amount` : les corrections de #324 et #325 changent les montants qu'une source produit, donc un re-import « corrige » **ne reconnaitra pas** les lignes fautives deja ecrites et les ajoutera en double — une inversion de signe produisant en prime des paires miroir qui se compensent a ~0 au lieu de sauter aux yeux. - Le seul chemin sur est `deleteImportWithTransactions` sur l'import fautif **avant** de le rejouer. Cela doit apparaitre dans l'interface, pas seulement dans le tableau des risques du plan. --- ## Fichiers concernes - `src/utils/bankSignatures.ts` — **creer** : signatures declaratives - `src/components/import/FormatDriftPanel.tsx` — **creer** : panneau d'ecart - `src/utils/csvAutoDetect.ts` — evaluation des signatures avant le dictionnaire generique - `src/hooks/useImportWizard.ts` — memorisation et comparaison de `header_signature` - `src/services/importSourceService.ts` - `src/i18n/locales/fr.json` + `en.json` ## Decisions prises en planification - **Emplacement du code partage** : le codec `formatToRow`/`formatFromRow` et `mapRow` vivent dans `src/utils/importFormat.ts` — meme dossier que `amountParser`, `dateParser` et `csvAutoDetect`, qui portent deja la logique pure du domaine. Les types restent dans `src/shared/types/`. - **Aucune correction retroactive** des transactions deja importees a l'envers : la voie de reparation est `deleteImportWithTransactions` puis re-import. Ne jamais muter des montants deja ecrits. - **L'import reste entierement en edition Free** — aucun gating a ajouter. - Les specs `spec-decisions-import-csv-format.md` et `spec-plan-import-csv-format.md` sont **committees a la racine** (force-add, precedent PR #295) : elles sont lisibles depuis un worktree. - **`header_signature` stocke un tableau JSON des libelles normalises** par `normalizeHeaderCell`, ex. `["date","description","montant","solde"]`. Pas un hash : le panneau d'ecart doit pouvoir nommer les colonnes qui ont bouge (« Montant : 3 -> 4 »), ce qu'un hash interdit. - Une source dont le fichier n'a pas de ligne d'en-tete garde `header_signature` NULL : la derive est inoperante sur ces sources, comportement documente et assume (inventer une signature sur les donnees produirait un faux positif a chaque changement de contenu). ## Spec source `spec-plan-import-csv-format.md` + `spec-decisions-import-csv-format.md` (racine du repo, committees).
maximus added this to the planned-2026-08-12-import-csv-format milestone 2026-08-12 20:14:33 +00:00
maximus added the
status:ready
type:feature
source:human
labels 2026-08-12 20:14:33 +00:00
maximus added
status:in-progress
and removed
status:ready
labels 2026-08-13 18:34:48 +00:00
maximus added
status:needs-fix
and removed
status:in-progress
labels 2026-08-14 15:45:16 +00:00
maximus added
status:approved
and removed
status:needs-fix
labels 2026-08-14 16:03:38 +00:00
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: maximus/Simpl-Resultat#330
No description provided.