feat(balance): add asset_type column to balance_categories #170

Merged
maximus merged 1 commit from issue-169-asset-type-balance-categories into main 2026-04-29 00:47:02 +00:00
Owner

Summary

Adds an explicit asset_type column ('stock' | 'crypto') to balance_categories so PriceFetchControl can route to the right provider without symbol heuristics. Closes the schema gap flagged in reports/DAILY-REPORT-2026-04-27.md (PR #167 hardcoded 'stock' because the schema didn't expose stock-vs-crypto).

Changes

  • Migration v10 in src-tauri/src/lib.rs — additive ALTER TABLE + backfill for the two priced seeds. Touches no v9 SQL (checksum preserved).
  • consolidated_schema.sql — column + seed values for fresh profiles.
  • TypeScript surface — new BalanceAssetType alias, asset_type field on BalanceCategory and category_asset_type on BalanceAccountWithCategory. Service validates priced creation requires asset_type and coerces simple kind to NULL.
  • AccountForm CategoryVariant — asset-type selector visible only when kind = priced, blocks submit until picked, resets on kind switch.
  • SnapshotLineRow — reads account.category_asset_type; hides PriceFetchControl when NULL on a priced row (legacy custom rows). Both TODO references removed.
  • i18n — 4 keys under balance.category.assetType.* (FR + EN).
  • CHANGELOG.md / CHANGELOG.fr.md[Unreleased] / Added entry.

Test plan

  • cargo test --lib — 69 passed (4 new v10 migration tests: column add, seed backfill, legacy row stays NULL, CHECK rejects invalid value)
  • npm test — 3701 passed (6 new vitest cases on createBalanceCategory + listBalanceAccounts SELECT assertion)
  • npx tsc --noEmit — clean
  • npm run build — clean (vite build + tsc)
  • Manual demo: create a priced category without asset_type → form blocks; pick stock vs crypto → snapshot editor shows correct PriceFetchControl behavior; legacy priced row (no asset_type) → fetch button hidden, manual entry only.

Notes

  • Pre-existing doctest failure in src/commands/return_calculator.rs - line 12 is unrelated (introduced in commit c9cdb5a). cargo test --lib is green; cargo test --doc is broken on main already.
  • No SnapshotLineRow.test added: project lacks @testing-library/react + jsdom (consistent with PR #167 / autopilot decisions). The render guard is a one-line boolean expression — covered by manual QA.
  • Edit-category UI is still out of scope (no edit form for balance categories yet). The service-level UpdateBalanceCategoryInput accepts asset_type so a future issue can add the UI without touching the service.

Fixes #169

## Summary Adds an explicit `asset_type` column ('stock' | 'crypto') to `balance_categories` so PriceFetchControl can route to the right provider without symbol heuristics. Closes the schema gap flagged in `reports/DAILY-REPORT-2026-04-27.md` (PR #167 hardcoded `'stock'` because the schema didn't expose stock-vs-crypto). ## Changes - **Migration v10** in `src-tauri/src/lib.rs` — additive `ALTER TABLE` + backfill for the two priced seeds. Touches no v9 SQL (checksum preserved). - **`consolidated_schema.sql`** — column + seed values for fresh profiles. - **TypeScript surface** — new `BalanceAssetType` alias, `asset_type` field on `BalanceCategory` and `category_asset_type` on `BalanceAccountWithCategory`. Service validates priced creation requires asset_type and coerces simple kind to NULL. - **AccountForm CategoryVariant** — asset-type selector visible only when `kind = priced`, blocks submit until picked, resets on kind switch. - **SnapshotLineRow** — reads `account.category_asset_type`; hides PriceFetchControl when NULL on a priced row (legacy custom rows). Both TODO references removed. - **i18n** — 4 keys under `balance.category.assetType.*` (FR + EN). - **CHANGELOG.md / CHANGELOG.fr.md** — `[Unreleased] / Added` entry. ## Test plan - [x] `cargo test --lib` — 69 passed (4 new v10 migration tests: column add, seed backfill, legacy row stays NULL, CHECK rejects invalid value) - [x] `npm test` — 3701 passed (6 new vitest cases on createBalanceCategory + listBalanceAccounts SELECT assertion) - [x] `npx tsc --noEmit` — clean - [x] `npm run build` — clean (vite build + tsc) - [ ] Manual demo: create a priced category without asset_type → form blocks; pick stock vs crypto → snapshot editor shows correct PriceFetchControl behavior; legacy priced row (no asset_type) → fetch button hidden, manual entry only. ## Notes - Pre-existing doctest failure in `src/commands/return_calculator.rs - line 12` is unrelated (introduced in commit c9cdb5a). `cargo test --lib` is green; `cargo test --doc` is broken on `main` already. - No SnapshotLineRow.test added: project lacks `@testing-library/react` + jsdom (consistent with PR #167 / autopilot decisions). The render guard is a one-line boolean expression — covered by manual QA. - Edit-category UI is still out of scope (no edit form for balance categories yet). The service-level `UpdateBalanceCategoryInput` accepts `asset_type` so a future issue can add the UI without touching the service. Fixes #169
maximus added 1 commit 2026-04-28 23:54:29 +00:00
feat(balance): add asset_type column to balance_categories
All checks were successful
PR Check / rust (push) Successful in 23m42s
PR Check / frontend (push) Successful in 2m26s
PR Check / rust (pull_request) Successful in 22m55s
PR Check / frontend (pull_request) Successful in 2m24s
3963f552ae
Priced balance categories now carry an explicit `asset_type`
('stock' | 'crypto') so PriceFetchControl can route to the right
provider without symbol heuristics. ETH = Ethan Allen NYSE AND
Ethereum crypto are no longer ambiguous.

Migration v10 adds a nullable column and backfills the two seeded
priced categories (key='stock','crypto'). Legacy custom priced rows
stay NULL until the user edits the category — SnapshotLineRow hides
the price-fetch button when asset_type is NULL on a priced row, so
manual entry remains available.

Service-side validation rejects priced creation without asset_type
('asset_type_required') and rejects values outside ('stock','crypto')
('asset_type_invalid'). Simple kind coerces asset_type to NULL.

The CategoryVariant of AccountForm shows the selector only when
kind=priced, requires it on submit, and resets it on kind switch.
i18n keys added under balance.category.assetType.* (FR + EN).

Tests:
- 4 new Rust migration tests in lib.rs (column add, seed backfill,
  legacy row stays NULL, CHECK rejects 'gold')
- 6 new vitest cases on createBalanceCategory + listBalanceAccounts
  asserts c.asset_type AS category_asset_type in the join
- balance-flow integration test updated to pass asset_type='stock'

No new test for SnapshotLineRow render guard — project lacks
@testing-library/react + jsdom; the guard is one boolean expression
covered by manual QA per autopilot decisions in PR #167.

Fixes #169
maximus added the
status:review
type:feature
type:schema
source:human
labels 2026-04-28 23:54:37 +00:00
Author
Owner

Verdict — APPROVE

Summary — Migration v10 additive impeccable, validation en triple défense (UI bloque submit → service normalizeAssetTypeForKind → SQL CHECK), suppression des deux TODO et du hardcode "stock" dans SnapshotLineRow. Tests Rust (4 cas) et TS (6 cas) couvrent les chemins critiques.

Points vérifiés

  • Sécurité : aucun secret, paramètres bind partout, validation au boundary service.
  • Migration v10 : additive, nullable, backfill gardé par is_seed = 1 (les rangées custom restent NULL — comportement attendu et testé).
  • consolidated_schema.sql : convergent avec v10 (nouveaux profils ↔ profils migrés produisent le même état).
  • Service : normalizeAssetTypeForKind coerce simple→NULL et exige priced. Codes d'erreur typés (asset_type_required, asset_type_invalid).
  • AccountForm : handleKindChange reset asset_type au switch priced→simple, submit bloqué tant que asset_type manquant sur priced.
  • SnapshotLineRow : guard account.category_asset_type cache PriceFetchControl sur lignes legacy NULL. Plus de hardcode.
  • listBalanceAccounts : SELECT thread c.asset_type AS category_asset_type, type TypeScript aligné.
  • i18n : 4 clés FR + EN sous balance.category.assetType.*.
  • CHANGELOG : entrée bilingue claire avec exemple ETH (Ethan Allen vs Ethereum) qui justifie le besoin.
  • Tests : suite verte (cargo --lib 69 / npm 3701 / tsc / build), pas de skip/only.

Suggestions non-bloquantes

  1. src/services/balance.service.ts:614-619 — le JSDoc de UpdateBalanceCategoryInput.asset_type dit "The service rejects an explicit null when the existing kind is priced". C'est exact, mais le mot "rejects" est un peu ambigu — ça lève en réalité BalanceServiceError("asset_type_required"). Préciser le code d'erreur dans le commentaire aiderait à la découvrabilité.

  2. Edit-category UI — la PR mentionne (à raison) qu'il n'y a pas de UI d'édition pour les catégories. Le service est prêt (UpdateBalanceCategoryInput.asset_type accepté), donc tracker une issue de suivi pour permettre aux utilisateurs de remplir le NULL sur leurs lignes legacy custom (sinon elles restent en saisie manuelle pour toujours).

  3. Doctest cassé sur main — bien noté dans la PR. Le pré-existant cargo test --doc sur return_calculator.rs:12 n'est pas dans le scope. À adresser dans une PR séparée.

LGTM, merge quand tu veux.

## Verdict — APPROVE **Summary** — Migration v10 additive impeccable, validation en triple défense (UI bloque submit → service `normalizeAssetTypeForKind` → SQL CHECK), suppression des deux TODO et du hardcode `"stock"` dans `SnapshotLineRow`. Tests Rust (4 cas) et TS (6 cas) couvrent les chemins critiques. ## Points vérifiés - **Sécurité** : aucun secret, paramètres bind partout, validation au boundary service. - **Migration v10** : additive, nullable, backfill gardé par `is_seed = 1` (les rangées custom restent NULL — comportement attendu et testé). - **`consolidated_schema.sql`** : convergent avec v10 (nouveaux profils ↔ profils migrés produisent le même état). - **Service** : `normalizeAssetTypeForKind` coerce simple→NULL et exige priced. Codes d'erreur typés (`asset_type_required`, `asset_type_invalid`). - **`AccountForm`** : `handleKindChange` reset `asset_type` au switch priced→simple, submit bloqué tant que asset_type manquant sur priced. - **`SnapshotLineRow`** : guard `account.category_asset_type` cache PriceFetchControl sur lignes legacy NULL. Plus de hardcode. - **`listBalanceAccounts`** : SELECT thread `c.asset_type AS category_asset_type`, type TypeScript aligné. - **i18n** : 4 clés FR + EN sous `balance.category.assetType.*`. - **CHANGELOG** : entrée bilingue claire avec exemple ETH (Ethan Allen vs Ethereum) qui justifie le besoin. - **Tests** : suite verte (cargo --lib 69 / npm 3701 / tsc / build), pas de skip/only. ## Suggestions non-bloquantes 1. **`src/services/balance.service.ts:614-619`** — le JSDoc de `UpdateBalanceCategoryInput.asset_type` dit *"The service rejects an explicit `null` when the existing kind is priced"*. C'est exact, mais le mot "rejects" est un peu ambigu — ça lève en réalité `BalanceServiceError("asset_type_required")`. Préciser le code d'erreur dans le commentaire aiderait à la découvrabilité. 2. **Edit-category UI** — la PR mentionne (à raison) qu'il n'y a pas de UI d'édition pour les catégories. Le service est prêt (`UpdateBalanceCategoryInput.asset_type` accepté), donc tracker une issue de suivi pour permettre aux utilisateurs de remplir le NULL sur leurs lignes legacy custom (sinon elles restent en saisie manuelle pour toujours). 3. **Doctest cassé sur main** — bien noté dans la PR. Le pré-existant `cargo test --doc` sur `return_calculator.rs:12` n'est pas dans le scope. À adresser dans une PR séparée. LGTM, merge quand tu veux.
maximus merged commit 3342fd9bb7 into main 2026-04-29 00:47:02 +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#170
No description provided.