fix(balance): snapshots orphelins quand save echoue apres INSERT du snapshot row #176

Closed
opened 2026-05-01 01:28:35 +00:00 by maximus · 0 comments
Owner

Description

L'utilisateur rapporte: save snapshot bloque avec message "changer la date" alors qu'aucun snapshot n'est visible a cette date. Cause: snapshots orphelins (zero ligne) crees par des saves precedents qui ont throw apres l'INSERT du snapshot row mais avant l'INSERT des lignes.

src/hooks/useSnapshotEditor.ts:434 execute createSnapshot AVANT de valider les lignes. Si snapshot_priced_quantity_required / snapshot_priced_unit_price_required / snapshot_value_invalid throw, le snapshot row reste orphelin en BDD et bloque tout save futur a cette date via le check snapshot_date_taken.

Approche (decidee 2026-04-30)

1) Atomicite via BEGIN/COMMIT explicite + validate-first

Refactor save dans useSnapshotEditor.ts:426-508:

  1. Construire en memoire simpleLines + pricedLines avec tous les throw new BalanceServiceError(...) (lignes 443-497) AVANT toute mutation BDD
  2. Si tout valide: await db.execute("BEGIN")
  3. INSERT INTO balance_snapshots ... (via createSnapshot)
  4. INSERT INTO balance_snapshot_lines ... (via upsertSnapshotLines)
  5. await db.execute("COMMIT") sur succes
  6. await db.execute("ROLLBACK") dans le catch

Pattern deja utilise dans categorizationService.ts:226 et categoryMigrationService.ts:184.

2) Cleanup des orphelins existants via Migration v10

Ajouter dans src-tauri/src/lib.rs:

Migration {
    version: 10,
    description: "cleanup orphan balance snapshots",
    sql: "DELETE FROM balance_snapshots
            WHERE NOT EXISTS (
                SELECT 1 FROM balance_snapshot_lines
                 WHERE snapshot_id = balance_snapshots.id);",
    kind: MigrationKind::Up,
}

S'execute une fois sur les profils existants. Le BEGIN/COMMIT empeche desormais la creation de nouveaux orphelins -> pas besoin de helper runtime.

Fichiers concernes

  • src/hooks/useSnapshotEditor.ts:426-508 - refactor save (validate-first + BEGIN/COMMIT)
  • src/hooks/useSnapshotEditor.test.ts - test transactionnel
  • src/services/balance.service.ts - eventuellement helper cleanupOrphanSnapshots() exporte pour tests
  • src/services/balance.service.test.ts - test du cleanup
  • src-tauri/src/lib.rs - Migration { version: 10, ... }
  • src-tauri/src/database/consolidated_schema.sql - PAS modifie (la migration s'applique aux profils existants, le schema consolide initial reste tel quel)
  • docs/architecture.md - section "Bilan" mise a jour si helper exporte
  • CHANGELOG.md + CHANGELOG.fr.md - section Fixed

Depends on

  • #175 (eviter conflits sur balance.service.ts si modifie en parallele)

Criteres d'acceptation

  • Save avec une priced line a quantity invalide ne cree PAS de snapshot row (test verifie row count == 0 avant et apres save fail)
  • Save reussi cree exactement 1 snapshot row + N lignes (test verifie counts)
  • Migration v10 nettoie les orphelins existants (test sur DB seedee avec un orphelin)
  • Le user peut sauver un snapshot a une date apres avoir ferme/recharge un brouillon casse
  • Sequence BEGIN / INSERT snapshots / INSERT lines / COMMIT (ou ROLLBACK sur erreur) verifiable dans les tests
  • cargo check (migration v10) + npm test + npm run build verts
  • Entree CHANGELOG sous [Unreleased] -> Fixed (FR + EN)

Decisions prises ce soir

  • Atomicite: BEGIN/COMMIT explicite (pattern existant categorizationService.ts:226)
  • Cleanup legacy: Migration v10 one-shot (pas de helper runtime, le BEGIN/COMMIT empeche les nouveaux orphelins)
  • Pas de modification de consolidated_schema.sql (la migration suffit; les nouveaux profils ne peuvent pas avoir d'orphelins puisqu'ils ne contiennent rien au depart)
  • Tests de regression OBLIGATOIRES pour eviter recurrence

Spec source

spec-plan-bilan-anomalies-174.md

## Description L'utilisateur rapporte: save snapshot bloque avec message "changer la date" alors qu'aucun snapshot n'est visible a cette date. Cause: snapshots orphelins (zero ligne) crees par des saves precedents qui ont throw apres l'INSERT du snapshot row mais avant l'INSERT des lignes. `src/hooks/useSnapshotEditor.ts:434` execute `createSnapshot` AVANT de valider les lignes. Si `snapshot_priced_quantity_required` / `snapshot_priced_unit_price_required` / `snapshot_value_invalid` throw, le snapshot row reste orphelin en BDD et bloque tout save futur a cette date via le check `snapshot_date_taken`. ## Approche (decidee 2026-04-30) **1) Atomicite via BEGIN/COMMIT explicite + validate-first** Refactor `save` dans `useSnapshotEditor.ts:426-508`: 1. Construire en memoire `simpleLines` + `pricedLines` avec tous les `throw new BalanceServiceError(...)` (lignes 443-497) AVANT toute mutation BDD 2. Si tout valide: `await db.execute("BEGIN")` 3. `INSERT INTO balance_snapshots ...` (via `createSnapshot`) 4. `INSERT INTO balance_snapshot_lines ...` (via `upsertSnapshotLines`) 5. `await db.execute("COMMIT")` sur succes 6. `await db.execute("ROLLBACK")` dans le `catch` Pattern deja utilise dans `categorizationService.ts:226` et `categoryMigrationService.ts:184`. **2) Cleanup des orphelins existants via Migration v10** Ajouter dans `src-tauri/src/lib.rs`: ```rust Migration { version: 10, description: "cleanup orphan balance snapshots", sql: "DELETE FROM balance_snapshots WHERE NOT EXISTS ( SELECT 1 FROM balance_snapshot_lines WHERE snapshot_id = balance_snapshots.id);", kind: MigrationKind::Up, } ``` S'execute une fois sur les profils existants. Le BEGIN/COMMIT empeche desormais la creation de nouveaux orphelins -> pas besoin de helper runtime. ## Fichiers concernes - `src/hooks/useSnapshotEditor.ts:426-508` - refactor `save` (validate-first + BEGIN/COMMIT) - `src/hooks/useSnapshotEditor.test.ts` - test transactionnel - `src/services/balance.service.ts` - eventuellement helper `cleanupOrphanSnapshots()` exporte pour tests - `src/services/balance.service.test.ts` - test du cleanup - `src-tauri/src/lib.rs` - Migration { version: 10, ... } - `src-tauri/src/database/consolidated_schema.sql` - PAS modifie (la migration s'applique aux profils existants, le schema consolide initial reste tel quel) - `docs/architecture.md` - section "Bilan" mise a jour si helper exporte - `CHANGELOG.md` + `CHANGELOG.fr.md` - section Fixed ## Depends on - #175 (eviter conflits sur balance.service.ts si modifie en parallele) ## Criteres d'acceptation - [ ] Save avec une priced line a quantity invalide ne cree PAS de snapshot row (test verifie row count == 0 avant et apres save fail) - [ ] Save reussi cree exactement 1 snapshot row + N lignes (test verifie counts) - [ ] Migration v10 nettoie les orphelins existants (test sur DB seedee avec un orphelin) - [ ] Le user peut sauver un snapshot a une date apres avoir ferme/recharge un brouillon casse - [ ] Sequence `BEGIN` / `INSERT snapshots` / `INSERT lines` / `COMMIT` (ou `ROLLBACK` sur erreur) verifiable dans les tests - [ ] `cargo check` (migration v10) + `npm test` + `npm run build` verts - [ ] Entree CHANGELOG sous `[Unreleased]` -> `Fixed` (FR + EN) ## Decisions prises ce soir - Atomicite: BEGIN/COMMIT explicite (pattern existant `categorizationService.ts:226`) - Cleanup legacy: Migration v10 one-shot (pas de helper runtime, le BEGIN/COMMIT empeche les nouveaux orphelins) - Pas de modification de `consolidated_schema.sql` (la migration suffit; les nouveaux profils ne peuvent pas avoir d'orphelins puisqu'ils ne contiennent rien au depart) - Tests de regression OBLIGATOIRES pour eviter recurrence ## Spec source spec-plan-bilan-anomalies-174.md
maximus added this to the overnight-2026-05-01-bilan-anomalies-174 milestone 2026-05-01 01:28:35 +00:00
maximus added the
source:human
status:ready
type:bug
labels 2026-05-01 01:28:36 +00:00
maximus added
status:approved
autopilot:pending-human
and removed
status:ready
labels 2026-05-01 11:34:11 +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#176
No description provided.