feat: add month breakdown and session backups
Allow recurring costs and confirmations to remain unassigned when created from a purchase without a post. Add browser backup retention and restore controls, with coverage and documentation updates.
This commit is contained in:
+29
-5
@@ -118,6 +118,28 @@ uses on a first run, so "reset" and "a brand-new browser" cannot drift apart.
|
||||
Both are preceded in the UI by a JSON backup download that must succeed before
|
||||
the destructive write is attempted.
|
||||
|
||||
## Session backups
|
||||
|
||||
On each page load, `src/frontend.tsx` waits for `store.load()`, then saves the
|
||||
loaded dataset to a separate `backups` object store in the same IndexedDB
|
||||
before rendering controls that can edit it. A failed backup does not prevent
|
||||
access to the budget; the app shows an alert with the failure. The snapshot is
|
||||
JSON, separate from the live `state` record, and is never read as live data.
|
||||
|
||||
Retention keeps the newest checkpoint from each of 7 UTC days, 8 Monday-based
|
||||
UTC weeks and 12 UTC months. A checkpoint may cover multiple tiers. In
|
||||
backup persistence, writing the new checkpoint and deleting checkpoints
|
||||
outside that set share one IndexedDB transaction: a failed write cannot prune
|
||||
older backups. Settings lists retained checkpoints for download and restore.
|
||||
Restoring parses through the same JSON import boundary, downloads the current
|
||||
active dataset first, then calls `store.replace()`; if the download fails, the
|
||||
active data stays put. Reset replaces the active dataset but leaves session
|
||||
backups available.
|
||||
|
||||
These backups use the same browser origin as the live dataset. They help with
|
||||
accidental edits or resets, but do not survive loss of all browser storage.
|
||||
Manual JSON export remains the portable backup.
|
||||
|
||||
## Session undo and redo
|
||||
|
||||
The snapshot store keeps up to 50 prior saved datasets in memory. A changed
|
||||
@@ -175,11 +197,13 @@ test — and charges a fixed "2000 to Video Games" in full on every slice.
|
||||
|
||||
A purchase with no post has an empty `splits` array. Its dated amount counts in
|
||||
monthly, yearly and summary Spent totals, but charges no envelope and changes no
|
||||
rollover balance. Recurring bills still require a destination post. JSON import
|
||||
and ODS export preserve these unassigned purchases. In the month Budget tree,
|
||||
they appear in the permanent, derived Unassigned section for each month their
|
||||
purchase or finance plan touches. Creating a post from one assigns its full
|
||||
amount to the new post without changing the purchase total or schedule.
|
||||
rollover balance. A recurring bill may also have empty splits: its occurrences
|
||||
still appear in Expected and Bills due, but they move no post's projected
|
||||
balance. Confirming one creates an unassigned purchase, which counts in Spent.
|
||||
JSON import and ODS export preserve these unassigned purchases. In the month
|
||||
Budget tree, purchases appear in the permanent, derived Unassigned section for
|
||||
each month they touch. Creating a post from one assigns its full amount to the
|
||||
new post without changing the purchase total or schedule.
|
||||
|
||||
A post with no applicable allocation rule has no overspend status, even if its
|
||||
balance is negative. The balance remains visible and carries normally. An
|
||||
|
||||
Reference in New Issue
Block a user