fix(splits): the cutover date, not a boolean, is what gates a balance
ci / lint-test (push) Successful in 46s

Nothing dated before 2026-01-09 can be owed, because carryover transaction 2348
already carries the entire pre-cutover balance as a single figure. ACTIVE_OBLIGATION
now says so directly.

This inverts which mechanism is load-bearing, and that is the point. Until now
the only thing keeping $37,233.28 of paid debt out of the balances was
transaction_splits.settled -- a boolean that any delete-and-recreate write path
resets to false, as the split modal did until commit 6add958. Losing the flag on
a pre-cutover row now costs nothing: the date still excludes it. The flag matters
only on or after the cutover, marking the few settled outside this app.

It also makes splitting history safe to do freely. A split on a 2024 grocery
shop can now describe how the expense was shared -- which is what stops it
inflating spend -- without asserting a debt that was settled years ago. That was
the whole reason not to split pre-2026 expenses, and it no longer applies.

The bound is inclusive because transaction 2348 is itself dated 2026-01-09; an
exclusive one would drop the carryover and with it the entire pre-cutover
balance.

Corrects one live figure: a Woolworths on 2026-01-06 was split 50/50 three days
before the cutover, double-counting $7.55 against the carryover. Sonu
$5,428.08 -> $5,420.53.

The test fixture default moved to 2026-06-15 -- it was 2024-06-15, which is now
pre-cutover and made every balance fixture read zero. That the suite caught this
is the guard working.
This commit is contained in:
2026-07-28 13:52:30 +10:00
parent 6add958132
commit 788219b9fd
3 changed files with 81 additions and 3 deletions
+22 -2
View File
@@ -217,9 +217,29 @@ END`;
* deliberately no "mark settled" action anywhere: settling up is recording a
* payment, and this column is only ever written by the historical import.
*
* Assumes the `transaction_splits` alias is `ts`.
* **The date is the real guard, and the flag is only a refinement of it.**
* Nothing before SPLIT_CUTOVER can be owed, because carryover transaction 2348
* already carries the entire pre-cutover balance as a single figure. A split on
* an older transaction is therefore free to describe *how an expense was shared*
* — which is what stops it inflating spend — without ever asserting a debt.
*
* That separation is what makes splitting history safe. Before it, the only
* thing keeping $37,233.28 of paid debt out of the balances was a boolean that
* any delete-and-recreate write path silently reset to false. Now losing the
* flag on a pre-cutover row costs nothing: the date still excludes it. The flag
* matters only for rows on or after the cutover, where it marks the handful
* settled outside this app.
*
* The boundary is inclusive because transaction 2348 is itself dated
* 2026-01-09 — an exclusive bound would drop the carryover and with it the
* entire pre-cutover balance.
*
* Assumes the `transaction_splits` alias is `ts` and `transactions` is `t`.
*/
export const ACTIVE_OBLIGATION = `ts.settled = false`;
export const SPLIT_CUTOVER = "2026-01-09";
export const ACTIVE_OBLIGATION = `ts.settled = false
AND t.transaction_date >= '${SPLIT_CUTOVER}'`;
/**
* The tab a split belongs to: its transaction's trip, else the household.