692dff5f89
In-park merchants discharge customers' parking: a merchant user scans the ticket on their device (/validate; validation:create + program↔user binding) and applies their program — comp / first-N-minutes free / amount-off (capped, typed at scan) / percent. All money stays at the booth: the quote folds live validations in a canonical order (timeCredit → percent → fixed → comp, net floors at 0, Σ lines ≡ gross − net), the payment records gross/discount and CONSUMES the validation ids (an overstay's fresh period never re-applies them), the receipt prints the gross → lines → net story, and the Z/X-report carries discountTotalMinor leakage. Every apply/void is a signed, attributed ledger event (refId = append-only void); program config is /setup/site master data (Bar/Lavazh checkboxes + right-column panel, tabs when both) whose saves sign config_change. Migration 0024 + reset-db drift-guard entries; 8 route integration tests + priceSession fold suite. See wiki/concepts/validation-discounts.md for the full design record. Claude-Session: https://claude.ai/code/session_01YYkpEsLmoQPaize5ec3oUm
32 lines
1.5 KiB
SQL
32 lines
1.5 KiB
SQL
-- Merchant validation programs (2026-07-13). In-park merchants (bar / lavazh) validate a
|
|
-- customer's ticket so the BOOTH settlement discounts the fee — the merchant only
|
|
-- validates, all money and paper stay at the booth. The /setup/site checkboxes toggle the
|
|
-- WELL-KNOWN rows ("bar", "lavazh"); a future merchant is a new row, not a migration.
|
|
-- Config is plainly MUTABLE (no versioning): the applied validation is a signed ledger
|
|
-- event carrying the RESOLVED values, so reproducibility never depends on these rows.
|
|
-- See wiki/concepts/validation-discounts.md.
|
|
CREATE TABLE `validation_programs` (
|
|
`id` text PRIMARY KEY NOT NULL,
|
|
`name` text NOT NULL,
|
|
`mode` text DEFAULT 'comp' NOT NULL,
|
|
`minutes` integer,
|
|
`percent` integer,
|
|
`max_amount_minor` integer,
|
|
`max_per_day` integer,
|
|
`active` integer DEFAULT 0 NOT NULL,
|
|
`created_at` text DEFAULT (current_timestamp) NOT NULL,
|
|
`deleted_at` text,
|
|
`deleted_by` text
|
|
);
|
|
--> statement-breakpoint
|
|
-- WHICH users may apply a program: the apply guard is `validation:create` AND a binding
|
|
-- row here — a bar user can never apply the lavazh program.
|
|
CREATE TABLE `validation_program_users` (
|
|
`program_id` text NOT NULL,
|
|
`user_id` text NOT NULL,
|
|
FOREIGN KEY (`program_id`) REFERENCES `validation_programs`(`id`) ON UPDATE no action ON DELETE no action,
|
|
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
|
|
);
|
|
--> statement-breakpoint
|
|
CREATE UNIQUE INDEX `validation_program_users_program_id_user_id_unique` ON `validation_program_users` (`program_id`,`user_id`);
|