mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-04 12:07:09 +00:00
fix(folio): use sum-of-added-rows in balanceMatchesAddedTxn
The old predicate (every row's signed amount equals delta) silently passed the double-submit bug because two same-amount rows each match the delta in isolation. Switching to the sum check (addedSum === delta) catches both the double-submit case and any future multi-row append whose total drifts from the balance change.
This commit is contained in:
1 parent
35fce05b65
commit
b88b6c239d
1 file changed
+14
-19
@@ -12,17 +12,13 @@ import {
|
|||||||
whenRoute,
|
whenRoute,
|
||||||
} from "@sanderling/spec";
|
} from "@sanderling/spec";
|
||||||
import { defaultActions } from "@sanderling/spec/defaults";
|
import { defaultActions } from "@sanderling/spec/defaults";
|
||||||
|
import { balanceMatchesAddedSum, type LedgerRow } from "./predicates";
|
||||||
|
|
||||||
interface Account {
|
interface Account {
|
||||||
name: string;
|
name: string;
|
||||||
balance: number;
|
balance: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LedgerRow {
|
|
||||||
key: string;
|
|
||||||
signed: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parses formatCents output like "$5.00", "-$1,234.56", "+$0.50" back to integer cents.
|
// Parses formatCents output like "$5.00", "-$1,234.56", "+$0.50" back to integer cents.
|
||||||
function parseDollarCents(text: string | undefined): number {
|
function parseDollarCents(text: string | undefined): number {
|
||||||
if (!text) return 0;
|
if (!text) return 0;
|
||||||
@@ -93,22 +89,21 @@ const newAccountBalanceIsZero = always(
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Property 2: when new ledger rows appear, the ledger balance delta equals
|
// Property 2: when new ledger rows appear, the balance delta equals the SUM
|
||||||
// the sum of the new rows' signed amounts. A double-submit lands two rows
|
// of the new rows' signed amounts. Single-row and multi-row sums both must
|
||||||
// whose individual amounts cannot both equal the aggregate delta, so each
|
// match; a double-submit lands two rows whose total is twice the balance
|
||||||
// such row fires the property.
|
// change and trips the sum check.
|
||||||
const balanceMatchesAddedTxn = always(
|
const balanceMatchesAddedTxn = always(
|
||||||
now(() => route.current === "ledger").implies(
|
now(() => route.current === "ledger").implies(
|
||||||
next(() => {
|
next(() =>
|
||||||
const prev = ledgerRows.previous ?? [];
|
balanceMatchesAddedSum(
|
||||||
const curr = ledgerRows.current;
|
ledgerRows.previous ?? [],
|
||||||
const prevKeys = new Set(prev.map(r => r.key));
|
ledgerRows.current,
|
||||||
const added = curr.filter(r => !prevKeys.has(r.key));
|
ledgerBalance.previous ?? 0,
|
||||||
if (added.length === 0) return true;
|
ledgerBalance.current,
|
||||||
const delta = ledgerBalance.current - (ledgerBalance.previous ?? 0);
|
),
|
||||||
return added.every(r => r.signed === delta);
|
),
|
||||||
})
|
),
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const DEMO_EMAIL = "[email protected]";
|
const DEMO_EMAIL = "[email protected]";
|
||||||
|
|||||||
Reference in new issue
Block a user