feat(balance): data layer — vehicle_type + custom_label migrations, starters, service #206

Merged
maximus merged 1 commit from issue-202-data-layer-vehicle-type into main 2026-06-02 00:43:35 +00:00
Owner

Fixes #202

Foundational data layer for Bilan axe véhicule (Étape 1): the fiscal envelope becomes an account attribute (balance_accounts.vehicle_type), the category stays a pure asset class.

Changes

  • Migration v12 (additive): vehicle_type (nullable, CHECK fiscal enum) on balance_accounts + custom_label on balance_categories; backfill envelope onto ex-tfsa/rrsp accounts (cash → NULL); defensive recovery of any seed i18n_key overwritten by free text (bug I).
  • Migration v13 (reclass, conditional/idempotent): re-link ex-tfsa/rrsp accounts to the other asset class (EXISTS + is_seed guards), deactivate the two envelope seeds.
  • consolidated_schema.sql: 2 new columns; 5 asset-class seeds (tfsa/rrsp removed); CELI/REER starters re-pointed to other + vehicle_type (prevents NULL FK → broken new-profile init).
  • Types: BalanceVehicleType, custom_label, vehicle_type, category_custom_label.
  • Service: normalizeVehicleType + vehicle_type_invalid; CRUD writes the columns; SELECT/JOINs read them back (no UPDATE wipe); STARTER_ACCOUNTS/proposeStarterAccounts (is_active=1 + vehicle_type)/getStarterCollisions adjusted.

Tests

  • cargo: 80 passed, 0 failed (9 new: v12/v13 chain v9→v13 with identical snapshot_lines + intact transfers + archived ex-tfsa, seeds deactivated, v13 EXISTS no-op guard, idempotence, CHECK reject; consolidated complete = 5 categories / 4 starters / 0 NULL FK).
  • vitest: 3300 passed, 0 failed (custom_label round-trip, vehicle_type valid/invalid, SELECT columns, starter mapping).
  • build (tsc + vite): ok.
  • Zero diff on migrations ≤ v11 (lib.rs lines 11–145 byte-identical to main).

🤖 Generated with Claude Code

Fixes #202 Foundational data layer for Bilan axe véhicule (Étape 1): the fiscal envelope becomes an account attribute (`balance_accounts.vehicle_type`), the category stays a pure asset class. ## Changes - **Migration v12** (additive): `vehicle_type` (nullable, CHECK fiscal enum) on `balance_accounts` + `custom_label` on `balance_categories`; backfill envelope onto ex-tfsa/rrsp accounts (cash → NULL); defensive recovery of any seed `i18n_key` overwritten by free text (bug I). - **Migration v13** (reclass, conditional/idempotent): re-link ex-tfsa/rrsp accounts to the `other` asset class (EXISTS + is_seed guards), deactivate the two envelope seeds. - **consolidated_schema.sql**: 2 new columns; 5 asset-class seeds (tfsa/rrsp removed); CELI/REER starters re-pointed to `other` + `vehicle_type` (prevents NULL FK → broken new-profile init). - **Types**: `BalanceVehicleType`, `custom_label`, `vehicle_type`, `category_custom_label`. - **Service**: `normalizeVehicleType` + `vehicle_type_invalid`; CRUD writes the columns; SELECT/JOINs read them back (no UPDATE wipe); `STARTER_ACCOUNTS`/`proposeStarterAccounts` (is_active=1 + vehicle_type)/`getStarterCollisions` adjusted. ## Tests - cargo: **80 passed, 0 failed** (9 new: v12/v13 chain v9→v13 with identical snapshot_lines + intact transfers + archived ex-tfsa, seeds deactivated, v13 EXISTS no-op guard, idempotence, CHECK reject; consolidated complete = 5 categories / 4 starters / 0 NULL FK). - vitest: **3300 passed, 0 failed** (custom_label round-trip, vehicle_type valid/invalid, SELECT columns, starter mapping). - build (tsc + vite): ok. - **Zero diff on migrations ≤ v11** (lib.rs lines 11–145 byte-identical to main). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
maximus added 1 commit 2026-06-02 00:38:14 +00:00
feat(balance): data layer — vehicle_type + custom_label migrations, starters, service (#202)
All checks were successful
PR Check / rust (pull_request) Successful in 22m31s
PR Check / frontend (pull_request) Successful in 2m26s
5861346eb3
Bilan axe véhicule (Étape 1) data foundation: separate the fiscal envelope
(now balance_accounts.vehicle_type) from the asset class (the category).

- Migration v12 (additive): add vehicle_type (fiscal enum, nullable) to
  balance_accounts + custom_label to balance_categories; backfill the envelope
  onto ex-tfsa/rrsp accounts (cash stays NULL); defensive recovery of any seed
  i18n_key overwritten by free text (bug I).
- Migration v13 (reclass, conditional/idempotent): re-link ex-tfsa/rrsp accounts
  to the `other` asset class and deactivate the two envelope seeds.
- consolidated_schema.sql: 2 new columns, 5 asset-class seeds (no tfsa/rrsp),
  CELI/REER starters re-pointed to `other` + vehicle_type (avoids NULL FK).
- Types: BalanceVehicleType, custom_label / vehicle_type / category_custom_label.
- Service: normalizeVehicleType + vehicle_type_invalid; CRUD writes the new
  columns; SELECT/JOINs read them back; STARTER_ACCOUNTS + proposeStarterAccounts
  (is_active=1 + vehicle_type) + getStarterCollisions adjusted.
- Tests: Rust chain v9→v13 (snapshot_lines identical, transfers intact, archived
  ex-tfsa covered, seeds deactivated, v13 EXISTS guard, idempotence, CHECK reject)
  + consolidated complete (5 categories, 4 starters, 0 NULL FK); TS service +
  StarterAccountsModal specs. No diff to migrations <= v11.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author
Owner

Review — APPROVE

Revue adversariale indépendante (orchestrateur /sprint). Diff vérifié directement sur la branche, pas seulement le self-report du worker.

Vérifications critiques

  • Checksum migrations ≤ v11 : 0 ligne supprimée/modifiée dans lib.rs sur les versions 1–11 (diff direct). v12/v13 ajoutées après v11 ; constantes de test V12_SQL/V13_SQL au pattern v10/v11.
  • SQL v12/v13 conforme à la spec : CHECK fiscal mono-colonne, backfill tfsa/rrsp par sous-SELECT, backfill défensif bug I (balance.category.% cohérent avec la convention de seed), reclass v13 gardé par EXISTS(other) AND is_seed=1, idempotent.
  • Profil neuf (risque #1) : starters consolidated CELI/REER → other + vehicle_type, aucun FK NULL. Test consolidated_schema_inits_cleanly_with_5_categories_and_4_starters assère 0 balance_category_id NULL, 4 starters, 5 catégories actives.
  • Pas de wipe à l'UPDATE : updateBalanceAccount/updateBalanceCategory lisent-puis-réécrivent ; SELECT/JOIN étendus (vehicle_type, custom_label, category_custom_label).
  • Starters : proposeStarterAccounts filtre is_active=1 ; getStarterCollisions désambiguïse les 3 starters de classe other par (categoryKey, name).

Qualité des tests (pas des coquilles) : migration_v9_to_v13_chain_... capture l'empreinte des snapshot_lines avant/après et assère l'égalité exacte + véhicule conservé + transfert intact. CHECK rejeté couvert (migration + consolidated). cargo test 80 ✓, vitest 3300 ✓, npm run build ok.

Déviations acceptées (justifiées)

  • listBalanceCategories({includeInactive}) défaut true → le filtre dropdown est porté par #203 (comportement-neutre pour #202).
  • StarterDef.key gardé comme identifiants UI (cash/tfsa/rrsp/other) ; categoryKey réduit à cash|other + champ vehicleType.

Non bloquant : entre le merge de #202 et celui de #203, la page de gestion (includeInactive=true) affichera les seeds tfsa/rrsp désactivés — résolu par #203, shippés ensemble.

Verdict : APPROVE. PR fidèle à la spec, tests rigoureux, invariants de précision financière préservés.

## Review — APPROVE ✅ Revue adversariale indépendante (orchestrateur `/sprint`). Diff vérifié directement sur la branche, pas seulement le self-report du worker. **Vérifications critiques** - ✅ **Checksum migrations ≤ v11** : 0 ligne supprimée/modifiée dans `lib.rs` sur les versions 1–11 (diff direct). v12/v13 ajoutées après v11 ; constantes de test `V12_SQL`/`V13_SQL` au pattern v10/v11. - ✅ **SQL v12/v13** conforme à la spec : CHECK fiscal mono-colonne, backfill tfsa/rrsp par sous-`SELECT`, backfill défensif bug I (`balance.category.%` cohérent avec la convention de seed), reclass v13 gardé par `EXISTS(other) AND is_seed=1`, idempotent. - ✅ **Profil neuf (risque #1)** : starters consolidated CELI/REER → `other` + `vehicle_type`, aucun FK NULL. Test `consolidated_schema_inits_cleanly_with_5_categories_and_4_starters` assère 0 `balance_category_id` NULL, 4 starters, 5 catégories actives. - ✅ **Pas de wipe à l'UPDATE** : `updateBalanceAccount`/`updateBalanceCategory` lisent-puis-réécrivent ; SELECT/JOIN étendus (`vehicle_type`, `custom_label`, `category_custom_label`). - ✅ **Starters** : `proposeStarterAccounts` filtre `is_active=1` ; `getStarterCollisions` désambiguïse les 3 starters de classe `other` par `(categoryKey, name)`. **Qualité des tests** (pas des coquilles) : `migration_v9_to_v13_chain_...` capture l'empreinte des `snapshot_lines` avant/après et assère l'égalité exacte + véhicule conservé + transfert intact. CHECK rejeté couvert (migration + consolidated). `cargo test` 80 ✓, `vitest` 3300 ✓, `npm run build` ok. **Déviations acceptées** (justifiées) - `listBalanceCategories({includeInactive})` défaut `true` → le filtre dropdown est porté par #203 (comportement-neutre pour #202). - `StarterDef.key` gardé comme identifiants UI (cash/tfsa/rrsp/other) ; `categoryKey` réduit à `cash|other` + champ `vehicleType`. **Non bloquant** : entre le merge de #202 et celui de #203, la page de gestion (`includeInactive=true`) affichera les seeds tfsa/rrsp désactivés — résolu par #203, shippés ensemble. Verdict : **APPROVE**. PR fidèle à la spec, tests rigoureux, invariants de précision financière préservés.
maximus merged commit cb58bbb31a into main 2026-06-02 00:43:35 +00:00
maximus deleted branch issue-202-data-layer-vehicle-type 2026-06-02 00:43:36 +00:00
Sign in to join this conversation.
No reviewers
No milestone
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#206
No description provided.