[REF] pms: rename availability plans to pms.room.type.availability & .rules

This commit is contained in:
miguelpadin
2020-12-14 20:26:51 +01:00
committed by Eric Antones
parent 6080db54be
commit ebe851aa24
22 changed files with 308 additions and 392 deletions

View File

@@ -29,7 +29,7 @@ This module is an all-in-one property management system (PMS) focused on medium-
for managing every aspect of your property's daily operations.
You can manage hotel properties with multi-hotel and multi-company support, including your rooms inventory,
reservations, check-in, daily reports, board services, rate and restriction plans among other hotel functionalities.
reservations, check-in, daily reports, board services, rate and availability plans among other hotel functionalities.
.. IMPORTANT::
This is an alpha version, the data model and design can change at any time without warning.

View File

@@ -52,8 +52,8 @@
"views/account_move_views.xml",
"views/res_users_views.xml",
"views/pms_room_type_class_views.xml",
"views/pms_room_type_restriction_views.xml",
"views/pms_room_type_restriction_item_views.xml",
"views/pms_room_type_availability_views.xml",
"views/pms_room_type_availability_rule_views.xml",
"views/pms_service_views.xml",
"views/pms_service_line_views.xml",
"views/pms_shared_room_views.xml",

View File

@@ -2,14 +2,17 @@
<odoo>
<data noupdate="1">
<!-- Basic pms -->
<record id="main_pms_room_type_restriction" model="pms.room.type.restriction">
<field name="name">Restriction Plan</field>
<record id="main_pms_room_type_availability" model="pms.room.type.availability">
<field name="name">Availability Plan</field>
</record>
<record id="main_pms_property" model="pms.property">
<field name="name">My Property</field>
<field name="company_id" ref="base.main_company" />
<field name="default_pricelist_id" ref="product.list0" />
<field name="default_restriction_id" ref="main_pms_room_type_restriction" />
<field
name="default_availability_id"
ref="main_pms_room_type_availability"
/>
<field name="street">Rua Street Demo, s/n</field>
<field name="city">Commitsun city</field>
<field name="country_id" ref="base.es" />

View File

@@ -298,14 +298,17 @@
</field>
</record>
<!-- Multi pms Demo -->
<record id="demo_pms_room_type_restriction" model="pms.room.type.restriction">
<field name="name">Restriction Plan Demo</field>
<record id="demo_pms_room_type_availability" model="pms.room.type.availability">
<field name="name">Availability Plan Demo</field>
</record>
<record id="demo_pms_property" model="pms.property">
<field name="name">My pms Demo</field>
<field name="company_id" ref="base.main_company" />
<field name="default_pricelist_id" ref="product.list0" />
<field name="default_restriction_id" ref="demo_pms_room_type_restriction" />
<field
name="default_availability_id"
ref="demo_pms_room_type_availability"
/>
</record>
<!-- pms.room.type -->
<record id="demo_pms_room_type_0" model="pms.room.type">

View File

@@ -24,8 +24,7 @@ from . import product_template
from . import res_company
from . import account_payment
from . import pms_room_type_availability
from . import pms_room_type_restriction
from . import pms_room_type_restriction_item
from . import pms_room_type_availability_rule
from . import pms_reservation_line
from . import pms_checkin_partner
from . import product_pricelist

View File

@@ -41,11 +41,11 @@ class PmsProperty(models.Model):
required=True,
help="The default pricelist used in this property.",
)
default_restriction_id = fields.Many2one(
"pms.room.type.restriction",
"Restriction Plan",
default_availability_id = fields.Many2one(
"pms.room.type.availability",
"Availability Plan",
required=True,
help="The default restriction plan used in this property.",
help="The default availability plan used in this property.",
)
default_arrival_hour = fields.Char(
"Arrival Hour (GMT)", help="HH:mm Format", default="14:00"

View File

@@ -521,7 +521,9 @@ class PmsReservation(models.Model):
[("active", "=", True)]
)
return
rooms_available = self.env["pms.room.type.restriction"].rooms_available(
rooms_available = self.env[
"pms.room.type.availability"
].rooms_available(
checkin=reservation.checkin,
checkout=reservation.checkout,
room_type_id=False, # Allow chosen any available room
@@ -1101,7 +1103,7 @@ class PmsReservation(models.Model):
}
def open_reservation_wizard(self):
rooms_available = self.env["pms.room.type.restriction"].rooms_available(
rooms_available = self.env["pms.room.type.availability"].rooms_available(
checkin=self.checkin,
checkout=self.checkout,
current_lines=self.reservation_line_ids.ids,
@@ -1184,7 +1186,7 @@ class PmsReservation(models.Model):
def _autoassign(self):
self.ensure_one()
room_chosen = False
rooms_available = self.env["pms.room.type.restriction"].rooms_available(
rooms_available = self.env["pms.room.type.availability"].rooms_available(
checkin=self.checkin,
checkout=self.checkout,
room_type_id=self.room_type_id.id or False,

View File

@@ -108,7 +108,9 @@ class PmsReservationLine(models.Model):
# select room_id regardless room_type_id selected on reservation
free_room_select = True if reservation.preferred_room_id else False
# we get the rooms available for the entire stay
rooms_available = self.env["pms.room.type.restriction"].rooms_available(
rooms_available = self.env[
"pms.room.type.availability"
].rooms_available(
checkin=line.reservation_id.checkin,
checkout=line.reservation_id.checkout,
room_type_id=reservation.room_type_id.id
@@ -139,7 +141,7 @@ class PmsReservationLine(models.Model):
else:
line.room_id = rooms_available[0]
# check that the reservation cannot be allocated even by dividing it
elif not self.env["pms.room.type.restriction"].splitted_availability(
elif not self.env["pms.room.type.availability"].splitted_availability(
checkin=line.reservation_id.checkin,
checkout=line.reservation_id.checkout,
room_type_id=line.reservation_id.room_type_id.id,
@@ -233,7 +235,7 @@ class PmsReservationLine(models.Model):
def _compute_impact_quota(self):
for line in self:
reservation = line.reservation_id
line.impacts_quota = self.env["pms.room.type.restriction"].update_quota(
line.impacts_quota = self.env["pms.room.type.availability"].update_quota(
pricelist_id=reservation.pricelist_id,
room_type_id=reservation.room_type_id,
date=line.date,

View File

@@ -1,92 +1,193 @@
# Copyright 2017 Alexandre Díaz
# Copyright 2017 Dario Lodeiros
# Copyright 2018 Pablo Quesada
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from datetime import timedelta
import datetime
from odoo import api, fields, models
class PmsRoomTypeAvailability(models.Model):
"""The room type availability is used as a daily availability plan for room types
and therefore is related only with one property."""
_name = "pms.room.type.availability"
_description = "Availability"
_inherit = "mail.thread"
_description = "Reservation availability plan"
# Default methods
@api.model
def _default_max_avail(self):
return self.room_type_id.default_max_avail
@api.model
def _default_quota(self):
return self.room_type_id.default_quota
def _get_default_pms_property(self):
return self.env.user.pms_property_id or None
# Fields declaration
room_type_id = fields.Many2one(
"pms.room.type", "Room Type", required=True, ondelete="cascade"
)
date = fields.Date(
"Date",
required=True,
tracking=True,
)
quota = fields.Integer(
"Quota",
default=_default_quota,
tracking=True,
help="Generic Quota assigned.",
)
max_avail = fields.Integer(
"Max. Availability",
default=-1,
readonly=True,
tracking=True,
help="Maximum simultaneous availability on own Booking Engine.",
)
no_web = fields.Boolean(
"No Web",
default=False,
tracking=True,
help="Set zero availability to the own Booking Engine "
"even when the availability is positive,",
name = fields.Char("Availability Plan Name", required=True)
pms_property_id = fields.Many2one(
comodel_name="pms.property",
string="Property",
ondelete="restrict",
)
_sql_constraints = [
(
"unique_availability_room_type_rule_date",
"unique(room_type_id, date)",
"The availability rule for this date in this room type already exists, "
"modify it instead of trying to create a new one",
),
]
pms_pricelist_ids = fields.One2many(
comodel_name="product.pricelist",
inverse_name="availability_id",
string="Pricelists",
required=False,
ondelete="restrict",
)
item_ids = fields.One2many(
comodel_name="pms.room.type.availability.rule",
inverse_name="availability_id",
string="Rule Items",
copy=True,
)
active = fields.Boolean(
string="Active",
default=True,
help="If unchecked, it will allow you to hide the "
"Availability plan without removing it.",
)
# Business Methods
@classmethod
def any_rule_applies(cls, checkin, checkout, item):
reservation_len = (checkout - checkin).days
return any(
[
(0 < item.max_stay < reservation_len),
(0 < item.min_stay > reservation_len),
(0 < item.max_stay_arrival < reservation_len and checkin == item.date),
(0 < item.min_stay_arrival > reservation_len and checkin == item.date),
item.closed,
(item.closed_arrival and checkin == item.date),
(item.closed_departure and checkout == item.date),
(item.quota == 0 or item.max_avail == 0),
]
)
@api.model
def rooms_available(
self, checkin, checkout, room_type_id=False, current_lines=False
self,
checkin,
checkout,
room_type_id=False,
current_lines=False,
pricelist=False,
):
domain = self._get_domain_reservations_occupation(
dfrom=checkin,
dto=checkout - timedelta(1),
current_lines=current_lines,
)
reservation_lines = self.env["pms.reservation.line"].search(domain)
reservations_rooms = reservation_lines.mapped("room_id.id")
free_rooms = self.env["pms.room"].search([("id", "not in", reservations_rooms)])
if room_type_id:
rooms_linked = (
self.env["pms.room.type"].search([("id", "=", room_type_id)]).room_ids
if current_lines and not isinstance(current_lines, list):
current_lines = [current_lines]
rooms_not_avail = (
self.env["pms.reservation.line"]
.search(
[
("date", ">=", checkin),
("date", "<=", checkout - datetime.timedelta(1)),
("occupies_availability", "=", True),
("id", "not in", current_lines if current_lines else []),
]
)
free_rooms = free_rooms & rooms_linked
.mapped("room_id.id")
)
domain_rooms = [
("id", "not in", rooms_not_avail if len(rooms_not_avail) > 0 else [])
]
domain_rules = [
("date", ">=", checkin),
("date", "<=", checkout),
]
if room_type_id:
domain_rooms.append(("room_type_id", "=", room_type_id))
domain_rules.append(("room_type_id", "=", room_type_id))
free_rooms = self.env["pms.room"].search(domain_rooms)
if pricelist:
domain_rules.append(("availability_id.pms_pricelist_ids", "=", pricelist))
rule_items = self.env["pms.room.type.availability.rule"].search(
domain_rules
)
if len(rule_items) > 0:
room_types_to_remove = []
for item in rule_items:
if self.any_rule_applies(checkin, checkout, item):
room_types_to_remove.append(item.room_type_id.id)
free_rooms = free_rooms.filtered(
lambda x: x.room_type_id.id not in room_types_to_remove
)
return free_rooms.sorted(key=lambda r: r.sequence)
@api.model
def _get_domain_reservations_occupation(self, dfrom, dto, current_lines=False):
if current_lines and not isinstance(current_lines, list):
current_lines = [current_lines]
domain = [
("date", ">=", dfrom),
("date", "<=", dto),
("occupies_availability", "=", True),
("id", "not in", current_lines),
]
return domain
def splitted_availability(
self,
checkin,
checkout,
room_type_id=False,
current_lines=False,
pricelist=False,
):
for date_iterator in [
checkin + datetime.timedelta(days=x)
for x in range(0, (checkout - checkin).days)
]:
rooms_avail = self.rooms_available(
checkin=date_iterator,
checkout=date_iterator + datetime.timedelta(1),
room_type_id=room_type_id,
current_lines=current_lines,
pricelist=pricelist.id,
)
if len(rooms_avail) < 1:
return False
return True
@api.model
def update_quota(self, pricelist_id, room_type_id, date, line):
if pricelist_id and room_type_id and date:
rule = self.env["pms.room.type.availability.rule"].search(
[
("availability_id.pms_pricelist_ids", "=", pricelist_id.id),
("room_type_id", "=", room_type_id.id),
("date", "=", date),
]
)
# applies a rule
if rule:
rule.ensure_one()
if rule and rule.quota != -1 and rule.quota > 0:
# the line has no rule item applied before
if not line.impacts_quota:
rule.quota -= 1
return rule.id
# the line has a rule item applied before
elif line.impacts_quota != rule.id:
# decrement quota on current rule item
rule.quota -= 1
# check old rule item
old_rule = self.env["pms.room.type.availability.rule"].search(
[("id", "=", line.impacts_quota)]
)
# restore quota in old rule item
if old_rule:
old_rule.quota += 1
return rule.id
# in any case, check old rule item
if line.impacts_quota:
old_rule = self.env["pms.room.type.availability.rule"].search(
[("id", "=", line.impacts_quota)]
)
# and restore quota in old rule item
if old_rule:
old_rule.quota += 1
return False

View File

@@ -4,15 +4,15 @@ from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
class PmsRoomTypeRestrictionItem(models.Model):
_name = "pms.room.type.restriction.item"
_description = "Reservation restriction by day"
class PmsRoomTypeAvailabilityRule(models.Model):
_name = "pms.room.type.availability.rule"
_description = "Reservation rule by day"
# Field Declarations
restriction_id = fields.Many2one(
comodel_name="pms.room.type.restriction",
string="Restriction Plan",
availability_id = fields.Many2one(
comodel_name="pms.room.type.availability",
string="Availability Plan",
ondelete="cascade",
index=True,
)
@@ -71,8 +71,8 @@ class PmsRoomTypeRestrictionItem(models.Model):
_sql_constraints = [
(
"room_type_registry_unique",
"unique(restriction_id, room_type_id, date)",
"Only can exists one restriction in the same \
"unique(availability_id, room_type_id, date)",
"Only can exists one availability rule in the same \
day for the same room type!",
)
]

View File

@@ -1,195 +0,0 @@
# Copyright 2017 Alexandre Díaz
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import datetime
from odoo import api, fields, models
class PmsRoomTypeRestriction(models.Model):
"""The room type restriction is used as a daily restriction plan for room types
and therefore is related only with one property."""
_name = "pms.room.type.restriction"
_description = "Reservation restriction plan"
# Default methods
@api.model
def _get_default_pms_property(self):
return self.env.user.pms_property_id or None
# Fields declaration
name = fields.Char("Restriction Plan Name", required=True)
pms_property_id = fields.Many2one(
comodel_name="pms.property",
string="Property",
ondelete="restrict",
)
pms_pricelist_ids = fields.One2many(
comodel_name="product.pricelist",
inverse_name="restriction_id",
string="Pricelists",
required=False,
ondelete="restrict",
)
item_ids = fields.One2many(
comodel_name="pms.room.type.restriction.item",
inverse_name="restriction_id",
string="Restriction Items",
copy=True,
)
active = fields.Boolean(
string="Active",
default=True,
help="If unchecked, it will allow you to hide the "
"restriction plan without removing it.",
)
# Business Methods
@classmethod
def any_restriction_applies(cls, checkin, checkout, item):
reservation_len = (checkout - checkin).days
return any(
[
(0 < item.max_stay < reservation_len),
(0 < item.min_stay > reservation_len),
(0 < item.max_stay_arrival < reservation_len and checkin == item.date),
(0 < item.min_stay_arrival > reservation_len and checkin == item.date),
item.closed,
(item.closed_arrival and checkin == item.date),
(item.closed_departure and checkout == item.date),
(item.quota == 0 or item.max_avail == 0),
]
)
@api.model
def rooms_available(
self,
checkin,
checkout,
room_type_id=False,
current_lines=False,
pricelist=False,
):
if current_lines and not isinstance(current_lines, list):
current_lines = [current_lines]
rooms_not_avail = (
self.env["pms.reservation.line"]
.search(
[
("date", ">=", checkin),
("date", "<=", checkout - datetime.timedelta(1)),
("occupies_availability", "=", True),
("id", "not in", current_lines if current_lines else []),
]
)
.mapped("room_id.id")
)
domain_rooms = [
("id", "not in", rooms_not_avail if len(rooms_not_avail) > 0 else [])
]
domain_restrictions = [
("date", ">=", checkin),
("date", "<=", checkout),
]
if room_type_id:
domain_rooms.append(("room_type_id", "=", room_type_id))
domain_restrictions.append(("room_type_id", "=", room_type_id))
free_rooms = self.env["pms.room"].search(domain_rooms)
if pricelist:
domain_restrictions.append(
("restriction_id.pms_pricelist_ids", "=", pricelist)
)
restriction_items = self.env["pms.room.type.restriction.item"].search(
domain_restrictions
)
if len(restriction_items) > 0:
room_types_to_remove = []
for item in restriction_items:
if self.any_restriction_applies(checkin, checkout, item):
room_types_to_remove.append(item.room_type_id.id)
free_rooms = free_rooms.filtered(
lambda x: x.room_type_id.id not in room_types_to_remove
)
return free_rooms.sorted(key=lambda r: r.sequence)
@api.model
def splitted_availability(
self,
checkin,
checkout,
room_type_id=False,
current_lines=False,
pricelist=False,
):
for date_iterator in [
checkin + datetime.timedelta(days=x)
for x in range(0, (checkout - checkin).days)
]:
rooms_avail = self.rooms_available(
checkin=date_iterator,
checkout=date_iterator + datetime.timedelta(1),
room_type_id=room_type_id,
current_lines=current_lines,
pricelist=pricelist.id,
)
if len(rooms_avail) < 1:
return False
return True
@api.model
def update_quota(self, pricelist_id, room_type_id, date, line):
if pricelist_id and room_type_id and date:
restriction = self.env["pms.room.type.restriction.item"].search(
[
("restriction_id.pms_pricelist_ids", "=", pricelist_id.id),
("room_type_id", "=", room_type_id.id),
("date", "=", date),
]
)
# applies a restriction
if restriction:
restriction.ensure_one()
if restriction and restriction.quota != -1 and restriction.quota > 0:
# the line has no restriction item applied before
if not line.impacts_quota:
restriction.quota -= 1
return restriction.id
# the line has a restriction item applied before
elif line.impacts_quota != restriction.id:
# decrement quota on current restriction_item
restriction.quota -= 1
# check old restricition item
old_restriction = self.env[
"pms.room.type.restriction.item"
].search([("id", "=", line.impacts_quota)])
# restore quota in old restriction item
if old_restriction:
old_restriction.quota += 1
return restriction.id
# in any case, check old restricition item
if line.impacts_quota:
old_restriction = self.env["pms.room.type.restriction.item"].search(
[("id", "=", line.impacts_quota)]
)
# and restore quota in old restriction item
if old_restriction:
old_restriction.quota += 1
return False

View File

@@ -22,9 +22,9 @@ class ProductPricelist(models.Model):
[("daily", "Daily Plan")], string="Pricelist Type", default="daily"
)
restriction_id = fields.Many2one(
comodel_name="pms.room.type.restriction",
string="restriction",
availability_id = fields.Many2one(
comodel_name="pms.room.type.availability",
string="Availability Plan",
ondelete="restrict",
)

View File

@@ -2,4 +2,4 @@ This module is an all-in-one property management system (PMS) focused on medium-
for managing every aspect of your property's daily operations.
You can manage hotel properties with multi-hotel and multi-company support, including your rooms inventory,
reservations, check-in, daily reports, board services, rate and restriction plans among other hotel functionalities.
reservations, check-in, daily reports, board services, rate and availability plans among other hotel functionalities.

View File

@@ -3,7 +3,6 @@ user_access_pms_floor,user_access_pms_floor,model_pms_floor,pms.group_pms_user,1
user_access_pms_amenity,user_access_pms_amenity,model_pms_amenity,pms.group_pms_user,1,0,0,0
user_access_pms_amenity_type,user_access_pms_amenity_type,model_pms_amenity_type,pms.group_pms_user,1,0,0,0
user_access_pms_service,user_access_pms_service,model_pms_service,pms.group_pms_user,1,1,1,1
user_access_pms_room_type_restriction,user_access_pms_room_type_restriction,model_pms_room_type_restriction,pms.group_pms_user,1,0,0,0
user_access_pms_reservation_line,user_access_pms_reservation_line,model_pms_reservation_line,pms.group_pms_user,1,1,1,1
user_access_room_closure_reason,user_access_room_closure_reason,model_room_closure_reason,pms.group_pms_user,1,0,0,0
user_access_pms_service_line,user_access_pms_service_line,model_pms_service_line,pms.group_pms_user,1,1,1,1
@@ -12,7 +11,7 @@ user_access_pms_checkin_partner,user_access_pms_checkin_partner,model_pms_checki
user_access_pms_room_type_class,user_access_pms_room_type_class,model_pms_room_type_class,pms.group_pms_user,1,0,0,0
user_access_pms_room,user_access_pms_room,model_pms_room,pms.group_pms_user,1,0,0,0
user_access_shared_pms_room,user_access_pms_shared_room,model_pms_shared_room,pms.group_pms_user,1,0,0,0
user_access_pms_room_type_restriction_item,user_access_pms_room_type_restriction_item,model_pms_room_type_restriction_item,pms.group_pms_user,1,0,0,0
user_access_pms_room_type_availability_rule,user_access_pms_room_type_availability_rule,model_pms_room_type_availability_rule,pms.group_pms_user,1,0,0,0
user_access_pms_reservation,user_access_pms_reservation,model_pms_reservation,pms.group_pms_user,1,1,1,1
user_access_pms_folio,user_access_pms_folio,model_pms_folio,pms.group_pms_user,1,1,1,1
user_access_pms_room_type,user_access_pms_room_type,model_pms_room_type,pms.group_pms_user,1,0,0,0
@@ -29,7 +28,6 @@ manager_access_pms_floor,manager_access_pms_floor,model_pms_floor,pms.group_pms_
manager_access_pms_amenity,manager_access_pms_amenity,model_pms_amenity,pms.group_pms_manager,1,1,1,1
manager_access_pms_amenity_type,manager_access_pms_amenity_type,model_pms_amenity_type,pms.group_pms_manager,1,1,1,1
manager_access_pms_service,manager_access_pms_service,model_pms_service,pms.group_pms_manager,1,1,1,1
manager_access_pms_room_type_restriction,manager_access_pms_room_type_restriction,model_pms_room_type_restriction,pms.group_pms_manager,1,1,1,1
manager_access_pms_reservation_line,manager_access_pms_reservation_line,model_pms_reservation_line,pms.group_pms_manager,1,1,1,1
manager_access_room_closure_reason,manager_access_room_closure_reason,model_room_closure_reason,pms.group_pms_manager,1,1,1,1
manager_access_pms_service_line,manager_access_pms_service_line,model_pms_service_line,pms.group_pms_manager,1,1,1,1
@@ -38,7 +36,7 @@ manager_access_pms_checkin_partner,manager_access_pms_checkin_partner,model_pms_
manager_access_pms_room_type_class,manager_access_pms_room_type_class,model_pms_room_type_class,pms.group_pms_manager,1,1,1,1
manager_access_pms_room,manager_access_pms_room,model_pms_room,pms.group_pms_manager,1,1,1,1
manager_access_pms_shared_room,manager_access_pms_shared_room,model_pms_shared_room,pms.group_pms_manager,1,1,1,1
manager_access_pms_room_type_restriction_item,manager_access_pms_room_type_restriction_item,model_pms_room_type_restriction_item,pms.group_pms_manager,1,1,1,1
manager_access_pms_room_type_availability_rule,manager_access_pms_room_type_availability_rule,model_pms_room_type_availability_rule,pms.group_pms_manager,1,1,1,1
manager_access_pms_reservation,manager_access_pms_reservation,model_pms_reservation,pms.group_pms_manager,1,1,1,1
manager_access_pms_folio,manager_access_pms_folio,model_pms_folio,pms.group_pms_manager,1,1,1,1
manager_access_pms_room_type,manager_access_pms_room_type,model_pms_room_type,pms.group_pms_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
3 user_access_pms_amenity user_access_pms_amenity model_pms_amenity pms.group_pms_user 1 0 0 0
4 user_access_pms_amenity_type user_access_pms_amenity_type model_pms_amenity_type pms.group_pms_user 1 0 0 0
5 user_access_pms_service user_access_pms_service model_pms_service pms.group_pms_user 1 1 1 1
user_access_pms_room_type_restriction user_access_pms_room_type_restriction model_pms_room_type_restriction pms.group_pms_user 1 0 0 0
6 user_access_pms_reservation_line user_access_pms_reservation_line model_pms_reservation_line pms.group_pms_user 1 1 1 1
7 user_access_room_closure_reason user_access_room_closure_reason model_room_closure_reason pms.group_pms_user 1 0 0 0
8 user_access_pms_service_line user_access_pms_service_line model_pms_service_line pms.group_pms_user 1 1 1 1
11 user_access_pms_room_type_class user_access_pms_room_type_class model_pms_room_type_class pms.group_pms_user 1 0 0 0
12 user_access_pms_room user_access_pms_room model_pms_room pms.group_pms_user 1 0 0 0
13 user_access_shared_pms_room user_access_pms_shared_room model_pms_shared_room pms.group_pms_user 1 0 0 0
14 user_access_pms_room_type_restriction_item user_access_pms_room_type_availability_rule user_access_pms_room_type_restriction_item user_access_pms_room_type_availability_rule model_pms_room_type_restriction_item model_pms_room_type_availability_rule pms.group_pms_user 1 0 0 0
15 user_access_pms_reservation user_access_pms_reservation model_pms_reservation pms.group_pms_user 1 1 1 1
16 user_access_pms_folio user_access_pms_folio model_pms_folio pms.group_pms_user 1 1 1 1
17 user_access_pms_room_type user_access_pms_room_type model_pms_room_type pms.group_pms_user 1 0 0 0
28 manager_access_pms_amenity manager_access_pms_amenity model_pms_amenity pms.group_pms_manager 1 1 1 1
29 manager_access_pms_amenity_type manager_access_pms_amenity_type model_pms_amenity_type pms.group_pms_manager 1 1 1 1
30 manager_access_pms_service manager_access_pms_service model_pms_service pms.group_pms_manager 1 1 1 1
manager_access_pms_room_type_restriction manager_access_pms_room_type_restriction model_pms_room_type_restriction pms.group_pms_manager 1 1 1 1
31 manager_access_pms_reservation_line manager_access_pms_reservation_line model_pms_reservation_line pms.group_pms_manager 1 1 1 1
32 manager_access_room_closure_reason manager_access_room_closure_reason model_room_closure_reason pms.group_pms_manager 1 1 1 1
33 manager_access_pms_service_line manager_access_pms_service_line model_pms_service_line pms.group_pms_manager 1 1 1 1
36 manager_access_pms_room_type_class manager_access_pms_room_type_class model_pms_room_type_class pms.group_pms_manager 1 1 1 1
37 manager_access_pms_room manager_access_pms_room model_pms_room pms.group_pms_manager 1 1 1 1
38 manager_access_pms_shared_room manager_access_pms_shared_room model_pms_shared_room pms.group_pms_manager 1 1 1 1
39 manager_access_pms_room_type_restriction_item manager_access_pms_room_type_availability_rule manager_access_pms_room_type_restriction_item manager_access_pms_room_type_availability_rule model_pms_room_type_restriction_item model_pms_room_type_availability_rule pms.group_pms_manager 1 1 1 1
40 manager_access_pms_reservation manager_access_pms_reservation model_pms_reservation pms.group_pms_manager 1 1 1 1
41 manager_access_pms_folio manager_access_pms_folio model_pms_folio pms.group_pms_manager 1 1 1 1
42 manager_access_pms_room_type manager_access_pms_room_type model_pms_room_type pms.group_pms_manager 1 1 1 1

View File

@@ -371,7 +371,7 @@ ul.auto-toc {
<p>This module is an all-in-one property management system (PMS) focused on medium-sized hotels
for managing every aspect of your propertys daily operations.</p>
<p>You can manage hotel properties with multi-hotel and multi-company support, including your rooms inventory,
reservations, check-in, daily reports, board services, rate and restriction plans among other hotel functionalities.</p>
reservations, check-in, daily reports, board services, rate and availability plans among other hotel functionalities.</p>
<div class="admonition important">
<p class="first admonition-title">Important</p>
<p class="last">This is an alpha version, the data model and design can change at any time without warning.

View File

@@ -25,4 +25,4 @@ from . import test_pms_pricelist_priority
from . import test_pms_checkin_partner
from . import test_pms_sale_channel
from . import test_pms_folio
from . import test_pms_room_type_restriction
from . import test_pms_room_type_availability_rules

View File

@@ -15,19 +15,19 @@ class TestPmsPricelistRules(common.TransactionCase):
{"name": "Category1"}
)
self.restriction = self.env["pms.room.type.restriction"].create(
{"name": "Restriction1"}
self.availability = self.env["pms.room.type.availability"].create(
{"name": "Availability 1"}
)
self.restriction2 = self.env["pms.room.type.restriction"].create(
{"name": "Restriction2"}
self.availability2 = self.env["pms.room.type.availability"].create(
{"name": "Availability"}
)
self.property1 = self.env["pms.property"].create(
{
"name": "Property_1",
"company_id": self.env.ref("base.main_company").id,
"default_pricelist_id": self.env.ref("product.list0").id,
"default_restriction_id": self.restriction.id,
"default_availability_id": self.availability.id,
}
)
@@ -36,7 +36,7 @@ class TestPmsPricelistRules(common.TransactionCase):
"name": "Property_2",
"company_id": self.env.ref("base.main_company").id,
"default_pricelist_id": self.env.ref("product.list0").id,
"default_restriction_id": self.restriction2.id,
"default_availability_id": self.availability2.id,
}
)

View File

@@ -11,9 +11,9 @@ from .common import TestHotel
@freeze_time("2012-01-14")
class TestPmsReservations(TestHotel):
def create_common_scenario(self):
# create a room type restriction
self.room_type_restriction = self.env["pms.room.type.restriction"].create(
{"name": "Restriction plan for TEST"}
# create a room type availability
self.room_type_availability = self.env["pms.room.type.availability"].create(
{"name": "Availability plan for TEST"}
)
# create a property
@@ -22,7 +22,7 @@ class TestPmsReservations(TestHotel):
"name": "MY PMS TEST",
"company_id": self.env.ref("base.main_company").id,
"default_pricelist_id": self.env.ref("product.list0").id,
"default_restriction_id": self.room_type_restriction.id,
"default_availability_id": self.room_type_availability.id,
}
)

View File

@@ -10,7 +10,7 @@ from .common import TestHotel
@freeze_time("1980-01-01")
class TestPmsRoomTypeRestriction(TestHotel):
class TestPmsRoomTypeAvailabilityRules(TestHotel):
def create_common_scenario(self):
# product.pricelist
self.test_pricelist1 = self.env["product.pricelist"].create(
@@ -18,10 +18,12 @@ class TestPmsRoomTypeRestriction(TestHotel):
"name": "test pricelist 1",
}
)
# pms.room.type.restriction
self.test_room_type_restriction1 = self.env["pms.room.type.restriction"].create(
# pms.room.type.availability
self.test_room_type_availability1 = self.env[
"pms.room.type.availability"
].create(
{
"name": "Restriction plan for TEST",
"name": "Availability plan for TEST",
"pms_pricelist_ids": [(6, 0, [self.test_pricelist1.id])],
}
)
@@ -31,7 +33,7 @@ class TestPmsRoomTypeRestriction(TestHotel):
"name": "MY PMS TEST",
"company_id": self.env.ref("base.main_company").id,
"default_pricelist_id": self.test_pricelist1.id,
"default_restriction_id": self.test_room_type_restriction1.id,
"default_availability_id": self.test_room_type_availability1.id,
}
)
# pms.room.type.class
@@ -115,7 +117,7 @@ class TestPmsRoomTypeRestriction(TestHotel):
@Skip
def test_availability_rooms_all(self):
# TEST CASE
# get availability withouth restrictions
# get availability withouth rules
# ARRANGE
self.create_common_scenario()
@@ -127,7 +129,7 @@ class TestPmsRoomTypeRestriction(TestHotel):
)
# ACT
result = self.env["pms.room.type.restriction"].rooms_available(
result = self.env["pms.room.type.availability"].rooms_available(
checkin=checkin,
checkout=checkout,
)
@@ -136,13 +138,13 @@ class TestPmsRoomTypeRestriction(TestHotel):
self.assertTrue(
obtained,
"Availability should contain the test rooms"
"because there's no restriction for them.",
"because there's no availability rules for them.",
)
@Skip
def test_availability_rooms_all_lines(self):
# TEST CASE
# get availability withouth restrictions
# get availability withouth rules
# given reservation lines to not consider
# ARRANGE
@@ -161,7 +163,7 @@ class TestPmsRoomTypeRestriction(TestHotel):
)
# ACT
result = self.env["pms.room.type.restriction"].rooms_available(
result = self.env["pms.room.type.availability"].rooms_available(
checkin=checkin,
checkout=checkout,
current_lines=test_reservation.reservation_line_ids.ids,
@@ -171,13 +173,13 @@ class TestPmsRoomTypeRestriction(TestHotel):
self.assertTrue(
obtained,
"Availability should contain the test rooms"
"because there's no restriction for them.",
"because there's no availability rules for them.",
)
@Skip
def test_availability_rooms_room_type(self):
# TEST CASE
# get availability withouth restrictions
# get availability withouth rules
# given a room type
# ARRANGE
@@ -190,7 +192,7 @@ class TestPmsRoomTypeRestriction(TestHotel):
)
# ACT
result = self.env["pms.room.type.restriction"].rooms_available(
result = self.env["pms.room.type.availability"].rooms_available(
checkin=fields.date.today(),
checkout=(fields.datetime.today() + datetime.timedelta(days=4)).date(),
room_type_id=self.test_room_type_double.id,
@@ -201,30 +203,31 @@ class TestPmsRoomTypeRestriction(TestHotel):
self.assertTrue(
obtained,
"Availability should contain the test rooms"
"because there's no restriction for them.",
"because there's no availability rules for them.",
)
@Skip
def test_availability_closed_no_room_type(self):
# TEST CASE:
# coverage for 2 points:
# 1. without room type, restrictions associated with the pricelist are applied
# 2. restriction rule "closed" is taken into account
# 1. without room type, availability rules associated
# with the pricelist are applied
# 2. availability rule "closed" is taken into account
# ARRANGE
self.create_common_scenario()
self.test_room_type_restriction1_item1 = self.env[
"pms.room.type.restriction.item"
self.test_room_type_availability1_item1 = self.env[
"pms.room.type.availability.rule"
].create(
{
"restriction_id": self.test_room_type_restriction1.id,
"availability_id": self.test_room_type_availability1.id,
"room_type_id": self.test_room_type_double.id,
"date": (fields.datetime.today() + datetime.timedelta(days=2)).date(),
"closed": True, # <- (1/2)
}
)
# ACT
result = self.env["pms.room.type.restriction"].rooms_available(
result = self.env["pms.room.type.availability"].rooms_available(
checkin=fields.date.today(),
checkout=(fields.datetime.today() + datetime.timedelta(days=4)).date(),
# room_type_id=False, # <- (2/2)
@@ -235,24 +238,24 @@ class TestPmsRoomTypeRestriction(TestHotel):
self.test_room_type_double,
result.mapped("room_type_id"),
"Availability should not contain rooms of a type "
"which its restriction rules applies",
"which its availability rules applies",
)
@Skip
def test_availability_restrictions(self):
def test_availability_rules(self):
# TEST CASE
# the availability should take into acount restriction rules:
# the availability should take into acount availability rules:
# closed_arrival, closed_departure, min_stay, max_stay,
# min_stay_arrival, max_stay_arrival
# ARRANGE
self.create_common_scenario()
self.test_room_type_restriction1_item1 = self.env[
"pms.room.type.restriction.item"
self.test_room_type_availability1_item1 = self.env[
"pms.room.type.availability.rule"
].create(
{
"restriction_id": self.test_room_type_restriction1.id,
"availability_id": self.test_room_type_availability1.id,
"room_type_id": self.test_room_type_double.id,
"date": (fields.datetime.today() + datetime.timedelta(days=0)).date(),
}
@@ -364,9 +367,9 @@ class TestPmsRoomTypeRestriction(TestHotel):
with self.subTest(k=test_case):
# ACT
self.test_room_type_restriction1_item1.write(test_case)
self.test_room_type_availability1_item1.write(test_case)
result = self.env["pms.room.type.restriction"].rooms_available(
result = self.env["pms.room.type.availability"].rooms_available(
checkin=checkin,
checkout=checkout,
room_type_id=self.test_room_type_double.id,
@@ -378,23 +381,23 @@ class TestPmsRoomTypeRestriction(TestHotel):
self.test_room_type_double,
result.mapped("room_type_id"),
"Availability should not contain rooms of a type "
"which its restriction rules applies",
"which its availability rules applies",
)
@Skip
@freeze_time("1980-11-01")
def test_restriction_on_create_reservation(self):
def test_rule_on_create_reservation(self):
# TEST CASE
# a restriction should be applied that would prevent the
# an availability rule should be applied that would prevent the
# creation of reservations
# ARRANGE
self.create_common_scenario()
self.test_room_type_restriction1_item1 = self.env[
"pms.room.type.restriction.item"
self.test_room_type_availability1_item1 = self.env[
"pms.room.type.availability.rule"
].create(
{
"restriction_id": self.test_room_type_restriction1.id,
"availability_id": self.test_room_type_availability1.id,
"room_type_id": self.test_room_type_double.id,
"date": (fields.datetime.today() + datetime.timedelta(days=2)).date(),
"closed": True,
@@ -407,7 +410,7 @@ class TestPmsRoomTypeRestriction(TestHotel):
# ACT & ASSERT
with self.assertRaises(
ValidationError,
msg="Restriction should be applied that would"
msg="Availability rules should be applied that would"
" prevent the creation of the reservation.",
):
self.env["pms.reservation"].create(
@@ -423,18 +426,18 @@ class TestPmsRoomTypeRestriction(TestHotel):
@Skip
@freeze_time("1980-11-01")
def test_restriction_on_create_splitted_reservation(self):
def test_rules_on_create_splitted_reservation(self):
# TEST CASE
# a restriction should be applied that would prevent the
# an availability rule should be applied that would prevent the
# creation of reservations including splitted reservations.
# ARRANGE
self.create_common_scenario()
self.test_room_type_restriction1_item1 = self.env[
"pms.room.type.restriction.item"
self.test_room_type_availability1_item1 = self.env[
"pms.room.type.availability.rule"
].create(
{
"restriction_id": self.test_room_type_restriction1.id,
"availability_id": self.test_room_type_availability1.id,
"room_type_id": self.test_room_type_double.id,
"date": (fields.datetime.today() + datetime.timedelta(days=2)).date(),
"closed": True,
@@ -469,7 +472,7 @@ class TestPmsRoomTypeRestriction(TestHotel):
# ACT & ASSERT
with self.assertRaises(
ValidationError,
msg="Restriction should be applied that would"
msg="Availability rule should be applied that would"
" prevent the creation of splitted reservation.",
):
self.env["pms.reservation"].create(
@@ -485,19 +488,19 @@ class TestPmsRoomTypeRestriction(TestHotel):
@Skip
@freeze_time("1980-11-01")
def test_restriction_update_quota_on_create_reservation(self):
def test_rule_update_quota_on_create_reservation(self):
# TEST CASE
# quota rule is changed after creating a reservation
# with pricelist linked to a restriction_item that applies
# with pricelist linked to a availability plan that applies
# ARRANGE
self.create_common_scenario()
self.test_room_type_restriction1_item1 = self.env[
"pms.room.type.restriction.item"
self.test_room_type_availability1_item1 = self.env[
"pms.room.type.availability.rule"
].create(
{
"restriction_id": self.test_room_type_restriction1.id,
"availability_id": self.test_room_type_availability1.id,
"room_type_id": self.test_room_type_double.id,
"date": datetime.date.today(),
"quota": 1,
@@ -530,12 +533,12 @@ class TestPmsRoomTypeRestriction(TestHotel):
)
@freeze_time("1980-11-01")
def test_restriction_update_quota_on_update_reservation(self):
def test_rule_update_quota_on_update_reservation(self):
# TEST CASE
# quota rule is restored after creating a reservation
# with pricelist linked to a restriction_item that applies
# with pricelist linked to a availability rule that applies
# and then modify the pricelist of the reservation and
# no restriction applies
# no rules applies
# ARRANGE
self.create_common_scenario()
@@ -545,9 +548,9 @@ class TestPmsRoomTypeRestriction(TestHotel):
"name": "test pricelist 2",
}
)
restriction = self.env["pms.room.type.restriction.item"].create(
rule = self.env["pms.room.type.availability.rule"].create(
{
"restriction_id": self.test_room_type_restriction1.id,
"availability_id": self.test_room_type_availability1.id,
"room_type_id": self.test_room_type_double.id,
"date": datetime.date.today(),
"quota": test_quota,
@@ -569,6 +572,6 @@ class TestPmsRoomTypeRestriction(TestHotel):
reservation.flush()
self.assertEqual(
test_quota,
restriction.quota,
rule.quota,
"The quota should be restored after changing the reservation's pricelist",
)

View File

@@ -21,10 +21,10 @@
<group
colspan="4"
col="4"
string="Price and Restriction Plans"
string="Price and Availability Plans"
>
<field name="default_pricelist_id" required="True" />
<field name="default_restriction_id" required="True" />
<field name="default_availability_id" required="True" />
</group>
<group string="Timezone">
<field name="tz" widget="timezone_mismatch" />

View File

@@ -1,10 +1,10 @@
<?xml version="1.0" ?>
<odoo>
<record id="room_type_restriction_item_view_form" model="ir.ui.view">
<field name="name">pms.room.type.restriction.item.form</field>
<field name="model">pms.room.type.restriction.item</field>
<record id="room_type_availability_rule_view_form" model="ir.ui.view">
<field name="name">pms.room.type.availability.rule.form</field>
<field name="model">pms.room.type.availability.rule</field>
<field name="arch" type="xml">
<form string="Restrictions">
<form string="Availability Plans">
<group>
<field name="room_type_id" required="True" />
<field name="date" />
@@ -31,11 +31,11 @@
</form>
</field>
</record>
<record id="room_type_restriction_item_view_tree" model="ir.ui.view">
<field name="name">pms.room.type.restriction.item.tree</field>
<field name="model">pms.room.type.restriction.item</field>
<record id="room_type_availability_rule_view_tree" model="ir.ui.view">
<field name="name">pms.room.type.availability.rule.tree</field>
<field name="model">pms.room.type.availability.rule</field>
<field name="arch" type="xml">
<tree string="Restrictions">
<tree string="Availability rules">
<field name="room_type_id" />
<field name="date" />
<field name="min_stay" />

View File

@@ -1,10 +1,10 @@
<?xml version="1.0" ?>
<odoo>
<record id="room_type_restriction_view_form" model="ir.ui.view">
<field name="name">pms.room.type.restriction.form</field>
<field name="model">pms.room.type.restriction</field>
<record id="room_type_availability_view_form" model="ir.ui.view">
<field name="name">pms.room.type.availability.form</field>
<field name="model">pms.room.type.availability</field>
<field name="arch" type="xml">
<form string="Restrictions">
<form string="Rules">
<sheet>
<div class="oe_button_box" name="button_box">
<button
@@ -37,9 +37,9 @@
options="{'no_create': True,'no_open': True}"
/>
</group>
<separator string="Restriction Items" />
<separator string="Availability Rules" />
<field name="item_ids" nolabel="1">
<tree string="Restriction Items">
<tree string="Availability Rules">
<field name="room_type_id" />
<field name="date" />
</tree>
@@ -48,11 +48,11 @@
</form>
</field>
</record>
<record id="room_type_restriction_view_tree" model="ir.ui.view">
<field name="name">pms.room.type.restriction.tree</field>
<field name="model">pms.room.type.restriction</field>
<record id="room_type_availability_view_tree" model="ir.ui.view">
<field name="name">pms.room.type.availability.tree</field>
<field name="model">pms.room.type.availability</field>
<field name="arch" type="xml">
<tree string="Restrictions">
<tree string="Availability Plans">
<field name="name" />
<field name="pms_property_id" />
<field
@@ -64,17 +64,17 @@
</tree>
</field>
</record>
<!-- Action of reservation restriction -->
<record model="ir.actions.act_window" id="room_type_restriction_action">
<field name="name">Reservation restrictions</field>
<field name="res_model">pms.room.type.restriction</field>
<!-- Action of reservation availability plan-->
<record model="ir.actions.act_window" id="room_type_availability_action">
<field name="name">Reservation Availability Plans</field>
<field name="res_model">pms.room.type.availability</field>
<field name="view_mode">tree,form</field>
</record>
<!-- MENUS -->
<menuitem
name="Restrictions"
id="reservation_restriction_menu"
action="room_type_restriction_action"
name="Availability Plans"
id="reservation_availability_rules_menu"
action="room_type_availability_action"
sequence="22"
parent="pms.configuration_others"
/>