fix(modules): Car Wash depends on parking only — the discount engine is core, not the validation module
Build & push images / images (push) Successful in 2m51s

A site entitled to parking,carwash had the wash silently dropped as dependency-broken.
The validation program routes (compose/read) leave the validation module gate; the
merchant scan routes (mine/lookup/apply/void) stay behind it.

Claude-Session: https://claude.ai/code/session_01FWncR69HgGPuei1dLrW3cU
This commit is contained in:
2026-09-06 10:40:01 +02:00
parent acde3bba5b
commit 2aa1045ddc
5 changed files with 55 additions and 20 deletions
+9 -6
View File
@@ -82,12 +82,15 @@ function validateProgram(b: ProgramBody): string | null {
}
export async function validationRoutes(app: FastifyInstance, db: Db, eventLog: EventLog): Promise<void> {
// Every route is behind the venue-module gate FIRST (403 module_disabled when the
// site has validation off — see ../modules.ts), then the usual permission guard.
const moduleOn = requireModule(db, "validation");
const siteRead = [moduleOn, requirePermission("site:read")];
const siteWrite = [moduleOn, requirePermission("site:update")];
const applyGuard = [moduleOn, requirePermission("validation:create")];
// The PROGRAM routes (compose / read discount programs) are CORE: the discount engine
// serves every module that grants a parking discount (Car Wash's "carwash" program
// rides it), so they are never behind the validation module gate — plain site:read /
// site:update. The MERCHANT routes (mine / lookup / apply / void — the scan screen)
// are the validation module itself: module gate FIRST (403 module_disabled when the
// site has validation off — see ../modules.ts), then the permission.
const siteRead = requirePermission("site:read");
const siteWrite = requirePermission("site:update");
const applyGuard = [requireModule(db, "validation"), requirePermission("validation:create")];
const liveProgram = (id: string) =>
db