# hospitality.room-rate
Prices a hotel stay night by night from a rate table the caller supplies, then
takes off the length-of-stay discount the stay qualifies for. The result lists
every night with its date, season and price, then the subtotal, discount and
total.
## The rate table is the hotel's
Rates are commercial decisions, not published rules, so there is no data file
here. Each row covers a range of nights (`firstNight` to `lastNight`,
inclusive) and, optionally, only some days of the week:
```
low 2026-01-01 .. 2026-03-31 Sun-Thu 90.00
low weekend 2026-01-01 .. 2026-03-31 Fri-Sat 110.00
high 2026-04-01 .. 2026-09-30 any 140.00
```
A night is named by the date it starts, so "Friday night" is the night of a
Friday, with the ISO weekday of that date (1 Monday to 7 Sunday).
**Exactly one row must match each night.** If none does, or two do, the
function fails and names the night. The obvious alternatives, "the last row
wins" or "the narrowest range wins", both quietly price a stay wrong the day
someone adds an event rate on top of a season without removing the season.
Make the rows disjoint, for example by splitting the season around the event.
## Length-of-stay discounts
`stayDiscounts` is a list of "at least n nights, x% off". The stay gets the
discount with the largest `minNights` it reaches (7 nights beats 3 nights), and
only that one. The percentage is taken off the subtotal once, rounded half-up,
so there is no per-night rounding drift.
## Edge cases
- A stay across 29 February, a month end or a year end steps through the
calendar with `dates.add-days`.
- Every row must be in the same currency as the others the stay uses.
- A malformed or impossible date anywhere in the table or the arrival is an
error.
## Not covered
Per-person pricing, occupancy supplements, children's rates, packages and taxes
(see `hospitality.tourist-tax` and `finance.tax.add-vat`) are separate steps.