feat(drawer): operator records cash movements, admin reviews after (own /drawer route)
Rework drawer cash movements from synchronous admin-authorization-at-creation
(operator typed an admin's password inline for every receipt/disbursement) to
operator-records-freely -> admin-reviews-after.
- New `drawer` resource: drawer:create (operator records; admin-revocable per
role) + drawer:review (admin authorizes/denies). Migration 0018 grants the
default operator role drawer:create; admin gets all in code.
- New signed `cash_review` ledger event { refId, decision, reviewedBy, note? }.
A DENIAL is a FLAG, not a reversal: it never appends reversing cash and never
touches the drawer balance (the correction is settled outside the app). This
is what keeps a late review from leaking into the next operator's inherited
drawer — a denial that lands after the reviewed shift closed moves no cash.
Regression test: op1 disburses -> closes -> op2 inherits -> admin denies ->
op2 drawer unchanged.
- Move the feature OFF the polluted /shifts route to a top-level /drawer
(operator: record + own; admin: review queue + all). routes/drawer.ts lifted
from routes/shift.ts (retired the authorizer-password gate; kept shift:cash
for its other job = admin-sees-all-shifts). New DrawerManager.tsx.
Display fixes bundled:
- Render cash_review in the event-detail modal (decision / reviewed-by / note /
movement ref) — previously showed nothing.
- Relabel the shift drawer figures for clarity: Daily takings / Receipts /
Disbursements (was Cash payments / Cash added / Cash removed).
- Hide the Card figure everywhere when CARD_PAYMENTS_ENABLED is false (no POS
on-site), matching the card-tender gate.
shared/db/server/web all typecheck; 225 server tests pass (incl. the drawer
review + cross-shift-leak regression); web build + i18n parity green. Verified
end-to-end via Playwright. Recorded in wiki/concepts/shift.md.
Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
+20
@@ -2088,3 +2088,23 @@ modal shows Total + "Pay + open barrier" with NO tender/cash/card row. Re-enable
|
||||
a bank-certified P2PE terminal is provisioned (PCI scope stays out of the app — the terminal captures
|
||||
card data, not the app). New page concepts/card-payments.md documents current state + future-POS
|
||||
device requirements + re-enable path; linked from index, parking-session, open-questions #3.
|
||||
|
||||
## [2026-07-01] feat | Drawer redesign — operator records freely, admin reviews after; moved to /drawer
|
||||
|
||||
Reworked drawer cash movements from synchronous admin-authorization-at-creation (operator typed an
|
||||
admin's password inline at the booth for every receipt/disbursement) to operator-records → admin-
|
||||
reviews-after. An operator with drawer:create RECORDS a cash_in/cash_out freely; it counts in the
|
||||
drawer immediately. An admin with drawer:review AUTHORIZES/DENIES it after via a new signed cash_review
|
||||
event { refId, decision, reviewedBy, note? }. THE LOAD-BEARING CHOICE (settled with user): a denial is
|
||||
a FLAG, not a reversal — it never appends reversing cash and never touches the drawer balance (the
|
||||
correction is the admin's/accountant's job outside the app; we are NOT building accounting). This kills
|
||||
the cross-shift-leak problem the user raised: a denial that lands after the reviewed shift closed can't
|
||||
pollute the next operator's inherited drawer, because it moves no cash. New `drawer` resource +
|
||||
drawer:create (per-role revocable) / drawer:review permissions; migration 0018 grants operator
|
||||
drawer:create. Feature moved OFF the polluted /shifts route to a top-level /drawer (operator: record +
|
||||
own; admin: review queue + all). New routes/drawer.ts (lifted from routes/shift.ts, retired the
|
||||
authorizer-password gate; kept shift:cash for its other job = admin-sees-all-shifts scope),
|
||||
DrawerManager.tsx, drawer.* i18n (sq+en). Verified: full monorepo build/lint/test green (225 server
|
||||
tests incl. the op1-denied → op2-drawer-unchanged regression); Playwright end-to-end on /drawer
|
||||
(record disbursement → pending → authorize → status flips, ledger shows cash_out + cash_review with no
|
||||
authorizedBy). Recorded in shift.md "Drawer review".
|
||||
|
||||
Reference in New Issue
Block a user