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:
@@ -157,6 +157,7 @@ export const sq = {
|
||||
badgeBarrierFailed: "barriera nuk u hap",
|
||||
badgeManualOpen: "hapje manuale",
|
||||
badgeSubRefused: "abonimi u refuzua",
|
||||
badgeSubSale: "shitje abonimi",
|
||||
badgeNoTicket: "bileta nuk u printua",
|
||||
feedSourceBooth: "kabinë",
|
||||
feedSourceReader: "lexues",
|
||||
@@ -393,6 +394,12 @@ export const sq = {
|
||||
perMonth: "muaj",
|
||||
monthlyPrice: "Çmimi mujor",
|
||||
pricePlaceholder: "p.sh. 10000",
|
||||
tender: "Paguar me",
|
||||
tenderCash: "Para në dorë",
|
||||
tenderCard: "Kartë",
|
||||
tenderHint: "Regjistrohet si pagesë e nënshkruar (aktiviteti, arka, raporti i turnit).",
|
||||
saleRecorded: "Shitja u regjistrua: {{amount}} {{currency}} ({{tender}}).",
|
||||
saleNoShift: "⚠ Asnjë turn i hapur — hapni një që arkëtimi të hyjë në një raport turni.",
|
||||
edit: "Ndrysho",
|
||||
revoke: "Anulo",
|
||||
delete: "Fshij",
|
||||
@@ -531,7 +538,7 @@ export const sq = {
|
||||
cashTaken: "Para të marra:",
|
||||
cashAdded: "Para të shtuara:",
|
||||
cashRemoved: "Para të hequra:",
|
||||
expectedDrawer: "Arka e pritshme:",
|
||||
expectedDrawer: "Gjëndje Arke:",
|
||||
printedToReceipt: "Printuar te printeri i kabinës.",
|
||||
recordedNoPrinter: "Regjistruar (pa printer për të printuar).",
|
||||
// Header shift control + the booth shift gate.
|
||||
@@ -559,7 +566,7 @@ export const sq = {
|
||||
payments: "Pagesa",
|
||||
cash: "Para",
|
||||
card: "Kartë",
|
||||
expectedDrawer: "Arka e pritshme",
|
||||
expectedDrawer: "Gjëndje arke",
|
||||
// Filter (admin only).
|
||||
filterFrom: "Nga",
|
||||
filterTo: "Deri",
|
||||
|
||||
Reference in New Issue
Block a user