feat(balance): CSV import of holdings in detailed snapshot (#245) #251
No reviewers
Labels
No labels
autopilot:pending-human
source:analyste
source:defenseur
source:human
source:medic
status:approved
status:blocked
status:in-progress
status:needs-clarification
status:needs-fix
status:ready
status:review
status:triage
type:bug
type:feature
type:infra
type:refactor
type:schema
type:security
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: maximus/Simpl-Resultat#251
Loading…
Reference in a new issue
No description provided.
Delete branch "issue-245-import-csv-titres"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Adds a CSV import path for detailed (by-security) balance accounts: an "Import CSV" button per detailed account opens a native picker, auto-detects the delimiter/encoding/columns (symbol, quantity, optional price + book_cost), and lets the user adjust the mapping before importing.
Purely frontend — file -> HoldingDraft[] -> IMPORT_HOLDINGS reducer -> existing atomic save (no SQL/Rust change). Duplicate symbols are merged both within the CSV and against the basket, so no UNIQUE(snapshot_line_id, security_id) violation. A CSV without a price column imports quantities with an empty price (fetch/type later). New pure helpers are unit-tested (csvAutoDetect + useSnapshotEditor); 654 vitest green, build clean, i18n FR/EN.
Resolves #245
Generated autonomously by /autopilot run of 2026-07-04
/pr-review — PR #251 · feat(balance): CSV import of holdings in detailed snapshot (#245)
Verdict : APPROVE ✅
Résumé
Feature purement frontend, bien découpée : la modale possède le cycle de vie fichier/parse, tandis que la construction des
HoldingDraftet le merge dans le panier vivent dansuseSnapshotEditor(purs, testés). Le chemin de sauvegarde atomique reste inchangé — aucun SQL/Rust touché. La déduplication par symbole normalisé, à la fois dans le CSV (holdingsFromCsvRows) et contre le panier existant (reducerIMPORT_HOLDINGS), protège correctement la contrainteUNIQUE(snapshot_line_id, security_id). i18n FR/EN complet (17 clés, jeux identiques), CHANGELOG mis à jour dans les deux langues, CI verte (frontend 2m25s + rust 22m45s), commit conventionnel, référence l'issue.Vérifié : boundary Tauri (
pick_import_filefiltreVec<(String, Vec<String>)>,detect_encoding/read_file_contentenfilePathcamelCase) conforme aux usages existants ;category_asset_typeexiste bien surBalanceAccountWithCategory; le nouveau prop requisonImportHoldingsdeSnapshotEditorest fourni par son unique appelant (SnapshotEditPage) ; couverture de tests solide (drafts, merge intra-CSV, reducer in-place/append, préservation du prix, bout-en-boutbuildDetailedLines+ gotcha no-price qui échoue à la validation de save). Aucunskip/only.Suggestions (non bloquantes)
autoDetectHoldingColumns— fallback quantité ignoreused(csvAutoDetect.ts, ~L1442). Sur un CSV à 2 colonnesSymbole,Cours(sans colonne quantité), le fallbackquantity = symbol === 0 && colCount > 1 ? 1 : 0forcequantity = 1, or la colonne 1 est déjàusedparunit_price→ quantité et prix pointent la même colonne. Résultat :holdingsFromCsvRowslit le cours comme quantité. Cas dégénéré (CSV de positions sans quantité) et rattrapable via la preview, mais le défaut auto est trompeur. Idéalement, ne pas réutiliser une colonne déjà prise, ou surfacer un état « aucune colonne quantité détectée ».Quantité illisible avalée en 0 (
useSnapshotEditor.ts, ~L819).const qty = isNaN(qtyParsed) ? 0 : qtyParsed: une cellule quantité type"N/A"/"-"importe une position à 0 au lieu de la signaler. La preview affiche la valeur brute, donc c'est rattrapable, mais un léger décalage preview↔import subsiste.Accumulation flottante au merge intra-CSV (
useSnapshotEditor.ts, ~L835).existing.qty += qty/existing.book = (existing.book ?? 0) + bookpeuvent produire des artefacts (0.1 + 0.2 → 0.30000000000000004) dans les stringsquantity/book_cost. Cosmétique — la valeur est re-arrondie au cent au save — mais un arrondi à la construction rendrait l'affichage propre.Preview : symbole normalisé, nombres bruts. Le symbole affiché passe par
normalizeSecuritySymbol(majuscules) alors que qty/cours/coût restent bruts non parsés. Léger manque de cohérence visuelle (choix défendable puisque la preview reflète la source).Pas de skip de préambule. Contrairement à
autoDetectConfig,analyzeHoldingsCsvne saute pas les lignes d'en-tête non tabulaires (relevés de courtier avec préambule « Compte : … »). La détection de colonnes/délimiteur peut alors dériver ; rattrapable via l'éditeur de mapping.Micro :
data[0].map(normalizeHeaderCell)recalculé deux fois dansautoDetectHoldingColumns(step 1 + exclusion des colonnes « valeur »).Aucun de ces points ne bloque le merge.
Merged into
mainlocally as part of the milestone stack (merge commit2a3cc07). Closing manually — the merge was done outside Forgejo's API, so it can't flag this PR as merged. The change is onmain.Pull request closed