diff --git a/pms/__init__.py b/pms/__init__.py
index 8197a1c05..22280bdac 100644
--- a/pms/__init__.py
+++ b/pms/__init__.py
@@ -1,6 +1,5 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import models
+from . import wizards
from .init_hook import post_init_hook
-
-# from . import wizard
diff --git a/pms/__manifest__.py b/pms/__manifest__.py
index bddefb43f..b541bb84a 100644
--- a/pms/__manifest__.py
+++ b/pms/__manifest__.py
@@ -63,6 +63,7 @@
"views/product_template_views.xml",
"views/webclient_templates.xml",
"views/ir_sequence_views.xml",
+ "wizards/wizard_reservation.xml",
],
"demo": [
"demo/pms_master_data.xml",
diff --git a/pms/demo/pms_folio.xml b/pms/demo/pms_folio.xml
index 10a4c7108..be9214c38 100644
--- a/pms/demo/pms_folio.xml
+++ b/pms/demo/pms_folio.xml
@@ -213,7 +213,7 @@
name="reservation_ids"
eval="[(5, 0), (0, 0, {
'pricelist_id': ref('product.list0'),
- 'room_id': ref('pms_room_1'),
+ 'preferred_room_id': ref('pms_room_1'),
'checkin': (DateTime.today() + timedelta(days=19)),
'checkout': (DateTime.today() + timedelta(days=20)),
'adults': 1,
@@ -233,7 +233,6 @@
(0, 0, {
'pricelist_id': ref('product.list0'),
'room_type_id': ref('pms_room_type_2'),
- 'room_id': ref('pms_room_4'),
'checkin': (DateTime.today() + timedelta(days=19)),
'checkout': (DateTime.today() + timedelta(days=20)),
'adults': 2,
@@ -303,7 +302,7 @@
name="reservation_ids"
eval="[(5, 0), (0, 0, {
'pricelist_id': ref('product.list0'),
- 'room_id': ref('pms_room_0'),
+ 'preferred_room_id': ref('pms_room_0'),
'checkin': (DateTime.today() + timedelta(days=19)),
'checkout': (DateTime.today() + timedelta(days=20)),
'adults': 1,
@@ -319,7 +318,7 @@
name="reservation_ids"
eval="[(5, 0), (0, 0, {
'pricelist_id': ref('product.list0'),
- 'room_id': ref('pms_room_7'),
+ 'preferred_room_id': ref('pms_room_7'),
'checkin': (DateTime.today() + timedelta(days=21)),
'checkout': (DateTime.today() + timedelta(days=22)),
'adults': 1,
@@ -370,7 +369,6 @@
eval="[(5, 0), (0, 0, {
'pricelist_id': ref('product.list0'),
'room_type_id': ref('pms_room_type_2'),
- 'room_id': ref('pms_room_4'),
'checkin': (DateTime.today() + timedelta(days=21)),
'checkout': (DateTime.today() + timedelta(days=22)),
'adults': 1,
@@ -391,7 +389,7 @@
name="reservation_ids"
eval="[(5, 0), (0, 0, {
'pricelist_id': ref('product.list0'),
- 'room_id': ref('pms_room_6'),
+ 'preferred_room_id': ref('pms_room_6'),
'checkin': (DateTime.today() + timedelta(days=20)),
'checkout': (DateTime.today() + timedelta(days=21)),
'adults': 3,
@@ -408,7 +406,6 @@
(0, 0, {
'pricelist_id': ref('product.list0'),
'room_type_id': ref('pms_room_type_2'),
- 'room_id': ref('pms_room_4'),
'checkin': (DateTime.today() + timedelta(days=23)),
'checkout': (DateTime.today() + timedelta(days=24)),
'adults': 1,
@@ -526,7 +523,6 @@
}),
(0, 0, {
'pricelist_id': ref('product.list0'),
- 'room_id': ref('pms_room_5'),
'checkin': (DateTime.today() + timedelta(days=24)),
'checkout': (DateTime.today() + timedelta(days=25)),
'adults': 1,
@@ -544,7 +540,7 @@
eval="[(5, 0),
(0, 0, {
'pricelist_id': ref('product.list0'),
- 'room_id': ref('pms_room_7'),
+ 'preferred_room_id': ref('pms_room_7'),
'checkin': (DateTime.today() + timedelta(days=24)),
'checkout': (DateTime.today() + timedelta(days=25)),
'adults': 1,
@@ -563,7 +559,7 @@
eval="[(5, 0),
(0, 0, {
'pricelist_id': ref('product.list0'),
- 'room_id': ref('pms_room_0'),
+ 'preferred_room_id': ref('pms_room_0'),
'checkin': (DateTime.today() + timedelta(days=25)),
'checkout': (DateTime.today() + timedelta(days=32)),
'adults': 1,
@@ -580,7 +576,7 @@
(0, 0, {
'pricelist_id': ref('product.list0'),
'room_type_id': ref('pms_room_type_1'),
- 'room_id': ref('pms_room_1'),
+ 'preferred_room_id': ref('pms_room_1'),
'checkin': (DateTime.today() + timedelta(days=25)),
'checkout': (DateTime.today() + timedelta(days=32)),
'adults': 1,
@@ -596,7 +592,7 @@
eval="[(5, 0),
(0, 0, {
'pricelist_id': ref('product.list0'),
- 'room_id': ref('pms_room_2'),
+ 'preferred_room_id': ref('pms_room_2'),
'checkin': (DateTime.today() + timedelta(days=25)),
'checkout': (DateTime.today() + timedelta(days=32)),
'adults': 1,
@@ -614,7 +610,6 @@
(0, 0, {
'pricelist_id': ref('product.list0'),
'room_type_id': ref('pms_room_type_2'),
- 'room_id': ref('pms_room_5'),
'checkin': (DateTime.today() + timedelta(days=25)),
'checkout': (DateTime.today() + timedelta(days=31)),
'adults': 2,
@@ -637,7 +632,7 @@
name="reservation_ids"
eval="[(5, 0), (0, 0, {
'pricelist_id': ref('product.list0'),
- 'room_id': ref('pms_room_7'),
+ 'preferred_room_id': ref('pms_room_7'),
'checkin': (DateTime.today() + timedelta(days=25)),
'checkout': (DateTime.today() + timedelta(days=26)),
'adults': 1,
@@ -689,7 +684,7 @@
eval="[(5, 0), (0, 0, {
'pricelist_id': ref('product.list0'),
'room_type_id': ref('pms_room_type_3'),
- 'room_id': ref('pms_room_6'),
+ 'preferred_room_id': ref('pms_room_6'),
'checkin': (DateTime.today() + timedelta(days=29)),
'checkout': (DateTime.today() + timedelta(days=30)),
'adults': 1,
diff --git a/pms/models/pms_reservation.py b/pms/models/pms_reservation.py
index 99fdde6d3..7ba04783f 100644
--- a/pms/models/pms_reservation.py
+++ b/pms/models/pms_reservation.py
@@ -106,15 +106,12 @@ class PmsReservation(models.Model):
# required=True,
)
priority = fields.Integer(compute="_compute_priority", store="True", index=True)
- room_id = fields.Many2one(
+
+ preferred_room_id = fields.Many2one(
"pms.room",
string="Room",
tracking=True,
ondelete="restrict",
- compute="_compute_room_id",
- store=True,
- readonly=False,
- # required=True,
domain="[('id', 'in', allowed_room_ids)]",
)
allowed_room_ids = fields.Many2many(
@@ -262,6 +259,13 @@ class PmsReservation(models.Model):
store=True,
)
+ rooms = fields.Char(
+ string="Room/s",
+ compute="_compute_rooms",
+ store=True
+ )
+
+
credit_card_details = fields.Text(related="folio_id.credit_card_details")
cancelled_reason = fields.Selection(
[("late", "Late"), ("intime", "In time"), ("noshow", "No Show")],
@@ -419,22 +423,15 @@ class PmsReservation(models.Model):
# TODO: Logic priority (100 by example)
self.priority = 100
- @api.depends("reservation_line_ids", "reservation_line_ids.room_id")
- def _compute_room_id(self):
- for reservation in self.filtered(
- lambda r: r.reservation_line_ids and not r.room_id
- ):
- reservation.room_id = reservation.reservation_line_ids[0].room_id
-
- @api.depends("room_id")
+ @api.depends("preferred_room_id")
def _compute_room_type_id(self):
for reservation in self:
- if reservation.room_id and not reservation.room_type_id:
- reservation.room_type_id = reservation.room_id.room_type_id.id
+ if reservation.preferred_room_id and not reservation.room_type_id:
+ reservation.room_type_id = reservation.preferred_room_id.room_type_id.id
elif not reservation.room_type_id:
reservation.room_type_id = False
- @api.depends("reservation_line_ids.date", "overbooking", "state", "room_id")
+ @api.depends("reservation_line_ids.date", "overbooking", "state", "preferred_room_id")
def _compute_allowed_room_ids(self):
for reservation in self:
if reservation.checkin and reservation.checkout:
@@ -541,12 +538,12 @@ class PmsReservation(models.Model):
reservation.pricelist_id = pricelist_id
# REVIEW: Dont run with set room_type_id -> room_id(compute)-> No set adults¿?
- @api.depends("room_id")
+ @api.depends("preferred_room_id")
def _compute_adults(self):
for reservation in self:
- if reservation.room_id:
+ if reservation.preferred_room_id:
if reservation.adults == 0:
- reservation.adults = reservation.room_id.capacity
+ reservation.adults = reservation.preferred_room_id.capacity
elif not reservation.adults:
reservation.adults = 0
@@ -557,6 +554,8 @@ class PmsReservation(models.Model):
reservation.splitted = True
else:
reservation.splitted = False
+ reservation.preferred_room_id = reservation.reservation_line_ids[0].room_id
+
@api.depends("state", "qty_to_invoice", "qty_invoiced")
def _compute_invoice_status(self):
@@ -691,7 +690,7 @@ class PmsReservation(models.Model):
# TODO: Use default values on checkin /checkout is empty
@api.constrains(
- "checkin", "checkout", "state", "room_id", "overbooking", "reselling"
+ "checkin", "checkout", "state", "preferred_room_id", "overbooking", "reselling"
)
def check_dates(self):
"""
@@ -791,6 +790,24 @@ class PmsReservation(models.Model):
"target": "new",
}
+ def open_reservation_wizard(self):
+ rooms_available = self.env["pms.room.type.availability"].rooms_available(
+ checkin=self.checkin,
+ checkout=self.checkout,
+ current_lines=self.reservation_line_ids.ids,
+ )
+ # REVIEW: check capacity room
+ return {
+ 'view_type': 'form',
+ 'view_mode': 'form',
+ 'name': 'Unify the reservation',
+ 'res_model': 'pms.reservation.wizard',
+ 'target': 'new',
+ 'type': 'ir.actions.act_window',
+ 'context': {'rooms_available': rooms_available.ids,
+ }
+ }
+
# ORM Overrides
@api.model
def name_search(self, name="", args=None, operator="ilike", limit=100):
@@ -800,7 +817,7 @@ class PmsReservation(models.Model):
args += [
"|",
("folio_id.name", operator, name),
- ("room_id.name", operator, name),
+ ("preferred_room_id.name", operator, name),
]
return super(PmsReservation, self).name_search(
name="", args=args, operator="ilike", limit=limit
@@ -809,7 +826,7 @@ class PmsReservation(models.Model):
def name_get(self):
result = []
for res in self:
- name = u"{} ({})".format(res.folio_id.name, res.room_id.name)
+ name = u"{} ({})".format(res.folio_id.name, res.rooms if res.rooms else 'No room')
result.append((res.id, name))
return result
@@ -911,7 +928,7 @@ class PmsReservation(models.Model):
"price_subtotal": self.price_subtotal,
"splitted": self.splitted,
"room_type_id": self.room_type_id.id,
- "room_id": self.room_id.id,
+ "preferred_room_id": self.preferred_room_id.id,
}
def confirm(self):
@@ -1000,8 +1017,8 @@ class PmsReservation(models.Model):
if record.reservation_type != "out":
record.checkin_partner_count = len(record.checkin_partner_ids)
record.checkin_partner_pending_count = (
- record.adults + record.children
- ) - len(record.checkin_partner_ids)
+ record.adults + record.children
+ ) - len(record.checkin_partner_ids)
else:
record.checkin_partner_count = 0
record.checkin_partner_pending_count = 0
@@ -1058,3 +1075,14 @@ class PmsReservation(models.Model):
record.tax_ids = product.taxes_id.filtered(
lambda r: not record.company_id or r.company_id == folio.company_id
)
+
+ @api.depends("reservation_line_ids", "reservation_line_ids.room_id")
+ def _compute_rooms(self):
+ self.rooms = False
+
+ for reservation in self:
+ if reservation.splitted:
+ reservation.rooms = ", ".join([r for r in reservation.reservation_line_ids.mapped('room_id.name')])
+ reservation.preferred_room_id = False
+ else:
+ reservation.rooms = reservation.preferred_room_id.name
diff --git a/pms/models/pms_reservation_line.py b/pms/models/pms_reservation_line.py
index a8a9f140f..0247e891d 100644
--- a/pms/models/pms_reservation_line.py
+++ b/pms/models/pms_reservation_line.py
@@ -2,14 +2,14 @@
# Copyright 2017 Dario Lodeiros
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
-from datetime import timedelta
+import datetime
+import operator
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
_logger = logging.getLogger(__name__)
-
class PmsReservationLine(models.Model):
_name = "pms.reservation.line"
_description = "Reservations by day"
@@ -91,37 +91,96 @@ class PmsReservationLine(models.Model):
]
# Compute and Search methods
- @api.depends(
- "reservation_id.adults",
- "reservation_id.room_type_id",
- )
+ @api.depends("reservation_id.room_type_id")
def _compute_room_id(self):
- for line in self:
- if line.reservation_id.room_type_id:
- preferred_room = line.reservation_id.room_id
- rooms_available = self.env[
- "pms.room.type.availability"
- ].rooms_available(
- checkin=line.date,
- checkout=line.date + timedelta(1),
- room_type_id=line.reservation_id.room_type_id.id or False,
- current_lines=line._origin.id,
+ for line in self.sorted(key=lambda r: (r.reservation_id, r.date)):
+
+ # if the reservation has a room type and no room id
+ if line.reservation_id.room_type_id and not line.room_id:
+
+ # we get the rooms available for the entire stay
+ rooms_available = self.env[ "pms.room.type.availability"].rooms_available(
+ 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,
)
+
+ # if there is availability for the entire stay
if rooms_available:
- if preferred_room.id in rooms_available.ids:
- room_chosen = preferred_room
+
+ # if the reservation has a preferred room
+ if line.reservation_id.preferred_room_id:
+
+ # if the preferred room is available
+ if line.reservation_id.preferred_room_id in rooms_available:
+ line.room_id = line.reservation_id.preferred_room_id
+
+ # if the preferred room is NOT available
+ else:
+ raise ValidationError(_("%s: No room available.")% (line.reservation_id.preferred_room_id.name))
+
+ # otherwise we assign the first of those available for the entire stay
else:
- room_chosen = rooms_available[0]
- line.room_id = room_chosen
+ line.room_id = rooms_available[0]
+
+ # if there is no availability for the entire stay without changing rooms (we assume a split reservation)
else:
- line.room_id = False
- raise ValidationError(
- _("%s: No rooms available")
- % (line.reservation_id.room_type_id.name)
- )
- line._check_adults()
- else:
- line.room_id = False
+ rooms_ranking = dict()
+
+ # we go through the rooms of the type
+ for room in self.env['pms.room'].search([('room_type_id', '=', line.reservation_id.room_type_id.id)]):
+
+ # we iterate the dates from the date of the line to the checkout
+ for date_iterator in \
+ [line.date + datetime.timedelta(days=x) for x in range(0, (line.reservation_id.checkout - line.date).days)]:
+
+ # if the room is already assigned for a date we go to the next room
+ if self.env['pms.reservation.line'].search_count([
+ ('date', '=', date_iterator),
+ ('room_id', '=', room.id),
+ ('id', 'not in', line.reservation_id.reservation_line_ids.ids),
+ ("occupies_availability", "=", True),
+ ]) > 0:
+ break
+
+ # if the room is not assigned for a date we add it to the ranking / update its ranking
+ else:
+ rooms_ranking[room.id] = 1 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") % (line.reservation_id.room_type_id.name))
+ else:
+ # we get the best score in the ranking
+ best = max(rooms_ranking.values())
+
+ # we keep the rooms with the best ranking
+ bests = {key: value for (key, value) in rooms_ranking.items() if value == best}
+
+ # if there is a tie in the rankings
+ if len(bests) > 1:
+
+ # we get the line from last night
+ date_last_night = line.date + datetime.timedelta(days=-1)
+ line_past_night = self.env['pms.reservation.line'].search([
+ ('date', '=', date_last_night),
+ ('reservation_id', '=', line.reservation_id.id)
+ ])
+
+ # if there is the night before and if the room from the night before is in the ranking
+ if line_past_night and line_past_night.room_id.id in bests:
+ line.room_id = line_past_night.room_id.id
+
+ # if the room from the night before is not in the ranking or there is no night before
+ else:
+ # At this point we set the room with the best ranking, no matter what it is
+ line.room_id = list(bests.keys())[0]
+
+ # if there is no tie in the rankings
+ else:
+ # At this point we set the room with the best ranking, no matter what it is
+ line.room_id = list(bests.keys())[0]
+
+
@api.depends(
"reservation_id",
@@ -158,7 +217,7 @@ class PmsReservationLine(models.Model):
line.reservation_id.tax_ids,
line.reservation_id.company_id,
)
- _logger.info(line.price)
+ # _logger.info(line.price)
# TODO: Out of service 0 amount
else:
line.price = line._origin.price
diff --git a/pms/security/ir.model.access.csv b/pms/security/ir.model.access.csv
index 3530f26ab..02f4a382b 100644
--- a/pms/security/ir.model.access.csv
+++ b/pms/security/ir.model.access.csv
@@ -47,3 +47,4 @@ manager_access_pms_board_service_line,manager_access_pms_board_service_line,mode
manager_access_property,manager_access_property,model_pms_property,pms.group_pms_manager,1,1,1,1
manager_access_pms_cancelation_rule,manager_access_pms_cancelation_rule,model_pms_cancelation_rule,pms.group_pms_manager,1,1,1,1
manager_access_availability,manager_access_availability,model_pms_room_type_availability,pms.group_pms_manager,1,1,1,1
+user_access_pms_reservation_wizard,user_access_pms_reservation_wizard,model_pms_reservation_wizard,pms.group_pms_user,1,1,1,1
diff --git a/pms/tests/test_pms_reservation.py b/pms/tests/test_pms_reservation.py
index 3fc967ce9..60a1726a9 100644
--- a/pms/tests/test_pms_reservation.py
+++ b/pms/tests/test_pms_reservation.py
@@ -1,20 +1,80 @@
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("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 property
+ self.property = self.env['pms.property'].create(
+ {
+ '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,
+ })
+
+ # create room type class
+ self.room_type_class = self.env['pms.room.type.class'].create(
+ {
+ 'name': 'Room'
+ }
+ )
+
+ # create room type
+ self.room_type_double = self.env['pms.room.type'].create(
+ {
+ 'pms_property_ids': [self.property.id],
+ 'name': 'Double Test',
+ 'code_type': 'DBL_Test',
+ 'class_id': self.room_type_class.id
+ }
+ )
+
+ # create rooms
+ self.room1 = self.env['pms.room'].create(
+ {
+ 'pms_property_id': self.property.id,
+ 'name': 'Double 101',
+ 'room_type_id': self.room_type_double.id,
+ 'capacity': 2
+ })
+
+ self.room2 = self.env['pms.room'].create(
+ {
+ 'pms_property_id': self.property.id,
+ 'name': 'Double 102',
+ 'room_type_id': self.room_type_double.id,
+ 'capacity': 2
+ })
+
+ self.room3 = self.env['pms.room'].create(
+ {
+ 'pms_property_id': self.property.id,
+ 'name': 'Double 103',
+ 'room_type_id': self.room_type_double.id,
+ 'capacity': 2
+ })
+ self.demo_user = self.env.ref("base.user_admin")
+
+
def test_create_reservation(self):
today = fields.date.today()
checkin = today + datetime.timedelta(days=8)
checkout = checkin + datetime.timedelta(days=11)
- demo_user = self.env.ref("base.user_demo")
customer = self.env.ref("base.res_partner_12")
reservation_vals = {
"checkin": checkin,
@@ -24,7 +84,7 @@ class TestPmsReservations(TestHotel):
"pms_property_id": self.main_hotel_property.id,
}
reservation = (
- self.env["pms.reservation"].with_user(demo_user).create(reservation_vals)
+ self.env["pms.reservation"].create(reservation_vals)
)
self.assertEqual(
@@ -36,7 +96,401 @@ class TestPmsReservations(TestHotel):
reservation.reservation_line_ids[-1].date,
checkout - datetime.timedelta(1),
"Reservation lines don't end in the correct date",
+ )
+
+ @freeze_time("1980-11-01")
+ def test_split_reservation01(self):
+ """
+ The reservation shouldn't be splitted
+ preferred_room_id with availability provided
+ +------------+------+------+------+----+----+----+
+ | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
+ +------------+------+------+------+----+----+----+
+ | Double 101 | test | test | test | | | |
+ | Double 102 | | | | | | |
+ | Double 103 | | | | | | |
+ +------------+------+------+------+----+----+----+
+ """
+
+ self.create_common_scenario()
+
+ r_test = self.env["pms.reservation"].create(
+ {
+ "pms_property_id": self.property.id,
+ "checkin": datetime.datetime.now(),
+ "checkout": datetime.datetime.now() + datetime.timedelta(days=3),
+ "adults": 2,
+ "preferred_room_id": self.room1.id
+ }
)
+ r_test.flush()
+ obtained = all(elem.room_id.id == r_test.reservation_line_ids[0].room_id.id for elem in r_test.reservation_line_ids)
+ self.assertTrue(obtained, "The entire reservation should be allocated in the preferred room")
+
+ @freeze_time("1980-11-01")
+ def test_split_reservation02(self):
+ """
+ The reservation shouldn't be splitted
+ room_type_id with availability provided
+ +------------+------+------+------+----+----+----+
+ | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
+ +------------+------+------+------+----+----+----+
+ | Double 101 | test | test | test | | | |
+ | Double 102 | | | | | | |
+ | Double 103 | | | | | | |
+ +------------+------+------+------+----+----+----+
+ """
+ self.create_common_scenario()
+
+ r_test = self.env["pms.reservation"].create(
+ {
+ "pms_property_id": self.property.id,
+ "checkin": datetime.datetime.now(),
+ "checkout": datetime.datetime.now() + datetime.timedelta(days=2),
+ "adults": 2,
+ "room_type_id": self.room_type_double.id,
+ }
+ )
+ r_test.flush()
+ self.assertFalse(r_test.splitted, "The reservation shouldn't be splitted")
+
+ @freeze_time("1980-11-01")
+ def test_split_reservation03(self):
+ """
+ The reservation should be splitted in 2 rooms
+ (there is only one better option on day 02 and a draw the next day.
+ The night before should be prioritized)
+ +------------+------+------+------+------+----+----+
+ | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
+ +------------+------+------+------+------+----+----+
+ | Double 101 | test | r3 | | | | |
+ | Double 102 | r1 | test | test | test | | |
+ | Double 103 | r2 | r4 | | | | |
+ +------------+------+------+------+------+----+----+
+ """
+
+ self.create_common_scenario()
+
+ r1 = self.env["pms.reservation"].create(
+ {
+ "pms_property_id": self.property.id,
+ "checkin": datetime.datetime.now(),
+ "checkout": datetime.datetime.now() + datetime.timedelta(days=1),
+ "adults": 2,
+ "room_type_id": self.room_type_double.id,
+ }
+ )
+ r1.reservation_line_ids[0].room_id = self.room2.id
+ r1.flush()
+
+ r2 = self.env["pms.reservation"].create(
+ {
+ "pms_property_id": self.property.id,
+ "checkin": datetime.datetime.now(),
+ "checkout": datetime.datetime.now() + datetime.timedelta(days=1),
+ "adults": 2,
+ "room_type_id": self.room_type_double.id,
+ }
+ )
+ r2.reservation_line_ids[0].room_id = self.room3.id
+ r2.flush()
+
+ r3 = self.env["pms.reservation"].create(
+ {
+ "pms_property_id": self.property.id,
+ "checkin": datetime.datetime.now() + datetime.timedelta(days=1),
+ "checkout": datetime.datetime.now() + datetime.timedelta(days=2),
+ "adults": 2,
+ "room_type_id": self.room_type_double.id,
+ }
+ )
+ r3.reservation_line_ids[0].room_id = self.room1.id
+ r3.flush()
+
+ r4 = self.env["pms.reservation"].create(
+ {
+ "pms_property_id": self.property.id,
+ "checkin": datetime.datetime.now() + datetime.timedelta(days=1),
+ "checkout": datetime.datetime.now() + datetime.timedelta(days=2),
+ "adults": 2,
+ "room_type_id": self.room_type_double.id,
+ }
+ )
+ r4.reservation_line_ids[0].room_id = self.room3.id
+ r4.flush()
+
+ r_test = self.env["pms.reservation"].create(
+ {
+ "pms_property_id": self.property.id,
+ "checkin": datetime.datetime.now(),
+ "checkout": datetime.datetime.now() + datetime.timedelta(days=4),
+ "adults": 2,
+ "room_type_id": self.room_type_double.id,
+ }
+ )
+ r_test.flush()
+ changes = 0
+ last_room = None
+
+ for line in r_test.reservation_line_ids:
+ if last_room != line.room_id.id:
+ last_room = line.room_id.id
+ changes +=1
+
+ self.assertEqual(2, changes, "The reservation shouldn't have more than 2 changes")
+
+ @freeze_time("1980-11-01")
+ def test_split_reservation04(self):
+ """
+ The reservation should be splitted in 3 rooms
+ (there are 2 best options on day 03 and room of last night is not available)
+ +------------+------+------+------+------+----+----+
+ | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
+ +------------+------+------+------+------+----+----+
+ | Double 101 | test | r3 | test | test | | |
+ | Double 102 | r1 | test | r5 | | | |
+ | Double 103 | r2 | r4 | | | | |
+ +------------+------+------+------+------+----+----+
+ """
+
+ self.create_common_scenario()
+
+ r1 = self.env["pms.reservation"].create(
+ {
+ "pms_property_id": self.property.id,
+ "checkin": datetime.datetime.now(),
+ "checkout": datetime.datetime.now() + datetime.timedelta(days=1),
+ "adults": 2,
+ "room_type_id": self.room_type_double.id,
+ }
+ )
+ r1.reservation_line_ids[0].room_id = self.room2.id
+ r1.flush()
+
+ r2 = self.env["pms.reservation"].create(
+ {
+ "pms_property_id": self.property.id,
+ "checkin": datetime.datetime.now(),
+ "checkout": datetime.datetime.now() + datetime.timedelta(days=1),
+ "adults": 2,
+ "room_type_id": self.room_type_double.id,
+ }
+ )
+ r2.reservation_line_ids[0].room_id = self.room3.id
+ r2.flush()
+
+ r3 = self.env["pms.reservation"].create(
+ {
+ "pms_property_id": self.property.id,
+ "checkin": datetime.datetime.now() + datetime.timedelta(days=1),
+ "checkout": datetime.datetime.now() + datetime.timedelta(days=2),
+ "adults": 2,
+ "room_type_id": self.room_type_double.id,
+ }
+ )
+ r3.reservation_line_ids[0].room_id = self.room1.id
+ r3.flush()
+
+ r4 = self.env["pms.reservation"].create(
+ {
+ "pms_property_id": self.property.id,
+ "checkin": datetime.datetime.now() + datetime.timedelta(days=1),
+ "checkout": datetime.datetime.now() + datetime.timedelta(days=2),
+ "adults": 2,
+ "room_type_id": self.room_type_double.id,
+ }
+ )
+ r4.reservation_line_ids[0].room_id = self.room3.id
+ r4.flush()
+
+ r5 = self.env["pms.reservation"].create(
+ {
+ "pms_property_id": self.property.id,
+ "checkin": datetime.datetime.now() + datetime.timedelta(days=2),
+ "checkout": datetime.datetime.now() + datetime.timedelta(days=3),
+ "adults": 2,
+ "room_type_id": self.room_type_double.id,
+ }
+ )
+ r5.reservation_line_ids[0].room_id = self.room2.id
+ r5.flush()
+
+ r_test = self.env["pms.reservation"].create(
+ {
+ "pms_property_id": self.property.id,
+ "checkin": datetime.datetime.now(),
+ "checkout": datetime.datetime.now() + datetime.timedelta(days=4),
+ "adults": 2,
+ "room_type_id": self.room_type_double.id,
+ }
+ )
+ r_test.flush()
+
+ changes = 0
+ last_room = None
+ for line in r_test.reservation_line_ids:
+ if line.room_id != last_room:
+ last_room = line.room_id
+ changes += 1
+
+ self.assertEqual(3, changes, "The reservation shouldn't be splitted in more than 3 roomss")
+
+ @freeze_time("1980-11-01")
+ def test_split_reservation05(self):
+ """
+ The preferred room_id is not available
+ +------------+------+------+------+----+----+----+
+ | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
+ +------------+------+------+------+----+----+----+
+ | Double 101 |r1/tst| | | | | |
+ | Double 102 | | | | | | |
+ | Double 103 | | | | | | |
+ +------------+------+------+------+----+----+----+
+ """
+
+ self.create_common_scenario()
+
+ r1 = self.env["pms.reservation"].create(
+ {
+ "pms_property_id": self.property.id,
+ "checkin": datetime.datetime.now(),
+ "checkout": datetime.datetime.now() + datetime.timedelta(days=1),
+ "adults": 2,
+ "room_type_id": self.room_type_double.id,
+ }
+ )
+ r1.reservation_line_ids[0].room_id = self.room1
+ r1.flush()
+
+ r_test = self.env["pms.reservation"].create(
+ {
+ "pms_property_id": self.property.id,
+ "checkin": datetime.datetime.now(),
+ "checkout": datetime.datetime.now() + datetime.timedelta(days=1),
+ "adults": 2,
+ "preferred_room_id": self.room1.id
+ }
+ )
+
+ with self.assertRaises(ValidationError):
+ r_test.flush()
+
+ @freeze_time("1980-11-01")
+ def test_split_reservation06(self):
+ """
+ There's no availability in the preferred_room_id provided
+ +------------+------+------+------+----+----+----+
+ | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
+ +------------+------+------+------+----+----+----+
+ | Double 101 | r1 |r1/tst| tst | | | |
+ | Double 102 | | | | | | |
+ | Double 103 | | | | | | |
+ +------------+------+------+------+----+----+----+
+ """
+
+ self.create_common_scenario()
+
+ r1 = self.env["pms.reservation"].create(
+ {
+ "pms_property_id": self.property.id,
+ "checkin": datetime.datetime.now(),
+ "checkout": datetime.datetime.now() + datetime.timedelta(days=2),
+ "adults": 2,
+ "room_type_id": self.room_type_double.id,
+
+ }
+ )
+ r1.reservation_line_ids[0].room_id = self.room1
+ r1.reservation_line_ids[1].room_id = self.room1
+ r1.flush()
+
+ r_test = self.env["pms.reservation"].create(
+ {
+ "pms_property_id": self.property.id,
+ "checkin": datetime.datetime.now() + datetime.timedelta(days=1),
+ "checkout": datetime.datetime.now() + datetime.timedelta(days=3),
+ "adults": 2,
+ "preferred_room_id": self.room1.id
+ }
+ )
+
+ with self.assertRaises(ValidationError):
+ r_test.flush()
+
+ @freeze_time("1980-11-01")
+ def test_split_reservation07(self):
+ """
+ There's no availability
+ +------------+------+------+------+----+----+----+
+ | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
+ +------------+------+------+------+----+----+----+
+ | Double 101 | r1 | r1 | r1 | | | |
+ | Double 102 | r2 | r2 | r2 | | | |
+ | Double 103 | r3 | r3 | r3 | | | |
+ +------------+------+------+------+----+----+----+
+ """
+
+ self.create_common_scenario()
+
+ r1 = self.env["pms.reservation"].create(
+ {
+ "pms_property_id": self.property.id,
+ "checkin": datetime.datetime.now(),
+ "checkout": datetime.datetime.now() + datetime.timedelta(days=3),
+ "adults": 2,
+ "room_type_id": self.room_type_double.id,
+
+ }
+ )
+ r1.reservation_line_ids[0].room_id = self.room1
+ r1.reservation_line_ids[1].room_id = self.room1
+ r1.reservation_line_ids[2].room_id = self.room1
+ r1.flush()
+
+
+
+ r2 = self.env["pms.reservation"].create(
+ {
+ "pms_property_id": self.property.id,
+ "checkin": datetime.datetime.now(),
+ "checkout": datetime.datetime.now() + datetime.timedelta(days=3),
+ "adults": 2,
+ "room_type_id": self.room_type_double.id,
+
+ }
+ )
+ r2.reservation_line_ids[0].room_id = self.room2
+ r2.reservation_line_ids[1].room_id = self.room2
+ r2.reservation_line_ids[2].room_id = self.room2
+ r2.flush()
+
+ r3 = self.env["pms.reservation"].create(
+ {
+ "pms_property_id": self.property.id,
+ "checkin": datetime.datetime.now(),
+ "checkout": datetime.datetime.now() + datetime.timedelta(days=3),
+ "adults": 2,
+ "room_type_id": self.room_type_double.id,
+ }
+ )
+ r3.reservation_line_ids[0].room_id = self.room3
+ r3.reservation_line_ids[1].room_id = self.room3
+ r3.reservation_line_ids[2].room_id = self.room3
+ r3.flush()
+
+ r_test = self.env["pms.reservation"].create(
+ {
+ "pms_property_id": self.property.id,
+ "checkin": datetime.datetime.now(),
+ "checkout": datetime.datetime.now() + datetime.timedelta(days=1),
+ "adults": 2,
+ "room_type_id": self.room_type_double.id,
+ }
+ )
+
+ with self.assertRaises(ValidationError):
+ r_test.flush()
+
def test_manage_children_raise(self):
diff --git a/pms/views/pms_reservation_views.xml b/pms/views/pms_reservation_views.xml
index ce6b98f0f..caa73059f 100644
--- a/pms/views/pms_reservation_views.xml
+++ b/pms/views/pms_reservation_views.xml
@@ -66,14 +66,13 @@
style="margin-bottom:0px;"
attrs="{'invisible': [('splitted','=',False)]}"
>
- This reservation is part of splitted reservation!, you can check it
- in the
+ This reservation is part of splitted reservation, you can try to unify the reservation here