fix: record subscription sale as a signed payment (close off-book hole)
Creating a priced subscription wrote only the mutable `subscriptions`
master row and appended NOTHING to the signed ledger — so the cash an
operator collected showed in the live feed, drawer, and shift Z-report
nowhere, leaving no signed trace. A booth operator could sell
subscriptions and pocket the money untraceably — the exact
operator-as-adversary path the append-only signed ledger exists to close.
Found live: 3 priced subscriptions (27,000 ALL) had zero payment events.
Selling a priced subscription now appends a signed `payment` event at
create time: amount = priceMinor x months (full multi-month prepay),
operator-chosen tender (cash->drawer / card->bank), payload
{ subscriptionSale: true, permitId, operator, months }. Folds into the
shift Z-report/drawer with no new summing logic; the feed badges it
"subscription sale" and resolves the holder name. The create response
returns the recorded { sale }; subscriptionRoutes now takes the EventLog
and ShiftService.
Not hard-gated on an open shift (a sale can happen outside the booth money
path) — it warns instead. The 3 historical off-book sales are not
back-fillable (append-only forbids forging dated events) — reconcile via
cash_movement or a Z-report note.
Verified against a copy of the live DB with the real signing modules:
signed payment appended, hash-chain still verifies, lands in shift cash
totals. Build + lint 12/12.
Wiki: subscription "Collecting the fee" deferred -> BUILT (+ the off-book
hole and why); shift sale-folds-in; threat-model worked example
("store the price != account for the sale").
Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
@@ -153,6 +153,7 @@ export const en: Catalog = {
|
||||
badgeBarrierFailed: "barrier did not open",
|
||||
badgeManualOpen: "manual open",
|
||||
badgeSubRefused: "subscription refused",
|
||||
badgeSubSale: "subscription sale",
|
||||
badgeNoTicket: "ticket not printed",
|
||||
feedSourceBooth: "booth",
|
||||
feedSourceReader: "reader",
|
||||
@@ -382,6 +383,12 @@ export const en: Catalog = {
|
||||
perMonth: "month",
|
||||
monthlyPrice: "Monthly price",
|
||||
pricePlaceholder: "e.g. 10000",
|
||||
tender: "Paid by",
|
||||
tenderCash: "Cash",
|
||||
tenderCard: "Card",
|
||||
tenderHint: "Recorded as a signed payment (feed, drawer, Z-report).",
|
||||
saleRecorded: "Sale recorded: {{amount}} {{currency}} ({{tender}}).",
|
||||
saleNoShift: "⚠ No shift was open — open one so the takings land in a Z-report.",
|
||||
edit: "Edit",
|
||||
revoke: "Revoke",
|
||||
delete: "Delete",
|
||||
|
||||
Reference in New Issue
Block a user