Transactions and the bank register become append-only with a void
(reversal) action — never edited or hard-deleted. This is the API half of
phase 5; the capture/void web UI is the remaining piece.
Schema:
- Transaction and BankTransaction gain voidedAt + voidedById. A non-null
voidedAt reverses the row. Pushed to dev.
Correctness (the high-stakes part):
- Every aggregate excludes voided rows: billing movements totals, the raw
balances SQL, stats (groupBy + the sides/crossLine raw subqueries +
first/last), facets (types/sources/years); the statement's running
balance freezes on a voided row and its per-currency/per-domain/per-type
summaries skip them; customers.detail and property owner-ledger groupBy;
and every bank total (totalsFor, stats counts/bounds, facets + summary
raw SQL). List views still return voided rows with a `voided` flag so
the UI can strike them through.
- Bank's legacy zero-amount "void" cheques are unchanged and distinct from
app voids (voidedAt).
API:
- POST /billing + POST /billing/:id/void (ledger:create / ledger:void);
POST /bank + POST /bank/:id/void (bank:create / bank:void). Create needs
STAFF+, void needs MANAGER+. Double-void -> 400, unknown id -> 404,
bad date -> 400. Mutations audited. DTOs added.
Verified against dev end-to-end: a -500 MXN charge moved a customer
balance 31082.08 -> 30582.08, and voiding it returned it to 31082.08 to
the cent; a +1234.56 bank ingreso moved net 899375.77 -> 900610.33 and
voiding returned it to 899375.77. VIEWER create/void both 403,
double-void 400. API compiles clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the office's own bank-register browser over the migrated SCOTHIA
data (22,354 bank_transactions), the last self-contained feature module.
API (apps/api/src/bank):
- GET /bank register browser: search over concepto/reference/notes/
amountInWords; direction (income|expense|void), cleared and
date-range filters; 5 sorts; income/expense/net totals for
the whole filtered set, not just the page
- GET /bank/stats headline income/expense/net + counts, date span, pending
- GET /bank/facets year list for the period filter
- GET /bank/summary year and month rollups with a running net-movement figure
Web (/banco): "Movimientos" register + "Resumen por periodo" with year->month
drill-down; added to the AppShell nav as "Chequera".
Deliberately kept OUT of /estado-cuenta: this is the office's own money, not
customer balances, and the two are never summed or shown together.
No category/ramo dimension, and the deferred concept->ramo classifier is
dropped as won't-build: concepto is a payee name (0 of 22,354 match a
category) and TABLA RAMODOS is an expense chart of accounts + owner names,
not the insurance/servicios/fideicomiso split it was assumed to be, so a
classifier would invent data. Single currency (MXN); the "acumulado" is net
movement since the register opened (no opening balance in the source), not a
bank balance. Verified end-to-end in the browser; totals reconcile.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>