packages/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.
packages/db/src/schema
enums.ts — all PostgreSQL enums (not a table)
auth.ts — user, session, account, verification (4)
profile.ts — customer_profile, staff_profile (2)
service.ts — service_category, service, staff_service (3)
schedule.ts — staff_schedule, staff_time_off, business_hour, holiday (4)
booking.ts — booking, booking_service, booking_status_log, waitlist (4)
invoice.ts — invoice, invoice_item (2)
membership.ts — spa_membership_tier, spa_membership (2)
offer.ts — offer, offer_service, offer_redemption (3)
lead.ts — lead, lead_note (2)
crm.ts — customer_tag, customer_tag_assignment, customer_note (3)
loyalty.ts — loyalty_account, loyalty_transaction (2)
notification.ts — notification, push_subscription (2)
branch.ts — branch (1)
system.ts — daily_sales_summary, monthly_gst_summary, audit_log, system_setting (4)
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.
Business rules enforced at the DB level
One offer per customer per day
One offer per customer per day
Unique constraint on
offer_redemption (customer + date).One active membership per customer
One active membership per customer
Partial unique index on
spa_membership where status = 'active'.One service type per booking
One service type per booking
A booking is salon OR spa, never mixed.
Snapshot pricing
Snapshot pricing
booking_service.price_at_booking_paise and invoice_item.unit_price_paise are frozen copies.Related pages
Conventions
Money, dates, layering, and the API response envelope
Background Jobs
The cron + QStash jobs that maintain this data
API Reference
Endpoints that read and write these tables
