Skip to content

[WIP][16.0] Refactor board services - #467

Draft
jesusVMayor wants to merge 4 commits into
OCA:16.0from
commitsun:16.0-ref-board-service-products
Draft

jesusVMayor wants to merge 4 commits into
OCA:16.0from
commitsun:16.0-ref-board-service-products

Conversation

@jesusVMayor

Copy link
Copy Markdown
Member

What this changes

A board service (breakfast, half board…) was a model of its own,
pms.board.service: a list of products with a fixed amount for adults or
children. To be sold it had to be copied to every room type
(pms.board.service.room.type), and that copy, with its own fixed amounts, is
what reservations, services and pricelist items pointed to. Its price could not
be changed per pricelist or per date, and keeping the copies in step across room
types was tedious.

This PR turns a board service into a product: a product template flagged
with is_board_service, priced by the pricelist on each consumption date like
any other product. The four board service models are removed.

Adults and children

pms ships a Guest attribute (Adult, Child). A board service:

  • without it is a single product, consumed by every guest;
  • with both values has a variant for adults and another for children, each with
    its own price (a pricelist rule per variant, or the price_extra of the
    value);
  • with Adult only is not consumed by children.

Each guest consumes the variant for them, counted per person and per night as
the product says. Adding or removing children adds or removes their variant
without touching the adults'.

Composed board services (half board as a breakfast and a dinner service) are
left to an optional module on top of product_pack:
product.template._get_board_service_products is the hook for it.

Pricelists

  • board_service_ids: the board services the pricelist sells. It is an
    explicit list, and a new pricelist starts with Room Only.
  • default_board_service_id: the board service of its reservations unless
    another one is chosen. Empty means Room Only.
  • A pricelist that does not sell Room Only needs a default.
  • board_service_room_type_id is removed from pricelist items. Rule selection
    had ignored it since 2025, so a rule on a product already applied to every
    board service.

Room Only is a product template created by data. It never adds a service
to a reservation: it exists to be listed in pricelists and closed in
availability plans.

Availability plans

A new model, pms.availability.plan.board.rule, closes a board service on a
date range for the pricelists of the plan, with a single closed field. Rules
can overlap, and the last one written wins, so a rule that is not closed reopens
what an earlier one closes. The checkout night is not part of the stay.

Closing Room Only is how a property forces a board service on some dates.

Reservations

  • board_service_id (a product template) replaces board_service_room_id.
    It takes the default of the pricelist if the pricelist sells it for the stay,
    and once chosen by the user (even empty) it is kept.
  • The reservation manages only the board services it generates, marked with
    the guests they count (board_guest_type). Board services added by other
    means are left alone.
  • As with sale restrictions today, the board services a pricelist sells limit
    what is offered and defaulted, not what a reservation can hold.

A board service menu stays under Services, as a filtered view of the products.

Fixed on the way

  • _compute_board_service_ids returned from inside the loop, so the rest of a
    batch was skipped, and children added after the board service never got
    their services.
  • pms.reservation.create used the values of the last reservation of the
    batch for all of them, and returned only the last record.
  • The board service filter of the folio changes wizard compared an id with a
    list, so it never matched.

Breaking changes

  • pms.board.service, pms.board.service.line, pms.board.service.room.type
    and pms.board.service.room.type.line are removed, together with
    pms.room.type.board_service_room_type_ids and get_default_board_service.
  • pms.reservation.board_service_room_id becomes board_service_id, and
    pms.service.board_service_line_id becomes board_guest_type.
  • The massive changes wizard loses its board services mode: board service
    variants are priced as services.

Pending

Depends on

#463 and #464, whose commits show up in this diff until they are merged.

Testing

  • pms/tests/test_pms_board_service.py, rewritten: consumption by variants,
    adding and removing children, Room Only, prices from the pricelist and from
    price_extra, the constraints, the default of the reservation, closings,
    reopening, the checkout night and properties.
  • The tests that built board services are adapted to the new model.
  • The whole pms suite passes on a fresh database with demo data, but for the
    two tests that already fail on 16.0
    (test_pms_folio_priority_fiscal_position_property and
    test_do_payment_sets_payment_method_line), and so does pms_l10n_es.

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.
A board service was a model of its own, pms.board.service, copied for each
room type in pms.board.service.room.type with a fixed amount per line that
no pricelist could change. It becomes a product template flagged as a board
service, priced by the pricelist on each consumption date like any product.

- Adults and children consume their own variant through the Guest attribute
  that pms provides. A board service without it is a single product for
  every guest.
- Pricelists list the board services they sell and a default one. Room Only,
  created by data, is listed like the rest and never adds a service: it is
  there to be sold, or closed to force a board service.
- Availability plans close board services on date ranges.
- A reservation keeps the board services it generates, marked with the
  guests they count, and leaves alone the ones added by other means.
- board_service_room_type_id is removed from pricelist items. Rule selection
  had ignored it since 2025.

There is no migration yet: the existing board services, their copies per
room type and the board service of each reservation are not carried over.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant