diff --git a/README.md b/README.md index 81c4a70..5e058db 100755 --- a/README.md +++ b/README.md @@ -67,9 +67,15 @@ in a field, those shortcuts use the browser's text undo. History holds up to Both destructive actions there — Import, and "Reset everything" — download a backup of your current data before replacing it, and abort if that download fails. Reset puts the app back to a brand-new install: the three starter posts, -the DKK/USD/EUR currency table, this month at zero income, no purchases. +the DKK/USD/EUR/GBP/JPY/AUD/CAD/MXN currency table, this month at zero income, +no purchases. Decimal places are one setting for the whole budget. Import and reset clear session undo history after they succeed. +The month page shows Actual available beside Spent: entered income minus actual +spending for that month. Show chart and Expected's Expand/Collapse controls +remember their state in this browser. Add purchase can create a new recurring +cost; enable choosing existing bills under Settings → Recurring costs. + ## Known limitations - **Data is per-browser.** There is no account and no sync. If you clear this diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index 157f0d6..00eeab1 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -32,6 +32,14 @@ record. ## Money and the data model +- **Actual available is this month's income minus actual spending.** Unpaid + bills, allocations and carry are separate figures. Purchases still count + before the envelope fold's start. + → [`specs/2026-10-03-month-and-purchase-refinements.md`](specs/2026-10-03-month-and-purchase-refinements.md) +- **JPY, AUD, CAD and MXN seed new budgets only.** Existing currency tables + remain under the owner's control; all currencies use the dataset's precision. + → [`specs/2026-10-03-month-and-purchase-refinements.md`](specs/2026-10-03-month-and-purchase-refinements.md) + - **Money is a float, not an integer of minor units.** Made safe by two rules rather than by the type: round at every boundary, and compute a distributed last part as `total - sum(others)`. @@ -53,7 +61,7 @@ record. code change. The compile-time safety is replaced by validation at the boundaries — `parseDatasetJson` rejects a code no currency defines. → `src/domain/types.ts`, commit `cdece63` -- **Every derived number comes from one left fold.** `src/domain/fold.ts` is the +- **Envelope rollover has one left fold.** `src/domain/fold.ts` is the only implementation; year and summary views aggregate over it rather than recomputing. Two screens disagreeing about someone's money is the worst failure this app has. → [`ARCHITECTURE.md`](ARCHITECTURE.md) @@ -89,6 +97,12 @@ record. ## Recurring costs +- **Creating a recurring cost is the default recurring action in Add purchase.** + Choosing an existing bill is enabled explicitly in Settings; ordinary purchases + still start unlinked, and editing preserves an existing link. Chosen over + showing existing bills on every new purchase. + → [`specs/2026-10-03-month-and-purchase-refinements.md`](specs/2026-10-03-month-and-purchase-refinements.md) + - **A recurring cost may have no post.** It still appears as due, while no envelope projection moves; confirming it records an unassigned purchase. This lets the purchase dialog turn an unassigned charge into a bill without @@ -150,6 +164,11 @@ record. ## Design system +- **Chart visibility and Expected's expanded state persist per browser.** + Chosen over resetting on every visit or moving display choices with a JSON + budget export, following the theme's storage boundary. + → [`specs/2026-10-03-month-and-purchase-refinements.md`](specs/2026-10-03-month-and-purchase-refinements.md) + - **Two token layers.** `--budget-*` is the app's own vocabulary; shadcn's entire variable set points at it, so every primitive inherits both themes. → [`specs/2026-09-02-design-foundation-design.md`](specs/2026-09-02-design-foundation-design.md) @@ -194,6 +213,11 @@ record. ## Build, offline and deploy +- **Colophon shows a source ref and a separate UTC build date.** A ref alone + cannot tell when the artifact was built; the browser's current day cannot + describe it. Source archives do not invent a Git ref. + → [`specs/2026-10-03-month-and-purchase-refinements.md`](specs/2026-10-03-month-and-purchase-refinements.md) + - **Bun is the runtime, package manager, bundler and test runner.** shadcn/ui components are vendored into the repo and owned by this project. → [`specs/2026-09-01-budget-app-design.md`](specs/2026-09-01-budget-app-design.md) @@ -251,8 +275,8 @@ Not oversights. Each was decided against, and reopening one needs a reason. amounts are all **allowed**. Adding validation is a regression. → [`AGENTS.md`](../AGENTS.md) §3 - The income field and the split editor's fixed-amount fields **do not parse a - currency**; only the purchase total does. → [`TODO.md`](TODO.md) §5 -- **No currency picker.** Adding a currency means typing code, name, symbol and - decimals by hand. → [`TODO.md`](TODO.md) §5 + currency**; only the purchase total does. → [`TODO.md`](TODO.md) §7 +- **No currency picker convenience.** Adding a currency means typing its code, + name and symbol by hand. Precision is dataset-wide. → [`TODO.md`](TODO.md) §7 - **Symbol collisions resolve to the first currency defined with that symbol.** - Tested and documented rather than solved. → [`TODO.md`](TODO.md) §5 + Tested and documented rather than solved. → [`TODO.md`](TODO.md) §7 diff --git a/docs/PRODUCT.md b/docs/PRODUCT.md index 0ac19e4..5d114e9 100755 --- a/docs/PRODUCT.md +++ b/docs/PRODUCT.md @@ -74,8 +74,10 @@ Confirmed and in place: - The month header shows actual available: that month's entered income minus actual spending, including unassigned purchases and refunds. Monthly breakdown can be hidden and Expected collapsed; these choices persist in this browser. -- Multi-currency (DKK/USD/EUR) against a user-maintained exchange-rate table, - with an optional on-demand fetch that fails soft and is never required. +- Multi-currency against a user-maintained exchange-rate table. New budgets + start with DKK, USD, EUR, GBP, JPY, AUD, CAD and MXN; existing currency tables + stay under the owner's control. An optional on-demand fetch fails soft and + is never required. Decimal places are one setting across all currencies. - JSON export/import and ODS (spreadsheet) export, both generated in the browser. On each app start, a separate browser-stored session backup is made before editing is available; recent daily, weekly and monthly checkpoints can diff --git a/docs/TODO.md b/docs/TODO.md index fcd40f1..05d3645 100755 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -8,9 +8,9 @@ decisions behind it are in `docs/specs/` — one spec per sub-project, each recording the alternatives that were rejected. Rules that outlived their task moved to `AGENTS.md`. -**Current state:** the anchored model is merged with budget folders and -placement. The month pie showcase, browser-stored session backups, and -unassigned recurring costs are implemented. 911 tests pass across 66 files; +**Current state:** the month and purchase refinements landed in `c250bbe`, +building on the merged anchored model, budget folders and session backups. +911 tests pass across 66 files; `bunx tsc --noEmit` is clean, and `SKIP_FX_FETCH=1 bun run build` succeeds using the committed seed rates. @@ -156,11 +156,12 @@ folders, placement and nested bills. Everything below is known and unfinished. ### A bill created from the purchase dialog has no folder choice -Settings' new-bill form picks a folder; the dialog's "+ New recurring cost…" -does not. The new bill nests under its post when it has exactly one split -(the same default Settings applies), otherwise it lands in Unfiled and has to -be moved from Budget folders. Decide whether the dialog needs the picker or -whether moving it afterwards is fine for an occasional action. +Settings' new-bill form picks a folder; the dialog's "Create a recurring cost +for this purchase…" does not. The new bill nests under its post when it has +exactly one split (the same default Settings applies), otherwise it lands in +Unfiled and has to be moved from Budget folders. Decide whether the dialog +needs the picker or whether moving it afterwards is fine for an occasional +action. ### "Unlink from bill" was replaced by the Recurring cost select @@ -178,12 +179,14 @@ absence. The merge was checked by the full suite, `tsc`, a build, and screenshots of the month page and Settings with demo data (both render, no console errors, Car insurance shows "Next 2026-10-19" after charges on 3 and 19 September). -Not yet clicked through by anyone: +The October round verified purchase save/reopen, creation of a new bill with its +first charge, and the Settings toggle for selecting existing bills. Remaining +browser checks: - tagging a new purchase to an existing anchored bill and seeing its next due date move in the Budget tree and in Expected; -- "+ New recurring cost…" end to end, including where the new bill appears in - Budget folders; +- placement of newly created bills in Budget folders, including both a bill + nested under its one post and a multi-post bill in Unfiled; - untagging an edited purchase via "None"; - the Settings anchoring control switching a never-charged monthly bill to "the last charge" (its month-only start should expand to the 1st and the @@ -232,9 +235,10 @@ than re-decide** (spec: `2026-09-02-month-view-design.md`): ## 6. Visual redesign, sub-project 4 — dialogs and fast entry -Untouched. Native `