Pass
Purpose
A pass template — a company’s reusable offer definition: name, validity in days, the activities it covers and how many sessions of each (Pass entitlement template), bundled extras (Pass entitlement covered extra), price tiers (Pass price), refund policy, and notification thresholds. When a customer buys, a Customer pass instance is created — a snapshot of this template.
Identity & key fields
- Primary key:
id(uuid, defaultgen_random_uuid()). companyId(uuid, NOT NULL, FK →companies.company.id, on-delete restrict).name,description(text).validityDays(integer, NOT NULL) — schema comment lists typical values 30/60/90/180/365.notifySessionsRemaining(nullable integer) — threshold to send “low sessions” notification.expiryNotifyDays(nullable integer) — threshold to send “pass expiring soon” notification when no future booking exists.currency(text, NOT NULL, default'UAH').cancelRefundPolicy(enumcancel_refund_policy:NONE,FULL,PROPORTIONAL, defaultNONE) — applied for WALLET-paid passes.isActive(boolean, NOT NULL, defaulttrue).
isActive=false is a soft-disable: the pass cannot be sold to new customers but every already-issued Customer pass keeps working. cancelRefundPolicy describes what happens to the wallet balance when a WALLET-paid pass is cancelled; refunds for LIQPAY / MANUAL payments are handled out-of-band.
Invariants
companyIdON DELETE RESTRICT (enforced in tktspace-backend/libs/shared/data-access-db/src/lib/schema/passes.schema.ts).validityDays,companyId,name,currency,cancelRefundPolicy,isActiveNOT NULL (enforced in tktspace-backend/libs/shared/data-access-db/src/lib/schema/passes.schema.ts).
Business invariants:
companyIdhas a real cross-schema FK withON DELETE RESTRICTto Company — a company cannot be hard-deleted while any pass template exists. This is one of the few cross-schema references that has an FK; see the note in ADR cross-schema-references-without-fk.validityDaysis the lifespan of an activated Customer pass — fromactivatedAtuntilvalidUntil = activatedAt + validityDays, minus any paused time.- Template edits are not retroactive — a Customer pass snapshots
price,currency,priceName,paymentMethod, plus the entitlement structure at purchase time. Later edits to this template do not flow into already-purchased instances. cancelRefundPolicyapplies only to WALLET-paid passes — LIQPAY and MANUAL paths have their own refund handling outside this column.- ON DELETE RESTRICT propagates through the template-tier FK graph: deleting a pass with any active customer-pass or any entitlement-template is forbidden.
Lifecycle
No status enum on the template — isActive boolean is the soft-disable flag.
- Create: admin via
POST /api/business/passes. Typically a transaction creates the pass row plus its entitlement-templates and price tiers in one shot. - Update: admin via
PATCH /api/business/passes/{id}— name, validity, notification thresholds, refund policy, etc. Changes do not propagate to already-issued Customer pass instances. - Soft-disable:
POST /api/business/passes/{id}/toggleflipsisActive. New purchases blocked; existing customer-passes unaffected. - Delete: hard delete possible only when no active customer-passes and no entitlement-templates reference the pass (ON DELETE RESTRICT on both relationships).
Relationships
- Company (ENT-016) —
companyId→companies.company.id, on-delete restrict. N:1. - Pass entitlement template (ENT-031) — 1:N composition rows (which activities are included and how many sessions).
- Pass price (ENT-032) — 1:N pricing tiers.
- Customer pass (ENT-028) — 1:N issued / purchased instances.
API surfaces
| Surface | Exposed | Notes |
|---|---|---|
| client | yes — /companies/{companyId}/passes (PassClientDto, PassPriceClientDto, PassEntitlementClientDto) | Swagger UI |
| business | yes — /passes, /passes/{id}, /passes/{id}/toggle (PassResponseDto, CreatePassDto, UpdatePassDto, PaginatedPassResponseDto, PassesIsActiveFilter) | Swagger UI |
| super-admin | no | — |
Known gotchas / open questions
- Cancellation refund policy applies only to WALLET-paid passes per schema comment. LIQPAY / MANUAL refund handling lives outside.
- ON DELETE RESTRICT on
companyId— a company cannot be hard-deleted while pass templates exist. - Template edits are NOT retroactive. This often surprises product folks: changing the price or pulling an activity out of a template does not affect customers who already bought the pass. Their
customerPassrow holds a snapshot at purchase time. companyIdhere has a real cross-schema FK (RESTRICT) — different fromactivities.activities.companyIdand friends, which have no FK at all. The ADR over-generalised “no cross-schema FK”; reality is more nuanced.
Recommendations
Forward-looking improvements suggested while filling this doc — not currently in place.
- DB CHECK
validity_days > 0— promote the implicit rule into a constraint. - UNIQUE on
(company_id, name)— typical UX expectation that a company doesn’t have two passes with the same display name. - Audit log for template changes — for compliance and disputes (“I bought it under these terms; you changed them later”). Today there is no record of when or who changed a template.
- Document refund policies for every payment method, not just WALLET. LIQPAY/MANUAL refund handling is currently outside this column and outside the docs.
- Revise ADR cross-schema-references-without-fk — add an explicit section explaining when cross-schema FKs are used (RESTRICT for template-tier ownership, CASCADE for tight booking-side coupling) vs when they are intentionally omitted.