docs: splits total 100%, and why the remainder is the owner's
ci / lint-test (push) Successful in 45s
ci / lint-test (push) Successful in 45s
This commit is contained in:
@@ -133,6 +133,37 @@ filter on `settled` — half a 2025 grocery shop was your expense whether or not
|
||||
the other half was repaid. Filtering it out re-inflates exactly the figures the
|
||||
historical import exists to correct.
|
||||
|
||||
**Every split totals 100%, and the payer's row is written down.** `myShare`
|
||||
resolves the payer's share as `100 - SUM(everyone else)`, so a 50/50 stored as
|
||||
a lone "Sonu 50%" row still computed correctly — and still read on screen as a
|
||||
50% share against a blank. `completeSplit` (`src/lib/splits.ts`) is the single
|
||||
place that materialises the remainder, and every write path ends in it:
|
||||
`applyRuleActions`, `POST /api/transactions`, the Slack nudge's share button,
|
||||
and the rule-revert restore. `POST /api/transactions/[id]/splits` needs no call
|
||||
— it already rejects anything not summing to 100.
|
||||
|
||||
The remainder always goes to the transaction's **owner**, never to "me". The
|
||||
owner's row on their own transaction is excluded from both halves of
|
||||
`getParticipantBalances` (`ts.participant_id != $1` on transactions I own; the
|
||||
converse on ones I do not), so writing it cannot create, enlarge or discharge a
|
||||
debt. A row for *me* on someone else's transaction is a real obligation — never
|
||||
synthesise one. This is what made the 7-row backfill in `22e4a1e` safe;
|
||||
balances were byte-identical across it.
|
||||
|
||||
There is no database-level constraint on the sum. Enforcing it needs a deferred
|
||||
constraint trigger, and the rule path commits its DELETE and INSERT as separate
|
||||
autocommitted statements, so the trigger would reject the intermediate state.
|
||||
|
||||
`share_percent` has a CHECK of `> 0 AND <= 100`, so a 0% row cannot be stored —
|
||||
when the others grow to cover the whole amount, the owner's row is deleted
|
||||
rather than zeroed.
|
||||
|
||||
**Un-sharing needs DELETE, not an empty POST.** The splits route rejects an
|
||||
empty array ("splits array required"), so `DELETE /api/transactions/[id]/splits`
|
||||
is the only way to clear. The order panel's "Shared 50/50" toggle was inert in
|
||||
both directions until `22e4a1e` because it posted a lone 50% row to share and
|
||||
`[]` to un-share, and the endpoint rejected both.
|
||||
|
||||
**Any split write path that deletes-and-recreates must carry `settled` across.**
|
||||
`POST /api/transactions/[id]/splits` did not, and silently converted discharged
|
||||
obligations into live debt — $37,233.28 was exposed. Fixed in `6add958`.
|
||||
|
||||
Reference in New Issue
Block a user