fix: regle debit/credit et parsing des montants #325

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

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

Contexte

src/hooks/useImportWizard.ts:509 :

amount = isNaN(credit) ? -(isNaN(debit) ? 0 : debit) : credit;

Le credit gagne toujours. Beaucoup de banques ecrivent 0,00 dans la colonne inutilisee plutot que de la laisser vide : tous les debits deviennent alors 0 $, et la ligne passe la validation puisque isNaN(0) est faux. La convention documentee est que dans un format a deux colonnes, debit et credit sont tous deux positifs — la regle correcte est donc credit - debit sur des magnitudes.

Les fallbacks ?? 0 (:504-512) lisent la colonne 0 — souvent la date — quand le mapping est incomplet, au lieu d'echouer.

src/utils/amountParser.ts ne gere ni les parentheses comptables (50,00) ni le signe suffixe 50,00-.

Taches

  • amount = credit - debit sur magnitudes, en remplacement de la comparaison de nullite
  • Colonne inutilisee a 0,00 traitee comme absente
  • Supprimer les fallbacks ?? 0 au profit d'une erreur de ligne explicite « colonne de montant non mappee »
  • parseFrenchAmount : parentheses comptables (50,00) et signe suffixe 50,00-
  • Separateur decimal arbitre au niveau de la colonne et non de la cellule (1.234 est ambigu isolement ; si la colonne porte des virgules decimales ailleurs, le point est un separateur de milliers)
  • Tests unitaires sur chaque cas

Criteres d'acceptation

  • Un fichier dont la colonne inutilisee porte 0,00 produit les bons montants, aucun n'est nul
  • Un mapping incomplet produit une erreur de ligne explicite, jamais une lecture de la colonne 0

Depends on #324


Revision /review-spec — 2026-08-13

Corrections a appliquer, issues de la revue 3 experts :

  • Le defaut de parseFrenchAmount est plus grave que decrit (verifie en executant la fonction) : elle termine sur parseFloat, qui s'arrete au premier caractere invalide au lieu de rejeter.
    "50,00-" rend 5000"1 234,56 CR" rend 123456"100,00 CAD" rend 10000.
    Erreur de facteur 100, pas un signe perdu, et chacune passe isNaN : ces lignes seront comptees valides dans le recapitulatif signe de #329, ce qui neutralise le filet de securite principal du chantier.
  • Fix requis : validation ancree par regex apres normalisation, NaN sur tout caractere residuel. Ajouter ces trois chaines aux tests.
  • 11 sites d'appel, non listes dans le corps : 8 dans csvAutoDetect.ts (:224 detectHeader, :289, :323, :489, :517 detectSingleAmount, :543-544 isSparseComplementary, :712 holdings) et 3 dans useSnapshotEditor.ts:191-202 (import CSV de titres, #245).
  • Portee du durcissement : global (decision tranchee) — un "100,00 CAD" a 10000 est un bug partout, y compris cote titres. Passe de regression obligatoire sur les tests holdings existants.
  • Ajouter useSnapshotEditor.ts au perimetre et creer src/utils/amountParser.test.ts (n'existe pas aujourd'hui).
  • Extraire mapRow(raw, format): ParsedRow — fonction pure exportee tiree de parseFilesInternal, consommee par le wizard, par le score de #328 et par l'apercu de #329. Sans elle, #328 reimplemente la regle de parsing et le score peut afficher 100 % pendant que l'import ecrit de mauvais signes.

Fichiers concernes

  • src/utils/amountParser.ts — validation ancree
  • src/utils/amountParser.test.tscreer
  • src/utils/importFormat.tsmapRow(raw, format): ParsedRow extrait de parseFilesInternal
  • src/hooks/useImportWizard.ts — regle credit - debit, suppression des fallbacks ?? 0, consommation de mapRow
  • src/utils/csvAutoDetect.ts — 8 sites d'appel de parseFrenchAmount a verifier
  • src/hooks/useSnapshotEditor.ts — 3 sites d'appel (:191-202), flux holdings #245

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.

  • Le durcissement s'applique globalement, y compris au flux holdings. Une valeur qui devient NaN alimente le chemin d'erreur de ligne deja existant — verifier que le flux holdings degrade proprement plutot que d'ecrire une quantite nulle.

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 `src/hooks/useImportWizard.ts:509` : ```ts amount = isNaN(credit) ? -(isNaN(debit) ? 0 : debit) : credit; ``` Le credit gagne toujours. Beaucoup de banques ecrivent `0,00` dans la colonne inutilisee plutot que de la laisser vide : **tous les debits deviennent alors 0 $**, et la ligne passe la validation puisque `isNaN(0)` est faux. La convention documentee est que dans un format a deux colonnes, debit et credit sont tous deux positifs — la regle correcte est donc `credit - debit` sur des magnitudes. Les fallbacks `?? 0` (`:504-512`) lisent la colonne 0 — souvent la date — quand le mapping est incomplet, au lieu d'echouer. `src/utils/amountParser.ts` ne gere ni les parentheses comptables `(50,00)` ni le signe suffixe `50,00-`. ## Taches - [ ] `amount = credit - debit` sur magnitudes, en remplacement de la comparaison de nullite - [ ] Colonne inutilisee a `0,00` traitee comme absente - [ ] Supprimer les fallbacks `?? 0` au profit d'une erreur de ligne explicite « colonne de montant non mappee » - [ ] `parseFrenchAmount` : parentheses comptables `(50,00)` et signe suffixe `50,00-` - [ ] Separateur decimal arbitre au niveau de la colonne et non de la cellule (`1.234` est ambigu isolement ; si la colonne porte des virgules decimales ailleurs, le point est un separateur de milliers) - [ ] Tests unitaires sur chaque cas ## Criteres d'acceptation - [ ] Un fichier dont la colonne inutilisee porte `0,00` produit les bons montants, aucun n'est nul - [ ] Un mapping incomplet produit une erreur de ligne explicite, jamais une lecture de la colonne 0 Depends on #324 --- ## Revision /review-spec — 2026-08-13 Corrections a appliquer, issues de la revue 3 experts : - **Le defaut de `parseFrenchAmount` est plus grave que decrit** (verifie en executant la fonction) : elle termine sur `parseFloat`, qui s'arrete au premier caractere invalide au lieu de rejeter. `"50,00-"` rend **5000** — `"1 234,56 CR"` rend **123456** — `"100,00 CAD"` rend **10000**. Erreur de facteur 100, pas un signe perdu, et chacune passe `isNaN` : ces lignes seront comptees **valides** dans le recapitulatif signe de #329, ce qui neutralise le filet de securite principal du chantier. - **Fix requis** : validation ancree par regex apres normalisation, `NaN` sur tout caractere residuel. Ajouter ces trois chaines aux tests. - **11 sites d'appel, non listes dans le corps** : 8 dans `csvAutoDetect.ts` (`:224` `detectHeader`, `:289`, `:323`, `:489`, `:517` `detectSingleAmount`, `:543-544` `isSparseComplementary`, `:712` holdings) et 3 dans `useSnapshotEditor.ts:191-202` (import CSV de titres, #245). - **Portee du durcissement : global** (decision tranchee) — un `"100,00 CAD"` a 10000 est un bug partout, y compris cote titres. Passe de regression obligatoire sur les tests holdings existants. - **Ajouter `useSnapshotEditor.ts`** au perimetre et **creer `src/utils/amountParser.test.ts`** (n'existe pas aujourd'hui). - **Extraire `mapRow(raw, format): ParsedRow`** — fonction pure exportee tiree de `parseFilesInternal`, consommee par le wizard, par le score de #328 et par l'apercu de #329. Sans elle, #328 reimplemente la regle de parsing et le score peut afficher 100 % pendant que l'import ecrit de mauvais signes. --- ## Fichiers concernes - `src/utils/amountParser.ts` — validation ancree - `src/utils/amountParser.test.ts` — **creer** - `src/utils/importFormat.ts` — `mapRow(raw, format): ParsedRow` extrait de `parseFilesInternal` - `src/hooks/useImportWizard.ts` — regle `credit - debit`, suppression des fallbacks `?? 0`, consommation de `mapRow` - `src/utils/csvAutoDetect.ts` — 8 sites d'appel de `parseFrenchAmount` a verifier - `src/hooks/useSnapshotEditor.ts` — 3 sites d'appel (`:191-202`), flux holdings #245 ## 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. - **Le durcissement s'applique globalement**, y compris au flux holdings. Une valeur qui devient `NaN` alimente le chemin d'erreur de ligne deja existant — verifier que le flux holdings degrade proprement plutot que d'ecrire une quantite nulle. ## 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:32 +00:00
maximus added the
status:ready
type:bug
source:human
labels 2026-08-12 20:14:32 +00:00
maximus added
status:in-progress
and removed
status:ready
labels 2026-08-13 17:23:44 +00:00
maximus added
status:needs-fix
and removed
status:in-progress
labels 2026-08-14 15:31:12 +00:00
maximus added
status:approved
and removed
status:needs-fix
labels 2026-08-14 16:03:37 +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#325
No description provided.