Data Model
Royal Glow runs on Neon PostgreSQL 16 with Drizzle ORM (pure TypeScript, edge-native). The schema lives inpackages/db/src/schema/ as 15 files
defining 38 tables that cover auth, profiles, services, scheduling,
bookings, billing, memberships, offers, CRM, leads, loyalty, notifications,
branches, and system operations.
Every monetary value is an integer in paise (₹1 = 100 paise) — never a
float. Every timestamp is
timestamptz (stored UTC, displayed IST). Prices
are GST-inclusive at 18%.Conventions
These rules are mandatory across every table.Schema files & table groupings
The 15 schema files inpackages/db/src/schema/ map to 38 tables. The tree below
lists the actual pgTable names defined in each file.
The
auth.ts tables (user, session, account, verification) are owned
by Better Auth’s Drizzle adapter. Don’t add custom columns to them — extend
via customer_profile / staff_profile instead. Enums are defined separately
in enums.ts (not counted as a table). Totals: 15 files, 38 tables.Key enums
Defined natively in PostgreSQL viaenums.ts.
ID & number formats
H = salon (hair/beauty), S = spa. The -M suffix marks a membership
session booking.
GST (18% inclusive)
Customer-facing prices already include GST. The invoice back-calculates the base and tax from the inclusive amount.Loyalty (gems) rules
- Earn: 1 gem per ₹100 invoiced (floor), on
invoice_type = 'service'only. - No gems on
membership_purchaseormembership_sessioninvoices. - Expiry: 365 days from earn date, auto-deducted by the QStash gems-auto-expire job.
- Redemption: against specific catalogue services — not a ₹ discount.
- Cannot combine with an offer on the same booking.
Booking lifecycle
pending and go straight to confirmed. Status transitions are
recorded in booking_status_log.