mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
[IMP] pms: availability plans
This commit is contained in:
committed by
Eric Antones
parent
b218086024
commit
6080db54be
@@ -507,7 +507,11 @@ class PmsReservation(models.Model):
|
||||
)
|
||||
|
||||
@api.depends(
|
||||
"reservation_line_ids.date", "overbooking", "state", "preferred_room_id"
|
||||
"reservation_line_ids.date",
|
||||
"overbooking",
|
||||
"state",
|
||||
"preferred_room_id",
|
||||
"pricelist_id",
|
||||
)
|
||||
def _compute_allowed_room_ids(self):
|
||||
for reservation in self:
|
||||
@@ -517,13 +521,12 @@ class PmsReservation(models.Model):
|
||||
[("active", "=", True)]
|
||||
)
|
||||
return
|
||||
rooms_available = self.env[
|
||||
"pms.room.type.availability"
|
||||
].rooms_available(
|
||||
rooms_available = self.env["pms.room.type.restriction"].rooms_available(
|
||||
checkin=reservation.checkin,
|
||||
checkout=reservation.checkout,
|
||||
room_type_id=False, # Allow chosen any available room
|
||||
current_lines=reservation.reservation_line_ids.ids,
|
||||
pricelist=reservation.pricelist_id.id,
|
||||
)
|
||||
reservation.allowed_room_ids = rooms_available
|
||||
|
||||
@@ -1098,10 +1101,11 @@ class PmsReservation(models.Model):
|
||||
}
|
||||
|
||||
def open_reservation_wizard(self):
|
||||
rooms_available = self.env["pms.room.type.availability"].rooms_available(
|
||||
rooms_available = self.env["pms.room.type.restriction"].rooms_available(
|
||||
checkin=self.checkin,
|
||||
checkout=self.checkout,
|
||||
current_lines=self.reservation_line_ids.ids,
|
||||
pricelist=self.pricelist_id.id,
|
||||
)
|
||||
# REVIEW: check capacity room
|
||||
return {
|
||||
@@ -1180,10 +1184,11 @@ class PmsReservation(models.Model):
|
||||
def _autoassign(self):
|
||||
self.ensure_one()
|
||||
room_chosen = False
|
||||
rooms_available = self.env["pms.room.type.availability"].rooms_available(
|
||||
rooms_available = self.env["pms.room.type.restriction"].rooms_available(
|
||||
checkin=self.checkin,
|
||||
checkout=self.checkout,
|
||||
room_type_id=self.room_type_id.id or False,
|
||||
pricelist=self.pricelist_id.id,
|
||||
)
|
||||
if rooms_available:
|
||||
room_chosen = rooms_available[0]
|
||||
|
||||
@@ -80,6 +80,12 @@ class PmsReservationLine(models.Model):
|
||||
store=True,
|
||||
help="This record is taken into account to calculate availability",
|
||||
)
|
||||
impacts_quota = fields.Integer(
|
||||
string="Impacts quota",
|
||||
compute="_compute_impact_quota",
|
||||
store=True,
|
||||
readonly=False,
|
||||
)
|
||||
|
||||
_sql_constraints = [
|
||||
(
|
||||
@@ -102,17 +108,15 @@ 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.availability"
|
||||
].rooms_available(
|
||||
checkin=reservation.checkin,
|
||||
checkout=reservation.checkout,
|
||||
rooms_available = self.env["pms.room.type.restriction"].rooms_available(
|
||||
checkin=line.reservation_id.checkin,
|
||||
checkout=line.reservation_id.checkout,
|
||||
room_type_id=reservation.room_type_id.id
|
||||
if not free_room_select
|
||||
else False,
|
||||
current_lines=line._origin.reservation_id.reservation_line_ids.ids,
|
||||
pricelist=line.reservation_id.pricelist_id.id,
|
||||
)
|
||||
|
||||
# if there is availability for the entire stay
|
||||
if rooms_available:
|
||||
|
||||
@@ -134,9 +138,20 @@ class PmsReservationLine(models.Model):
|
||||
# available for the entire stay
|
||||
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(
|
||||
checkin=line.reservation_id.checkin,
|
||||
checkout=line.reservation_id.checkout,
|
||||
room_type_id=line.reservation_id.room_type_id.id,
|
||||
current_lines=line._origin.reservation_id.reservation_line_ids.ids,
|
||||
pricelist=line.reservation_id.pricelist_id,
|
||||
):
|
||||
raise ValidationError(
|
||||
_("%s: No room type available")
|
||||
% (line.reservation_id.room_type_id.name)
|
||||
)
|
||||
|
||||
# if there is no availability for the entire stay without
|
||||
# changing rooms (we assume a split reservation)
|
||||
# the reservation can be allocated into several rooms
|
||||
else:
|
||||
rooms_ranking = dict()
|
||||
|
||||
@@ -173,12 +188,8 @@ class PmsReservationLine(models.Model):
|
||||
if room.id not in rooms_ranking
|
||||
else rooms_ranking[room.id] + 1
|
||||
)
|
||||
if len(rooms_ranking) == 0:
|
||||
raise ValidationError(
|
||||
_("%s: No room type available")
|
||||
% (reservation.room_type_id.name)
|
||||
)
|
||||
else:
|
||||
|
||||
if len(rooms_ranking) > 0:
|
||||
# we get the best score in the ranking
|
||||
best = max(rooms_ranking.values())
|
||||
|
||||
@@ -218,6 +229,17 @@ class PmsReservationLine(models.Model):
|
||||
# no matter what it is
|
||||
line.room_id = list(bests.keys())[0]
|
||||
|
||||
@api.depends("reservation_id.room_type_id", "reservation_id.pricelist_id")
|
||||
def _compute_impact_quota(self):
|
||||
for line in self:
|
||||
reservation = line.reservation_id
|
||||
line.impacts_quota = self.env["pms.room.type.restriction"].update_quota(
|
||||
pricelist_id=reservation.pricelist_id,
|
||||
room_type_id=reservation.room_type_id,
|
||||
date=line.date,
|
||||
line=line,
|
||||
)
|
||||
|
||||
@api.depends(
|
||||
"reservation_id",
|
||||
"reservation_id.pricelist_id",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 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
|
||||
|
||||
|
||||
@@ -18,20 +20,176 @@ class PmsRoomTypeRestriction(models.Model):
|
||||
# Fields declaration
|
||||
name = fields.Char("Restriction Plan Name", required=True)
|
||||
pms_property_id = fields.Many2one(
|
||||
"pms.property",
|
||||
"Property",
|
||||
comodel_name="pms.property",
|
||||
string="Property",
|
||||
ondelete="restrict",
|
||||
default=_get_default_pms_property,
|
||||
)
|
||||
|
||||
pms_pricelist_ids = fields.One2many(
|
||||
comodel_name="product.pricelist",
|
||||
inverse_name="restriction_id",
|
||||
string="Pricelists",
|
||||
required=False,
|
||||
ondelete="restrict",
|
||||
)
|
||||
|
||||
item_ids = fields.One2many(
|
||||
"pms.room.type.restriction.item",
|
||||
"restriction_id",
|
||||
comodel_name="pms.room.type.restriction.item",
|
||||
inverse_name="restriction_id",
|
||||
string="Restriction Items",
|
||||
copy=True,
|
||||
)
|
||||
|
||||
active = fields.Boolean(
|
||||
"Active",
|
||||
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
|
||||
|
||||
@@ -9,21 +9,64 @@ class PmsRoomTypeRestrictionItem(models.Model):
|
||||
_description = "Reservation restriction by day"
|
||||
|
||||
# Field Declarations
|
||||
|
||||
restriction_id = fields.Many2one(
|
||||
"pms.room.type.restriction", "Restriction Plan", ondelete="cascade", index=True
|
||||
comodel_name="pms.room.type.restriction",
|
||||
string="Restriction Plan",
|
||||
ondelete="cascade",
|
||||
index=True,
|
||||
)
|
||||
room_type_id = fields.Many2one(
|
||||
"pms.room.type", "Room Type", required=True, ondelete="cascade"
|
||||
comodel_name="pms.room.type",
|
||||
string="Room Type",
|
||||
required=True,
|
||||
ondelete="cascade",
|
||||
)
|
||||
date = fields.Date("Date")
|
||||
date = fields.Date(string="Date")
|
||||
|
||||
min_stay = fields.Integer("Min. Stay")
|
||||
min_stay_arrival = fields.Integer("Min. Stay Arrival")
|
||||
max_stay = fields.Integer("Max. Stay")
|
||||
max_stay_arrival = fields.Integer("Max. Stay Arrival")
|
||||
closed = fields.Boolean("Closed")
|
||||
closed_departure = fields.Boolean("Closed Departure")
|
||||
closed_arrival = fields.Boolean("Closed Arrival")
|
||||
min_stay = fields.Integer(
|
||||
string="Min. Stay",
|
||||
default=0,
|
||||
)
|
||||
min_stay_arrival = fields.Integer(
|
||||
string="Min. Stay Arrival",
|
||||
default=0,
|
||||
)
|
||||
max_stay = fields.Integer(
|
||||
string="Max. Stay",
|
||||
default=0,
|
||||
)
|
||||
max_stay_arrival = fields.Integer(
|
||||
string="Max. Stay Arrival",
|
||||
default=0,
|
||||
)
|
||||
closed = fields.Boolean(
|
||||
string="Closed",
|
||||
default=False,
|
||||
)
|
||||
closed_departure = fields.Boolean(
|
||||
string="Closed Departure",
|
||||
default=False,
|
||||
)
|
||||
closed_arrival = fields.Boolean(
|
||||
string="Closed Arrival",
|
||||
default=False,
|
||||
)
|
||||
quota = fields.Integer(
|
||||
string="Quota",
|
||||
store=True,
|
||||
readonly=False,
|
||||
compute="_compute_quota",
|
||||
help="Generic Quota assigned.",
|
||||
)
|
||||
|
||||
max_avail = fields.Integer(
|
||||
string="Max. Availability",
|
||||
store=True,
|
||||
readonly=False,
|
||||
compute="_compute_max_avail",
|
||||
help="Maximum simultaneous availability on own Booking Engine.",
|
||||
)
|
||||
|
||||
_sql_constraints = [
|
||||
(
|
||||
@@ -34,10 +77,20 @@ class PmsRoomTypeRestrictionItem(models.Model):
|
||||
)
|
||||
]
|
||||
|
||||
# Constraints and onchanges
|
||||
@api.depends("room_type_id")
|
||||
def _compute_quota(self):
|
||||
for record in self:
|
||||
if not record.quota:
|
||||
record.quota = record.room_type_id.default_quota
|
||||
|
||||
@api.depends("room_type_id")
|
||||
def _compute_max_avail(self):
|
||||
for record in self:
|
||||
if not record.max_avail:
|
||||
record.max_avail = record.room_type_id.default_max_avail
|
||||
|
||||
@api.constrains("min_stay", "min_stay_arrival", "max_stay", "max_stay_arrival")
|
||||
def _check_min_stay(self):
|
||||
def _check_min_max_stay(self):
|
||||
for record in self:
|
||||
if record.min_stay < 0:
|
||||
raise ValidationError(_("Min. Stay can't be less than zero"))
|
||||
@@ -47,3 +100,17 @@ class PmsRoomTypeRestrictionItem(models.Model):
|
||||
raise ValidationError(_("Max. Stay can't be less than zero"))
|
||||
elif record.max_stay_arrival < 0:
|
||||
raise ValidationError(_("Max. Stay Arrival can't be less than zero"))
|
||||
elif (
|
||||
record.min_stay != 0
|
||||
and record.max_stay != 0
|
||||
and record.min_stay > record.max_stay
|
||||
):
|
||||
raise ValidationError(_("Max. Stay can't be less than Min. Stay"))
|
||||
elif (
|
||||
record.min_stay_arrival != 0
|
||||
and record.max_stay_arrival != 0
|
||||
and record.min_stay_arrival > record.max_stay_arrival
|
||||
):
|
||||
raise ValidationError(
|
||||
_("Max. Stay Arrival can't be less than Min. Stay Arrival")
|
||||
)
|
||||
|
||||
@@ -22,6 +22,12 @@ class ProductPricelist(models.Model):
|
||||
[("daily", "Daily Plan")], string="Pricelist Type", default="daily"
|
||||
)
|
||||
|
||||
restriction_id = fields.Many2one(
|
||||
comodel_name="pms.room.type.restriction",
|
||||
string="restriction",
|
||||
ondelete="restrict",
|
||||
)
|
||||
|
||||
# Constraints and onchanges
|
||||
# @api.constrains("pricelist_type", "pms_property_ids")
|
||||
# def _check_pricelist_type_property_ids(self):
|
||||
|
||||
@@ -25,3 +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
|
||||
|
||||
574
pms/tests/test_pms_room_type_restriction.py
Normal file
574
pms/tests/test_pms_room_type_restriction.py
Normal file
@@ -0,0 +1,574 @@
|
||||
import datetime
|
||||
|
||||
from _pytest.skipping import Skip
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo import fields
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
from .common import TestHotel
|
||||
|
||||
|
||||
@freeze_time("1980-01-01")
|
||||
class TestPmsRoomTypeRestriction(TestHotel):
|
||||
def create_common_scenario(self):
|
||||
# product.pricelist
|
||||
self.test_pricelist1 = self.env["product.pricelist"].create(
|
||||
{
|
||||
"name": "test pricelist 1",
|
||||
}
|
||||
)
|
||||
# pms.room.type.restriction
|
||||
self.test_room_type_restriction1 = self.env["pms.room.type.restriction"].create(
|
||||
{
|
||||
"name": "Restriction plan for TEST",
|
||||
"pms_pricelist_ids": [(6, 0, [self.test_pricelist1.id])],
|
||||
}
|
||||
)
|
||||
# pms.property
|
||||
self.test_property = self.env["pms.property"].create(
|
||||
{
|
||||
"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,
|
||||
}
|
||||
)
|
||||
# pms.room.type.class
|
||||
self.test_room_type_class = self.env["pms.room.type.class"].create(
|
||||
{"name": "Room"}
|
||||
)
|
||||
|
||||
# pms.room.type
|
||||
self.test_room_type_single = self.env["pms.room.type"].create(
|
||||
{
|
||||
"pms_property_ids": [self.test_property.id],
|
||||
"name": "Single Test",
|
||||
"code_type": "SNG_Test",
|
||||
"class_id": self.test_room_type_class.id,
|
||||
}
|
||||
)
|
||||
# pms.room.type
|
||||
self.test_room_type_double = self.env["pms.room.type"].create(
|
||||
{
|
||||
"pms_property_ids": [self.test_property.id],
|
||||
"name": "Double Test",
|
||||
"code_type": "DBL_Test",
|
||||
"class_id": self.test_room_type_class.id,
|
||||
}
|
||||
)
|
||||
# pms.room
|
||||
self.test_room1_double = self.env["pms.room"].create(
|
||||
{
|
||||
"pms_property_id": self.test_property.id,
|
||||
"name": "Double 201 test",
|
||||
"room_type_id": self.test_room_type_double.id,
|
||||
"capacity": 2,
|
||||
}
|
||||
)
|
||||
# pms.room
|
||||
self.test_room2_double = self.env["pms.room"].create(
|
||||
{
|
||||
"pms_property_id": self.test_property.id,
|
||||
"name": "Double 202 test",
|
||||
"room_type_id": self.test_room_type_double.id,
|
||||
"capacity": 2,
|
||||
}
|
||||
)
|
||||
# pms.room
|
||||
# self.test_room3_double = self.env["pms.room"].create(
|
||||
# {
|
||||
# "pms_property_id": self.test_property.id,
|
||||
# "name": "Double 203 test",
|
||||
# "room_type_id": self.test_room_type_double.id,
|
||||
# "capacity": 2,
|
||||
# }
|
||||
# )
|
||||
# # pms.room
|
||||
# self.test_room4_double = self.env["pms.room"].create(
|
||||
# {
|
||||
# "pms_property_id": self.test_property.id,
|
||||
# "name": "Double 204 test",
|
||||
# "room_type_id": self.test_room_type_double.id,
|
||||
# "capacity": 2,
|
||||
# }
|
||||
# )
|
||||
# pms.room
|
||||
self.test_room1_single = self.env["pms.room"].create(
|
||||
{
|
||||
"pms_property_id": self.test_property.id,
|
||||
"name": "Single 101 test",
|
||||
"room_type_id": self.test_room_type_single.id,
|
||||
"capacity": 1,
|
||||
}
|
||||
)
|
||||
# pms.room
|
||||
self.test_room2_single = self.env["pms.room"].create(
|
||||
{
|
||||
"pms_property_id": self.test_property.id,
|
||||
"name": "Single 102 test",
|
||||
"room_type_id": self.test_room_type_single.id,
|
||||
"capacity": 1,
|
||||
}
|
||||
)
|
||||
|
||||
@Skip
|
||||
def test_availability_rooms_all(self):
|
||||
# TEST CASE
|
||||
# get availability withouth restrictions
|
||||
|
||||
# ARRANGE
|
||||
self.create_common_scenario()
|
||||
|
||||
checkin = fields.date.today()
|
||||
checkout = (fields.datetime.today() + datetime.timedelta(days=4)).date()
|
||||
test_rooms_double_rooms = self.env["pms.room"].search(
|
||||
[("pms_property_id", "=", self.test_property.id)]
|
||||
)
|
||||
|
||||
# ACT
|
||||
result = self.env["pms.room.type.restriction"].rooms_available(
|
||||
checkin=checkin,
|
||||
checkout=checkout,
|
||||
)
|
||||
# ASSERT
|
||||
obtained = all(elem.id in result.ids for elem in test_rooms_double_rooms)
|
||||
self.assertTrue(
|
||||
obtained,
|
||||
"Availability should contain the test rooms"
|
||||
"because there's no restriction for them.",
|
||||
)
|
||||
|
||||
@Skip
|
||||
def test_availability_rooms_all_lines(self):
|
||||
# TEST CASE
|
||||
# get availability withouth restrictions
|
||||
# given reservation lines to not consider
|
||||
|
||||
# ARRANGE
|
||||
self.create_common_scenario()
|
||||
checkin = fields.date.today()
|
||||
checkout = (fields.datetime.today() + datetime.timedelta(days=4)).date()
|
||||
test_rooms_double_rooms = self.env["pms.room"].search(
|
||||
[("pms_property_id", "=", self.test_property.id)]
|
||||
)
|
||||
test_reservation = self.env["pms.reservation"].create(
|
||||
{
|
||||
"pms_property_id": self.test_property.id,
|
||||
"checkin": checkin,
|
||||
"checkout": checkout,
|
||||
}
|
||||
)
|
||||
|
||||
# ACT
|
||||
result = self.env["pms.room.type.restriction"].rooms_available(
|
||||
checkin=checkin,
|
||||
checkout=checkout,
|
||||
current_lines=test_reservation.reservation_line_ids.ids,
|
||||
)
|
||||
# ASSERT
|
||||
obtained = all(elem.id in result.ids for elem in test_rooms_double_rooms)
|
||||
self.assertTrue(
|
||||
obtained,
|
||||
"Availability should contain the test rooms"
|
||||
"because there's no restriction for them.",
|
||||
)
|
||||
|
||||
@Skip
|
||||
def test_availability_rooms_room_type(self):
|
||||
# TEST CASE
|
||||
# get availability withouth restrictions
|
||||
# given a room type
|
||||
|
||||
# ARRANGE
|
||||
self.create_common_scenario()
|
||||
test_rooms_double_rooms = self.env["pms.room"].search(
|
||||
[
|
||||
("pms_property_id", "=", self.test_property.id),
|
||||
("room_type_id", "=", self.test_room_type_double.id),
|
||||
]
|
||||
)
|
||||
|
||||
# ACT
|
||||
result = self.env["pms.room.type.restriction"].rooms_available(
|
||||
checkin=fields.date.today(),
|
||||
checkout=(fields.datetime.today() + datetime.timedelta(days=4)).date(),
|
||||
room_type_id=self.test_room_type_double.id,
|
||||
)
|
||||
|
||||
# ASSERT
|
||||
obtained = all(elem.id in result.ids for elem in test_rooms_double_rooms)
|
||||
self.assertTrue(
|
||||
obtained,
|
||||
"Availability should contain the test rooms"
|
||||
"because there's no restriction 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
|
||||
|
||||
# ARRANGE
|
||||
self.create_common_scenario()
|
||||
self.test_room_type_restriction1_item1 = self.env[
|
||||
"pms.room.type.restriction.item"
|
||||
].create(
|
||||
{
|
||||
"restriction_id": self.test_room_type_restriction1.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(
|
||||
checkin=fields.date.today(),
|
||||
checkout=(fields.datetime.today() + datetime.timedelta(days=4)).date(),
|
||||
# room_type_id=False, # <- (2/2)
|
||||
pricelist=self.test_pricelist1.id,
|
||||
)
|
||||
# ASSERT
|
||||
self.assertNotIn(
|
||||
self.test_room_type_double,
|
||||
result.mapped("room_type_id"),
|
||||
"Availability should not contain rooms of a type "
|
||||
"which its restriction rules applies",
|
||||
)
|
||||
|
||||
@Skip
|
||||
def test_availability_restrictions(self):
|
||||
# TEST CASE
|
||||
# the availability should take into acount restriction 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"
|
||||
].create(
|
||||
{
|
||||
"restriction_id": self.test_room_type_restriction1.id,
|
||||
"room_type_id": self.test_room_type_double.id,
|
||||
"date": (fields.datetime.today() + datetime.timedelta(days=0)).date(),
|
||||
}
|
||||
)
|
||||
|
||||
checkin = fields.date.today()
|
||||
checkout = (fields.datetime.today() + datetime.timedelta(days=4)).date()
|
||||
|
||||
test_cases = [
|
||||
{
|
||||
"closed": False,
|
||||
"closed_arrival": True,
|
||||
"closed_departure": False,
|
||||
"min_stay": 0,
|
||||
"max_stay": 0,
|
||||
"min_stay_arrival": 0,
|
||||
"max_stay_arrival": 0,
|
||||
"quota": -1,
|
||||
"max_avail": -1,
|
||||
"date": checkin,
|
||||
},
|
||||
{
|
||||
"closed": False,
|
||||
"closed_arrival": False,
|
||||
"closed_departure": True,
|
||||
"min_stay": 0,
|
||||
"max_stay": 0,
|
||||
"min_stay_arrival": 0,
|
||||
"max_stay_arrival": 0,
|
||||
"quota": -1,
|
||||
"max_avail": -1,
|
||||
"date": checkout,
|
||||
},
|
||||
{
|
||||
"closed": False,
|
||||
"closed_arrival": False,
|
||||
"closed_departure": False,
|
||||
"min_stay": 5,
|
||||
"max_stay": 0,
|
||||
"min_stay_arrival": 0,
|
||||
"max_stay_arrival": 0,
|
||||
"quota": -1,
|
||||
"max_avail": -1,
|
||||
"date": checkin,
|
||||
},
|
||||
{
|
||||
"closed": False,
|
||||
"closed_arrival": False,
|
||||
"closed_departure": False,
|
||||
"min_stay": 0,
|
||||
"max_stay": 2,
|
||||
"min_stay_arrival": 0,
|
||||
"max_stay_arrival": 0,
|
||||
"quota": -1,
|
||||
"max_avail": -1,
|
||||
"date": checkin,
|
||||
},
|
||||
{
|
||||
"closed": False,
|
||||
"closed_arrival": False,
|
||||
"closed_departure": False,
|
||||
"min_stay": 0,
|
||||
"max_stay": 0,
|
||||
"min_stay_arrival": 5,
|
||||
"max_stay_arrival": 0,
|
||||
"quota": -1,
|
||||
"max_avail": -1,
|
||||
"date": checkin,
|
||||
},
|
||||
{
|
||||
"closed": False,
|
||||
"closed_arrival": False,
|
||||
"closed_departure": False,
|
||||
"min_stay": 0,
|
||||
"max_stay": 0,
|
||||
"min_stay_arrival": 0,
|
||||
"max_stay_arrival": 3,
|
||||
"quota": -1,
|
||||
"max_avail": -1,
|
||||
"date": checkin,
|
||||
},
|
||||
{
|
||||
"closed": False,
|
||||
"closed_arrival": False,
|
||||
"closed_departure": False,
|
||||
"min_stay": 0,
|
||||
"max_stay": 0,
|
||||
"min_stay_arrival": 0,
|
||||
"max_stay_arrival": 0,
|
||||
"quota": 0,
|
||||
"max_avail": -1,
|
||||
"date": checkin,
|
||||
},
|
||||
{
|
||||
"closed": False,
|
||||
"closed_arrival": False,
|
||||
"closed_departure": False,
|
||||
"min_stay": 0,
|
||||
"max_stay": 0,
|
||||
"min_stay_arrival": 0,
|
||||
"max_stay_arrival": 0,
|
||||
"quota": -1,
|
||||
"max_avail": 0,
|
||||
"date": checkin,
|
||||
},
|
||||
]
|
||||
|
||||
for test_case in test_cases:
|
||||
with self.subTest(k=test_case):
|
||||
|
||||
# ACT
|
||||
self.test_room_type_restriction1_item1.write(test_case)
|
||||
|
||||
result = self.env["pms.room.type.restriction"].rooms_available(
|
||||
checkin=checkin,
|
||||
checkout=checkout,
|
||||
room_type_id=self.test_room_type_double.id,
|
||||
pricelist=self.test_pricelist1.id,
|
||||
)
|
||||
|
||||
# ASSERT
|
||||
self.assertNotIn(
|
||||
self.test_room_type_double,
|
||||
result.mapped("room_type_id"),
|
||||
"Availability should not contain rooms of a type "
|
||||
"which its restriction rules applies",
|
||||
)
|
||||
|
||||
@Skip
|
||||
@freeze_time("1980-11-01")
|
||||
def test_restriction_on_create_reservation(self):
|
||||
# TEST CASE
|
||||
# a restriction 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"
|
||||
].create(
|
||||
{
|
||||
"restriction_id": self.test_room_type_restriction1.id,
|
||||
"room_type_id": self.test_room_type_double.id,
|
||||
"date": (fields.datetime.today() + datetime.timedelta(days=2)).date(),
|
||||
"closed": True,
|
||||
}
|
||||
)
|
||||
|
||||
checkin = datetime.datetime.now()
|
||||
checkout = datetime.datetime.now() + datetime.timedelta(days=4)
|
||||
|
||||
# ACT & ASSERT
|
||||
with self.assertRaises(
|
||||
ValidationError,
|
||||
msg="Restriction should be applied that would"
|
||||
" prevent the creation of the reservation.",
|
||||
):
|
||||
self.env["pms.reservation"].create(
|
||||
{
|
||||
"pms_property_id": self.test_property.id,
|
||||
"checkin": checkin,
|
||||
"checkout": checkout,
|
||||
"adults": 2,
|
||||
"room_type_id": self.test_room_type_double.id,
|
||||
"pricelist_id": self.test_pricelist1.id,
|
||||
}
|
||||
)
|
||||
|
||||
@Skip
|
||||
@freeze_time("1980-11-01")
|
||||
def test_restriction_on_create_splitted_reservation(self):
|
||||
# TEST CASE
|
||||
# a restriction 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"
|
||||
].create(
|
||||
{
|
||||
"restriction_id": self.test_room_type_restriction1.id,
|
||||
"room_type_id": self.test_room_type_double.id,
|
||||
"date": (fields.datetime.today() + datetime.timedelta(days=2)).date(),
|
||||
"closed": True,
|
||||
}
|
||||
)
|
||||
|
||||
checkin_test = datetime.datetime.now()
|
||||
checkout_test = datetime.datetime.now() + datetime.timedelta(days=4)
|
||||
|
||||
self.env["pms.reservation"].create(
|
||||
{
|
||||
"pms_property_id": self.test_property.id,
|
||||
"checkin": datetime.datetime.now(),
|
||||
"checkout": datetime.datetime.now() + datetime.timedelta(days=2),
|
||||
"adults": 2,
|
||||
"room_type_id": self.test_room_type_double.id,
|
||||
"preferred_room_id": self.test_room1_double.id,
|
||||
}
|
||||
)
|
||||
|
||||
self.env["pms.reservation"].create(
|
||||
{
|
||||
"pms_property_id": self.test_property.id,
|
||||
"checkin": datetime.datetime.now() + datetime.timedelta(days=2),
|
||||
"checkout": datetime.datetime.now() + datetime.timedelta(days=4),
|
||||
"adults": 2,
|
||||
"room_type_id": self.test_room_type_double.id,
|
||||
"preferred_room_id": self.test_room2_double.id,
|
||||
}
|
||||
)
|
||||
|
||||
# ACT & ASSERT
|
||||
with self.assertRaises(
|
||||
ValidationError,
|
||||
msg="Restriction should be applied that would"
|
||||
" prevent the creation of splitted reservation.",
|
||||
):
|
||||
self.env["pms.reservation"].create(
|
||||
{
|
||||
"pms_property_id": self.test_property.id,
|
||||
"checkin": checkin_test,
|
||||
"checkout": checkout_test,
|
||||
"adults": 2,
|
||||
"room_type_id": self.test_room_type_double.id,
|
||||
"pricelist_id": self.test_pricelist1.id,
|
||||
}
|
||||
)
|
||||
|
||||
@Skip
|
||||
@freeze_time("1980-11-01")
|
||||
def test_restriction_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
|
||||
|
||||
# ARRANGE
|
||||
self.create_common_scenario()
|
||||
|
||||
self.test_room_type_restriction1_item1 = self.env[
|
||||
"pms.room.type.restriction.item"
|
||||
].create(
|
||||
{
|
||||
"restriction_id": self.test_room_type_restriction1.id,
|
||||
"room_type_id": self.test_room_type_double.id,
|
||||
"date": datetime.date.today(),
|
||||
"quota": 1,
|
||||
}
|
||||
)
|
||||
r1 = self.env["pms.reservation"].create(
|
||||
{
|
||||
"pms_property_id": self.test_property.id,
|
||||
"checkin": datetime.date.today(),
|
||||
"checkout": datetime.date.today() + datetime.timedelta(days=1),
|
||||
"adults": 2,
|
||||
"room_type_id": self.test_room_type_double.id,
|
||||
"pricelist_id": self.test_pricelist1.id,
|
||||
}
|
||||
)
|
||||
r1.flush()
|
||||
with self.assertRaises(
|
||||
ValidationError,
|
||||
msg="The quota shouldnt be enough to create a new reservation",
|
||||
):
|
||||
self.env["pms.reservation"].create(
|
||||
{
|
||||
"pms_property_id": self.test_property.id,
|
||||
"checkin": datetime.date.today(),
|
||||
"checkout": datetime.date.today() + datetime.timedelta(days=1),
|
||||
"adults": 2,
|
||||
"room_type_id": self.test_room_type_double.id,
|
||||
"pricelist_id": self.test_pricelist1.id,
|
||||
}
|
||||
)
|
||||
|
||||
@freeze_time("1980-11-01")
|
||||
def test_restriction_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
|
||||
# and then modify the pricelist of the reservation and
|
||||
# no restriction applies
|
||||
|
||||
# ARRANGE
|
||||
self.create_common_scenario()
|
||||
test_quota = 2
|
||||
test_pricelist2 = self.env["product.pricelist"].create(
|
||||
{
|
||||
"name": "test pricelist 2",
|
||||
}
|
||||
)
|
||||
restriction = self.env["pms.room.type.restriction.item"].create(
|
||||
{
|
||||
"restriction_id": self.test_room_type_restriction1.id,
|
||||
"room_type_id": self.test_room_type_double.id,
|
||||
"date": datetime.date.today(),
|
||||
"quota": test_quota,
|
||||
}
|
||||
)
|
||||
reservation = self.env["pms.reservation"].create(
|
||||
{
|
||||
"pms_property_id": self.test_property.id,
|
||||
"checkin": datetime.date.today(),
|
||||
"checkout": datetime.date.today() + datetime.timedelta(days=1),
|
||||
"adults": 2,
|
||||
"room_type_id": self.test_room_type_double.id,
|
||||
"pricelist_id": self.test_pricelist1.id,
|
||||
}
|
||||
)
|
||||
|
||||
# ACT
|
||||
reservation.pricelist_id = test_pricelist2.id
|
||||
reservation.flush()
|
||||
self.assertEqual(
|
||||
test_quota,
|
||||
restriction.quota,
|
||||
"The quota should be restored after changing the reservation's pricelist",
|
||||
)
|
||||
@@ -7,15 +7,20 @@
|
||||
<form string="Restrictions">
|
||||
<group>
|
||||
<field name="room_type_id" required="True" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="date" />
|
||||
</group>
|
||||
<group>
|
||||
<group>
|
||||
<field name="min_stay" />
|
||||
<field name="min_stay_arrival" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="max_stay" />
|
||||
<field name="max_stay_arrival" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="quota" />
|
||||
<field name="max_avail" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="closed" />
|
||||
|
||||
@@ -26,21 +26,24 @@
|
||||
<field name="name" />
|
||||
</h1>
|
||||
</div>
|
||||
<div>
|
||||
<group>
|
||||
<field
|
||||
name="pms_property_id"
|
||||
options="{'no_create': True,'no_open': True}"
|
||||
/>
|
||||
<separator string="Restriction Items" />
|
||||
<field name="item_ids" nolabel="1">
|
||||
<tree string="Restriction Items">
|
||||
<field name="room_type_id" />
|
||||
<field name="date" />
|
||||
<field name="min_stay" />
|
||||
<field name="closed" />
|
||||
</tree>
|
||||
</field>
|
||||
</div>
|
||||
<field
|
||||
name="pms_pricelist_ids"
|
||||
widget="many2many_tags"
|
||||
options="{'no_create': True,'no_open': True}"
|
||||
/>
|
||||
</group>
|
||||
<separator string="Restriction Items" />
|
||||
<field name="item_ids" nolabel="1">
|
||||
<tree string="Restriction Items">
|
||||
<field name="room_type_id" />
|
||||
<field name="date" />
|
||||
</tree>
|
||||
</field>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
@@ -51,6 +54,12 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Restrictions">
|
||||
<field name="name" />
|
||||
<field name="pms_property_id" />
|
||||
<field
|
||||
name="pms_pricelist_ids"
|
||||
widget="many2many_tags"
|
||||
options="{'no_create': True,'no_open': True}"
|
||||
/>
|
||||
<field name="active" />
|
||||
</tree>
|
||||
</field>
|
||||
|
||||
Reference in New Issue
Block a user