Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pms_l10n_es/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

{
"name": "PMS Spanish Adaptation",
"version": "16.0.2.3.0",
"version": "16.0.2.4.0",
"author": "Commit [Sun], Odoo Community Association (OCA)",
"license": "AGPL-3",
"application": True,
Expand Down
1 change: 1 addition & 0 deletions pms_l10n_es/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
from . import pms_checkin_partner
from . import res_partner_id_number
from . import pms_ses_communication
from . import pms_folio
from . import pms_reservation
from . import pms_tourism_classification
56 changes: 37 additions & 19 deletions pms_l10n_es/models/pms_checkin_partner.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
CODE_SPAIN = "ES"
CODE_NIF = "D"
CODE_NIE = "N"
AGE_OF_MAJORITY = 18

_logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -66,6 +67,17 @@ def _compute_partner_document_data(self):
record.support_number = last_update_document[0].support_number
return res

def _is_minor(self):
"""Whether the guest is under the age of majority.

A guest with no birthdate yet is not considered a minor: their age is
unknown, not known to be under age.
"""
self.ensure_one()
return bool(self.birthdate_date) and self.birthdate_date > (
fields.Date.today() - relativedelta(years=AGE_OF_MAJORITY)
)

def _checkin_mandatory_fields(self):
self.ensure_one()
mandatory_fields = super()._checkin_mandatory_fields()
Expand All @@ -81,25 +93,27 @@ def _checkin_mandatory_fields(self):
]
)

if self.birthdate_date:
# Checkins with age greater than 14 must have an identity document
if self.birthdate_date <= fields.Date.today() - relativedelta(years=14):
mandatory_fields.extend(
[
"document_number",
"document_type",
"document_country_id",
]
)
# Checkins with age lower than 18 must have a relationship
# with another checkin partner
if self.birthdate_date > fields.Date.today() - relativedelta(years=18):
mandatory_fields.extend(
[
"ses_partners_relationship",
"ses_related_checkin_partner_id",
]
)
# Checkins with age greater than 14 must have an identity document
if self.birthdate_date and self.birthdate_date <= (
fields.Date.today() - relativedelta(years=14)
):
mandatory_fields.extend(
[
"document_number",
"document_type",
"document_country_id",
]
)

# Minors must have a relationship with another checkin partner, unless
# the folio declares that they travel unaccompanied
if self._is_minor() and not self.folio_id.ses_unaccompanied_minors:
mandatory_fields.extend(
[
"ses_partners_relationship",
"ses_related_checkin_partner_id",
]
)

if self.country_id and self.country_id.code == CODE_SPAIN:
mandatory_fields.extend(
Expand Down Expand Up @@ -137,6 +151,10 @@ def _checkin_manual_fields(self, country=False):
)
return manual_fields

def action_on_board(self):
self.folio_id._check_ses_unaccompanied_minors()
return super().action_on_board()

def get_document_vals(self):
vals = super().get_document_vals()
vals["support_number"] = self.support_number
Expand Down
87 changes: 87 additions & 0 deletions pms_l10n_es/models/pms_folio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError


class PmsFolio(models.Model):
_inherit = "pms.folio"

ses_all_guests_minors = fields.Boolean(
string="All guests are minors",
help="Every guest of the folio whose birthdate is known is under the "
"age of majority.",
compute="_compute_ses_all_guests_minors",
)
ses_unaccompanied_minors = fields.Boolean(
string="Unaccompanied minors",
help="The minors travel on their own with the authorization of their "
"legal guardian. No relationship with an accompanying guest is "
"required on their checkin data.",
tracking=True,
)
ses_minors_authorization = fields.Binary(
string="Guardian authorization",
help="Authorization signed by the legal guardian of the minors.",
)
ses_minors_authorization_filename = fields.Char(
string="Guardian authorization filename",
)

def _ses_staying_checkin_partners(self):
"""Guests of the folio that are actually staying.

The guests are taken from the whole folio and not from a single
reservation, because the party is not split by room: the guardians may
be booked in one reservation and the minors in another one. Cancelled
and out of service reservations are left out, since their guests are not
part of the party the declaration talks about.
"""
self.ensure_one()
return self.checkin_partner_ids.filtered(
lambda checkin_partner: checkin_partner.reservation_id.state != "cancel"
and checkin_partner.reservation_id.reservation_type != "out"
)

@api.depends(
"checkin_partner_ids.birthdate_date",
"reservation_ids.state",
"reservation_ids.reservation_type",
)
def _compute_ses_all_guests_minors(self):
for record in self:
# Guests with no birthdate yet are left out instead of blocking:
# otherwise an unfilled guest slot anywhere in the folio would hide
# the declaration, and the checkin data gets filled in any order.
guests_with_birthdate = record._ses_staying_checkin_partners().filtered(
"birthdate_date"
)
record.ses_all_guests_minors = bool(guests_with_birthdate) and all(
checkin_partner._is_minor() for checkin_partner in guests_with_birthdate
)

def _check_ses_unaccompanied_minors(self):
"""Refuse an unaccompanied minors declaration that the guests contradict.

Every guest of the folio is taken into account, no matter whether they
already boarded, so the check does not depend on the order in which the
guests are checked in. A guest with no birthdate yet is not known to be
of age, so incomplete checkin data never raises here.
"""
for record in self:
if not record.ses_unaccompanied_minors:
continue
guests_of_age = record._ses_staying_checkin_partners().filtered(
lambda checkin_partner: checkin_partner.birthdate_date
and not checkin_partner._is_minor()
)
if guests_of_age:
raise ValidationError(
_(
"%(guests)s is of age, so the unaccompanied minors "
"declaration of folio %(folio)s does not hold. Uncheck "
"the declaration or review the birthdates."
)
% {
"guests": ", ".join(guests_of_age.mapped("name")),
"folio": record.name,
}
)
19 changes: 19 additions & 0 deletions pms_l10n_es/models/pms_reservation.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,25 @@ class PmsReservation(models.Model):
],
compute="_compute_ses_status_traveller_report",
)
# The unaccompanied minors declaration belongs to the folio, because the
# party is not split by room: the guardians may be booked in one reservation
# and the minors in another one. It is exposed here because the reservation
# is where it gets managed.
ses_all_guests_minors = fields.Boolean(
related="folio_id.ses_all_guests_minors",
)
ses_unaccompanied_minors = fields.Boolean(
related="folio_id.ses_unaccompanied_minors",
readonly=False,
)
ses_minors_authorization = fields.Binary(
related="folio_id.ses_minors_authorization",
readonly=False,
)
ses_minors_authorization_filename = fields.Char(
related="folio_id.ses_minors_authorization_filename",
readonly=False,
)

@api.depends("pms_property_id", "preferred_room_id")
def _compute_is_ses(self):
Expand Down
1 change: 1 addition & 0 deletions pms_l10n_es/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from . import test_wizard_ine
from . import test_res_partner
from . import test_pms_ses_communication
from . import test_pms_checkin_partner
Loading
Loading