← All notes

Hotel Management

Rate plans are rules, not rows

Storing a price per room per night works until the first minimum-stay promotion, and then you are writing a rules engine in the pricing query.

  • Invexa Technologies
  • 2 min read

The first pricing model is a table of nightly rates. Room type, date, amount. It is obvious, it is fast, and it holds for about one commercial meeting.

Then revenue management wants: 15% off for three nights or more, non-refundable, direct bookings only, excluding public holidays, valid for stays booked before the end of the month.

Where the naive model breaks

You cannot store that as a rate row, so it becomes a conditional in the pricing function. Then another. Six months later the function is four hundred lines, nobody will touch it, and a promotion that should take ten minutes takes a sprint and a deploy.

The mistake is treating price as data when it is the output of applying rules to a stay.

Separate the rate from the qualification

A rate plan has a base price and a set of conditions that decide whether it applies at all:

rate_plans
  id, name, base_amount_minor, refundable, channel_scope

rate_conditions
  plan_id
  kind        -- min_stay | max_stay | book_before | stay_between
              -- | dow_in | channel_in | advance_days
  value       jsonb

Pricing becomes: gather every plan whose conditions all hold for this stay, then pick by policy — cheapest, or highest priority. Adding a promotion is inserting rows. No deploy, and the rules are visible to the people who own them rather than buried in a function.

Derivation beats duplication

Most plans are modifications of another: a corporate rate is BAR minus 12%, a non-refundable is BAR minus 8%. Store the relationship, not the copy. Otherwise a BAR change means updating nine plans and you will miss one.

derived_from   plan_id
adjustment     { kind: 'percent', value: -12 }

Store what you charged, not just what you would charge

The rate that applied to a booking must be frozen onto the booking at the moment of sale — amount, plan id, and the conditions that qualified it. Rules change. A guest querying their bill in March against a plan edited in February should see what they actually agreed to, and you should be able to show why.

That is the same append-only instinct as the folio and the ledger: rules describe the future, records describe what happened, and the two must never be the same table.

Next step

Have a project in mind?

A 30-minute call is usually enough to know whether we are the right team for it. If we are not, we will say so.

Start a project

Replies within one working day