feat: refine month totals and recurring purchase entry
Continuous Integration / lint-and-test (push) Successful in 29s
Continuous Integration / docker-build-and-push (push) Successful in 40s

This commit is contained in:
Lucas Winther committed 2026-10-03 16:40:00 +02:00
1 parent 7cccd7a41a
commit c250bbe476
30 files changed
+634 -67

No files matched your search

+26
View File
@@ -62,6 +62,12 @@ aggregate envelope figures over `foldBalances` / `figuresFor`. Overall Spent
also includes unassigned purchase slices, which have no envelope to fold. A
closing balance is read from `byMonth[11].remaining`, never re-summed.
The month header's Actual available is entered income minus all actual Spent
for that month. It includes refunds, unassigned spending and finance-plan slices,
but no allocation, carry or unpaid expectations. Before the rollover start,
the month view reads the existing charge calculation directly because the fold
has deliberately produced no envelope figures yet.
A recurring cost adds a SECOND balance beside it, on its own carry:
```
@@ -157,6 +163,11 @@ still works.
## Money
The default currency table seeds new datasets only. JPY, AUD, CAD and MXN join
DKK, USD, EUR and GBP; existing currency tables are not rewritten. The build
derives its FX targets from the seed table and carries complete committed
fallback rates. These rates never replace one an owner has cleared.
A float, deliberately, with two rules that make it safe:
1. **Round at every boundary** via `roundMoney` — after any division, after any
@@ -187,6 +198,11 @@ which also says what re-adding a per-currency override would take.
## Splits and finance plans compose
Purchase entry defaults to creating a new recurring cost when the owner chooses
to make a purchase recur. Selecting an existing bill is enabled explicitly in
Settings by the optional `allowExistingRecurringCost` boolean (absent is false),
validated on JSON import. Existing purchase associations remain editable.
A purchase can be split across posts *and* spread over months at once. The trick
is in `charges.ts`: first compute how the **whole** purchase divides across posts
(`splitPartsOfTotal`), then distribute each month's slice proportionally *to
@@ -211,6 +227,16 @@ explicit zero rule, including a zero monthly override, does have an overspend
status when its balance is negative. The views use the presence of a rule rather
than infer it from the numeric allocation, since both cases fold to zero.
## Display preferences and build identity
Monthly chart visibility and Expected's expanded state persist per browser,
alongside the theme preference. They never enter an exported dataset or change
the month calculations. Storage errors leave the display controls usable.
Colophon receives a source ref and UTC build date separately from the build.
The date describes the artifact, not the browser's current day. A source archive
has no Git ref but still receives a unique service-worker cache version.
## Things the app deliberately does NOT validate
Adding a "missing" guard here is a regression, not a fix: