Skip to content

[16.0][ADD] pms: price a room type by its occupancy - #464

Open
jesusVMayor wants to merge 3 commits into
OCA:16.0from
commitsun:16.0-add-occupancy-pricing
Open

jesusVMayor wants to merge 3 commits into
OCA:16.0from
commitsun:16.0-add-occupancy-pricing

Conversation

@jesusVMayor

@jesusVMayor jesusVMayor commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

Depends on #463 (children capacity in rooms), which this builds upon. Review
that one first; only the last commit belongs here.

A pricelist gave a room type one price per night, whoever stayed in it. Selling
a double for less when a single guest takes it, or charging for a third one,
could only be done by hand on every reservation.

What this does

Room types get two things: the occupancy their rooms guarantee (max_occupancy,
the smallest capacity among them, as any of them can be the one assigned) and
the occupancy their price is given for (default_occupancy, which defaults to
the former and follows it down when a smaller room joins the type).

A pricelist then carries, per room type, how much each guest above or below that
default changes the price, as a percentage or as a fixed amount:

default_occupancy = 2, price = 100

1 adult   ->  decrease 25%     ->   75
2 adults  ->  the price as is  ->  100
3 adults  ->  increase 10      ->  110
4 adults  ->  increase 10 x 2  ->  120

The modifier is linear, applying once per guest of difference, which is what
channel managers derive from a default occupancy as well.

It stays out of the way

The modifiers do not live in product.pricelist.item, so neither the rule
resolution nor its ordering change: an item is still one price per pricelist,
room type and day. The price is derived afterwards, in a single place.

A price asked for without an occupancy, or a room type with no modifiers
configured, is the plain one of the pricelist. Everything that prices a room
type without guests in hand keeps working untouched.

Where it is wired

product.pricelist.item._compute_consumption_price() ends in
_apply_occupancy_price(), which hands over to pms.room.type. Reservations
pass the adults staying through pms.reservation.line._get_price_kwargs(), and
the booking engine lets you pick them per room type so the price shown is the
one you will pay.

Changing the adults leaves the prices of the nights as they are, the same as
changing the pricelist or the room type does: the form flags them as outdated,
and updating them prices the nights for the adults staying. Nights added
afterwards are priced for them already. A price is never rewritten behind the
back of whoever wrote it, such as a channel manager writing what an OTA sold.
The birthdate of the guests checking in never takes part in it: nobody reprices
at the desk because a guest had a birthday.

Rooms declared how many people fit, with no way to say how many of those
places are for children only, such as child bunk beds. Children were
meant to be counted through the reservation children_occupying, but
nothing kept that field in sync with children and it was left empty, so
children did not count towards the room capacity at all.

Rooms get children_capacity now. capacity keeps its meaning, the places
where anyone can sleep, and children take those first, overflowing into
the children places only once they are full. Adults never occupy them,
so a room with free children places can still be full for adults.

Whether children take a place at all is up to the property, through the
pms.children_occupy_capacity system parameter, and off unless it says
otherwise: counting them refuses reservations that properties have been
taking all along, so it cannot be something an upgrade decides for them.

children_occupying is dropped. A reservation could declare an occupying
child without counting it in children, so the migration folds it into
children before the column goes away.

Assisted-by: Claude Opus 5
The INE extra bed count subtracted the children occupying the room from
the extra beds sold for that date. Nothing ever filled children_occupying
in, so the subtraction always took zero, and the field is gone now.

Assisted-by: Claude Opus 5
A pricelist gave a room type one price per night, whoever stayed in it.
Selling a double for less when a single guest takes it, or charging for
a third one, could only be done by hand on every reservation.

Room types get the occupancy their rooms guarantee (max_occupancy, the
smallest capacity among them, as any of them can be the one assigned)
and the occupancy their price is given for (default_occupancy). A
pricelist then carries, per room type, how much each guest above or
below that default changes the price, as a percentage or as an amount.

The modifier is linear, applying once per guest of difference, which is
what channel managers derive from a default occupancy as well. A price
asked for without an occupancy, or a room type with no modifiers, is the
plain one of the pricelist, so this is transparent to everything pricing
a room type without guests in hand.

Changing the adults leaves the prices of the nights as they are, the
same as changing the pricelist or the room type does: the form flags
them as outdated, and updating them prices the nights for the adults
staying. Nights added afterwards are priced for them already. A price
is never rewritten behind the back of whoever wrote it, such as a
channel manager writing what an OTA sold. The birthdate of the guests
checking in never takes part in it: nobody reprices at the desk because
a guest had a birthday.
@jesusVMayor
jesusVMayor force-pushed the 16.0-add-occupancy-pricing branch from f683146 to 7b3773f Compare October 21, 2026 10:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mod:pms_l10n_es Module pms_l10n_es mod:pms Module pms series:16.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants