Data Model Diagram

This document presents the Tours data model at the field level: the key domain tables drawn directly from the CREATE TABLE / ALTER TABLE statements in src/product/booking/schema.ts, src/lib/operators.ts and src/lib/vendor-schema.ts. Tours stores "soft enums" as plain VARCHAR columns with documented allowed values (so they can be extended without a migration), money as integer cents, and arrays/objects as JSONB.

Recall the domain ↔ database mapping: a tour is a listings row, a departure is a units row, an operator is a hosts row, and a traveler is a customers row.

Tour

listingssrc/product/booking/schema.ts.

Column Type Default Meaning
id SERIAL Primary key
public_id VARCHAR(40) generated Non-sequential public id (p…), unique
slug VARCHAR(180) Unique URL slug
name VARCHAR(220) Tour name
summary / description VARCHAR/TEXT '' Short tagline + full description
category_id INT null Soft link → master_items (kind='property_category')
owner_id INT null Soft link → hosts (NULL = admin-owned)
hero_image / gallery / videos VARCHAR/JSONB ''/[] Cover image, gallery, and videos
address / city / region / country VARCHAR '' Location
latitude / longitude NUMERIC(9,6) null Map coordinates
duration_days / duration_nights INT 1 / 0 Tour length
daily_capacity INT 0 Seats per start date (0 = unlimited)
meeting_point TEXT '' Where the tour starts
difficulty VARCHAR(20) '' easy · moderate · challenging
min_age INT 0 Minimum age (0 = none)
group_size_min / group_size_max INT 1 / 0 Party bounds (max 0 = default departure capacity)
itinerary / included / excluded JSONB [] Day-by-day plan; what's included / excluded
amenities / amenity_ids JSONB [] Inline inclusions + selected amenities ids
price_adult / price_child / price_infant INT 0 Base per-person prices, cents (departures may override)
private_available / private_price BOOLEAN/INT false/0 Offer a whole-group private booking + its flat price
deposit_pct INT 30 Deposit percentage
cancellation_tier VARCHAR(20) 'moderate' flexible · moderate · strict (drives auto-refund)
instant_booking BOOLEAN true false = Request to Book (operator must accept)
status VARCHAR(20) 'draft' Lifecycle status
seo_title / meta_description / meta_keywords VARCHAR '' SEO
sort_order INT 0 Ordering
created_at / updated_at TIMESTAMPTZ NOW() Timestamps

Departure

unitssrc/product/booking/schema.ts. A departure is a dated, bookable option of a tour. Per-person prices fall back to the parent tour when left NULL.

Column Type Default Meaning
id SERIAL Primary key
listing_id INT FK → listings (ON DELETE CASCADE)
name VARCHAR(200) '' Optional label, e.g. "Morning departure"
start_date DATE null The scheduled departure date
start_time VARCHAR(5) '' HH:MM
capacity INT 0 Seats; 0 = fall back to listing.group_size_max
price_adult / price_child / price_infant INT null Per-person price, cents; NULL = fall back to the tour
private_price INT null Flat private-group price override (cents)
currency VARCHAR(3) 'EUR' ISO currency code
status VARCHAR(20) 'scheduled' scheduled · cancelled
is_active BOOLEAN true Active flag
sort_order INT 0 Ordering

Companion pricing/availability tables: seasons + season_dates define seasons; unit_rates (unique on (unit_id, season_id)) holds price_per_night per season; unit_blocks and ical_feeds manage blocked dates and external-calendar imports.

Booking

bookingssrc/product/booking/schema.ts. A lightly polymorphic reservation: product_type + product_id identify what was booked ('tour' + a listings.id), and departure_id the specific departure (NULL for a private on-request booking).

Column Type Default Meaning
id SERIAL Primary key
reference VARCHAR(30) Human booking reference (unique)
public_id VARCHAR(40) generated Non-sequential public id (b…), unique
product_type VARCHAR(20) class · tour · accommodation
product_id INT Soft link → the booked tour (listings.id)
departure_id INT null Soft link → the booked departure (units.id)
booking_mode VARCHAR(20) 'per_person' per_person · private
session_id INT null Legacy class-session id (class product)
tour_date / end_date DATE null Departure / requested date and end date
start_time VARCHAR(5) '' HH:MM
nights INT 0 Trip length in nights (multi-day tours)
customer_id INT null FK → customers (ON DELETE SET NULL)
guest_name / guest_email / guest_phone / guest_country VARCHAR '' Lead traveler contact
num_adults / num_children / num_infants INT 1/0/0 Party size
guests_detail / price_breakdown JSONB [] Per-traveler details; itemised price lines
subtotal / discount / fees / tax / deposit / total INT 0 Money breakdown (cents)
currency VARCHAR(3) 'EUR' ISO currency code
coupon_id / coupon_code INT/VARCHAR(60) null/'' Applied coupon, if any
referral_discount / influencer_id INT 0/null Influencer referral discount (cents) + attribution
status VARCHAR(20) 'pending' pending·confirmed·cancelled·completed·no_show
payment_status VARCHAR(20) 'unpaid' unpaid·deposit_paid·paid·refunded
host_status VARCHAR(20) 'none' Operator acceptance: none·requested·accepted·declined
contact_revealed BOOLEAN false Traveler PII revealed to the operator
provider / provider_ref VARCHAR '' Payment gateway + reference
special_requests / dietary_notes TEXT '' Free-text traveler notes
source VARCHAR(20) 'website' website · admin · ical
created_at / updated_at / cancelled_at TIMESTAMPTZ NOW() Timestamps

Companion: booking_payments records each deposit / balance / refund movement; booking_extras snapshots chosen upsells; booking_notes / booking_emails hold the internal trail.

Operator Earnings

host_earningssrc/lib/operators.ts. One row per settled booking on an operator's tour. net = gross − commission − fees (all cents).

Column Type Default Meaning
id SERIAL Primary key
booking_id INT The settled booking (unique)
host_id INT FK → hosts (ON DELETE CASCADE)
gross_cents INT 0 Gross amount (cents)
commission_cents INT 0 Platform commission (cents)
fee_cents INT 0 Platform fees (cents)
net_cents INT 0 Payable to the operator (cents)
currency VARCHAR(3) 'EUR' ISO currency code
rate NUMERIC(5,2) 0 Snapshot of the commission % applied
status VARCHAR(20) 'pending' pending · approved · paid · rejected
payout_request_id INT null Attached host_payout_requests id when batched

The effective commission % applied is resolved by getEffectiveCommission(): host-specific commission_override → active subscription plan's override → the platform_settings.default_commission_percent. Withdrawals flow through host_payout_requests (amount_cents, paypal_email, status of pending/approved/paid/rejected) and are recorded in host_payment_history.

Operator (Tour Owner)

hostssrc/lib/operators.ts. An operator is a customer; this row holds the seller-only fields (1:1 via customer_id UNIQUE).

Column Type Default Meaning
id SERIAL Primary key
customer_id INT FK → customers (ON DELETE CASCADE), UNIQUE
business_name VARCHAR(200) '' Public operator/business name
slug VARCHAR(220) Unique public slug
bio / avatar / cover_image TEXT/VARCHAR '' Public profile
paypal_email VARCHAR(200) '' Payout email
payout_method VARCHAR(20) 'paypal' paypal · bank · stripe
commission_override NUMERIC(5,2) null Per-operator commission % (NULL = platform default)
status VARCHAR(20) 'active' pending · active · suspended
is_verified BOOLEAN false Verified flag
verification_status VARCHAR(20) 'unverified' unverified · pending · verified · rejected
min_payout_cents INT 5000 Minimum payout threshold (cents)

Additional KYC / legal / banking columns (legal_name, tax_id, bank_iban, ID metadata, registered address, agreement timestamps) are added incrementally by ALTER TABLE. Uploaded KYC files live in host_documents.

Customer (Traveler / Operator account)

customerssrc/lib/vendor-schema.ts. One row per portal account; account_type distinguishes a traveler from an operator (the operator profile lives in hosts).

Column Type Default Meaning
id SERIAL Primary key
slug VARCHAR(255) Unique public slug
name VARCHAR(255) Display name
email VARCHAR(255) '' Email address
email_verified / phone_verified BOOLEAN false Verification flags
phone / phone_code VARCHAR '' Phone number
password_hash VARCHAR(255) '' bcrypt password hash
otp_code / otp_expires_at / otp_purpose VARCHAR/TIMESTAMPTZ ''/null bcrypt-hashed OTP + context
account_type VARCHAR(20) role tag traveler (buyer) · operator (seller)
preferred_language / preferred_currency VARCHAR '' Travel preferences
interests / dietary_notes / trip_type TEXT/VARCHAR '' Traveler profile
id_type / id_number / verification_status VARCHAR ''/'unverified' ID verification
notification_prefs JSONB {} Notification preferences
is_blacklisted / is_active BOOLEAN false/true Block / active flags
created_at / updated_at TIMESTAMPTZ NOW() Timestamps

Coupon

couponssrc/product/booking/schema.ts (booking discounts).

Column Type Default Meaning
id SERIAL Primary key
code VARCHAR(60) Unique coupon code
description VARCHAR(200) '' Internal note
discount_type VARCHAR(10) 'percent' percent or fixed
discount_value INT 0 Percentage or cents
applies_to VARCHAR(20) 'all' all · class · tour · accommodation · specific
target_ids JSONB [] Scope target ids
min_amount INT 0 Minimum order to qualify (cents)
max_discount INT 0 Cap on the discount (cents)
max_redemptions INT 0 Global redemption limit (0 = unlimited)
per_customer_limit INT 0 Per-customer limit
times_redeemed INT 0 Times used
combinable BOOLEAN false May stack with other discounts
valid_from / valid_until DATE null Validity window
is_active BOOLEAN true Active flag

A second coupons variant (discount_type PERCENT, applies_to ALL/PRODUCTS/CATEGORIES, min_total_cents, …) is provisioned for the storefront in src/lib/vendor-schema.ts. Whichever ensure*Schema() runs first creates the table.

Tour Review

listing_reviewssrc/product/booking/schema.ts. Traveler testimonials for the tour detail page (author is free text, not a customer FK).

Column Type Default Meaning
id SERIAL Primary key
listing_id INT FK → listings (ON DELETE CASCADE)
author_name / author_location VARCHAR '' Reviewer name + location
avatar VARCHAR(600) '' Reviewer avatar
rating NUMERIC(2,1) 5 1.0–5.0
comment TEXT '' Review text
photos / topics JSONB [] Traveler photos; topic tags
review_date DATE null Displayed review date
is_published BOOLEAN true Visible on the site
sort_order INT 0 Ordering

Soft Enums

Enumerations are stored as VARCHAR with documented allowed values:

Field Table Allowed values
status listings draft · published · … (lifecycle)
difficulty listings easy · moderate · challenging
cancellation_tier listings flexible · moderate · strict
status units scheduled · cancelled
booking_mode bookings per_person · private
status bookings pending · confirmed · cancelled · completed · no_show
payment_status bookings unpaid · deposit_paid · paid · refunded (partially_refunded)
host_status bookings none · requested · accepted · declined
product_type bookings class · tour · accommodation
source bookings website · admin · ical
kind booking_payments deposit · balance · refund
status booking_payments pending · paid · failed · refunded
status hosts pending · active · suspended
verification_status hosts unverified · pending · verified · rejected
payout_method hosts paypal · bank · stripe
status host_earnings pending · approved · paid · rejected
status host_payout_requests pending · approved · paid · rejected
status host_subscriptions active · past_due · cancelled · expired
monetization_mode platform_settings commission · subscription · hybrid
discount_type coupons percent · fixed
applies_to coupons all · class · tour · accommodation · specific
price_type extras flat · per_person · per_night · per_person_night
sender messages guest · host

These values are read from the actual CREATE TABLE / ALTER TABLE comments in the schema source; because they are plain strings, admins and developers can extend them without altering the column type.


© CreativeCape Solutions · creative-cape.com · support@creative-cape.com