[IMP] pms: availability plans

This commit is contained in:
miguelpadin
2020-11-30 17:27:59 +01:00
committed by Eric Antones
parent b218086024
commit 6080db54be
9 changed files with 898 additions and 51 deletions

View File

@@ -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]

View File

@@ -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",

View File

@@ -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

View File

@@ -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")
)

View File

@@ -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):

View File

@@ -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

View 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",
)

View File

@@ -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" />

View File

@@ -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>