Skip to content

Pricing engine

One pure function (packages/shared/src/pricing.ts) prices every stay. The public quote box, the booking dialog’s auto-price and (later) owner statements all call the same code — totals can never diverge between what the guest saw and what the backoffice books.

nightly rate = season price ?? base rate (global season calendar; a season's
price conditions target all units,
one category or a manual unit list)
→ holiday rules adjust the resolved rate around moving holidays
(±% on it, an absolute amount on top, or a fixed price)
accommodation = Σ nightly rate
− long-stay discount (≥28 nights: monthly %, else ≥7 nights: weekly %)
+ cleaning fee (once per stay)
+ Tourism Dirham (fee per bedroom per night, capped at 30 nights;
studios count as 1 bedroom)
+ VAT (default 5% on accommodation − discount + cleaning;
the Tourism Dirham is a government levy outside the VAT base*)
= total

* Assumption documented for tax-advisor review; both the VAT rate and the Dirham fee/cap are tenant-configurable (tenants.vat_pct, tourism_fee_per_bedroom_night, tourism_fee_max_nights).

LineAmount
7 nights × AED 425AED 2,975
Weekly discount (−6%)−AED 178.50
Cleaning feeAED 350
Tourism Dirham (2 BR × 7 nights × AED 10)AED 140
VAT (5%)AED 157.33
TotalAED 3,443.83
  • Seasons are global recurring periods (month/day, wrap over New Year allowed). Each season carries price conditions — the same price for everyone, one per category (lifestyle/luxury), or a hand-picked unit list; the most specific matching condition wins (units > category > all).
  • Holiday rules are an overlay, not date ranges: they follow the synced dates of a moving holiday (Ramadan, Eid, Easter, CNY …) plus days before/after, and adjust the already-resolved nightly price per condition — ±%, an absolute amount, or a fixed price.
  • Minimum stay per unit is enforced in the quote endpoint (422 with the required nights); seasons and holiday rules can raise it for the nights they touch.
  • Manual override: the backoffice may replace the auto total (e.g. OTA gross amounts); the override is stored on the booking, the engine’s breakdown remains reproducible.
  • All amounts are stored as integer minor units (fils); formatting happens at the edge.

Per-day overrides via drag-selection in the price-calendar mode, min-stay per date, and channel-specific rate adjustments.