From bafd2c75bd651db1072ffb6c42e0dc292e29d5be Mon Sep 17 00:00:00 2001 From: Dario Lodeiros Date: Mon, 9 Aug 2021 17:21:05 +0200 Subject: [PATCH 1/3] [ADD] Shared Room Tests & parent/child room fields --- pms/__manifest__.py | 1 - pms/models/__init__.py | 1 - pms/models/pms_room.py | 32 +++-- pms/models/pms_room_type.py | 6 - pms/models/pms_shared_room.py | 143 --------------------- pms/security/ir.model.access.csv | 2 - pms/tests/__init__.py | 1 + pms/tests/test_shared_room.py | 185 ++++++++++++++++++++++++++++ pms/views/pms_room_type_views.xml | 1 - pms/views/pms_room_views.xml | 37 +----- pms/views/pms_shared_room_views.xml | 133 -------------------- 11 files changed, 211 insertions(+), 331 deletions(-) delete mode 100644 pms/models/pms_shared_room.py create mode 100644 pms/tests/test_shared_room.py delete mode 100644 pms/views/pms_shared_room_views.xml diff --git a/pms/__manifest__.py b/pms/__manifest__.py index 8c9133b21..67bbce05c 100644 --- a/pms/__manifest__.py +++ b/pms/__manifest__.py @@ -65,7 +65,6 @@ "views/pms_room_type_class_views.xml", "views/pms_availability_plan_views.xml", "views/pms_availability_plan_rule_views.xml", - "views/pms_shared_room_views.xml", "views/res_partner_views.xml", "views/product_pricelist_views.xml", "views/product_pricelist_item_views.xml", diff --git a/pms/models/__init__.py b/pms/models/__init__.py index 2a7be0cf8..5bcb034f5 100644 --- a/pms/models/__init__.py +++ b/pms/models/__init__.py @@ -13,7 +13,6 @@ from . import pms_ubication from . import pms_folio from . import pms_reservation from . import pms_room -from . import pms_shared_room from . import pms_amenity from . import pms_amenity_type from . import pms_room_type diff --git a/pms/models/pms_room.py b/pms/models/pms_room.py index 4c64cc214..59ad462ef 100644 --- a/pms/models/pms_room.py +++ b/pms/models/pms_room.py @@ -48,12 +48,20 @@ class PmsRoom(models.Model): ondelete="restrict", check_pms_properties=True, ) - # TODO: design shared rooms - shared_room_id = fields.Many2one( - string="Shared Room", - help="The room can be sold by beds", - default=False, - comodel_name="pms.shared.room", + parent_id = fields.Many2one( + string="Parent Room", + help="Indicates that this room is a child of another room", + comodel_name="pms.room", + ondelete="restrict", + check_pms_properties=True, + ) + child_ids = fields.One2many( + string="Child Rooms", + help="Child rooms of the room", + comodel_name="pms.room", + inverse_name="parent_id", + ondelete="restrict", + check_pms_properties=True, ) ubication_id = fields.Many2one( string="Ubication", @@ -146,10 +154,8 @@ class PmsRoom(models.Model): def get_capacity(self, extra_bed=0): for record in self: - if not record.shared_room_id: - if extra_bed > record.extra_beds_allowed: - raise ValidationError( - _("Extra beds can't be greater than allowed beds for this room") - ) - return record.capacity + extra_bed - return record.capacity + if extra_bed > record.extra_beds_allowed: + raise ValidationError( + _("Extra beds can't be greater than allowed beds for this room") + ) + return record.capacity + extra_bed diff --git a/pms/models/pms_room_type.py b/pms/models/pms_room_type.py index 076465651..116b00caa 100644 --- a/pms/models/pms_room_type.py +++ b/pms/models/pms_room_type.py @@ -67,12 +67,6 @@ class PmsRoomType(models.Model): help="Identification code for a room type", required=True, ) - # TODO: Session review to define shared room and "sales rooms packs" - is_shared_room = fields.Boolean( - string="Shared Room", - help="This room type is reservation by beds", - default=False, - ) total_rooms_count = fields.Integer( string="Total Rooms Count", help="The number of rooms in a room type", diff --git a/pms/models/pms_shared_room.py b/pms/models/pms_shared_room.py deleted file mode 100644 index 8566db551..000000000 --- a/pms/models/pms_shared_room.py +++ /dev/null @@ -1,143 +0,0 @@ -# Copyright 2017 Alexandre Díaz -# Copyright 2017 Dario Lodeiros -# Copyright 2018 Pablo Quesada -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import _, api, fields, models -from odoo.exceptions import ValidationError - - -class PmsSharedRoom(models.Model): - _name = "pms.shared.room" - _description = "Shared Room" - _order = "room_type_id, name" - _check_pms_properties_auto = True - - name = fields.Char( - string="Room Name", help="Name of the shared room", required=True - ) - active = fields.Boolean( - string="Active", help="Determines if shared room is active", default=True - ) - sequence = fields.Integer( - string="Sequence", - help="Field used to change the position of the shared rooms in tree view." - "Changing the position changes the sequence", - required=True, - ) - room_type_id = fields.Many2one( - string="Room Type", - help="Room type which the shared room belongs", - comodel_name="pms.room.type", - required=True, - ondelete="restrict", - domain=[("shared_room", "=", True)], - ) - # TODO: properties relation - pms_property_ids = fields.Many2many( - string="Properties", - help="Properties with access to the element;" - " if not set, all properties can access", - comodel_name="pms.property", - relation="pms_shared_room_pms_property_rel", - column1="shared_room_id", - column2="pms_property_id", - check_pms_properties=True, - ) - ubication_id = fields.Many2one( - string="Ubication", - help="At which ubication the room is located.", - comodel_name="pms.ubication", - ondelete="restrict", - ) - bed_ids = fields.One2many( - string="Beds", - help="Beds in one room", - comodel_name="pms.room", - inverse_name="shared_room_id", - readonly=True, - ) - beds = fields.Integer( - string="Number Of Beds", help="Number of beds in a shared room" - ) - description_sale = fields.Text( - string="Sale Description", - help="A description of the Product that you want to communicate to " - " your customers. This description will be copied to every Sales " - " Order, Delivery Order and Customer Invoice/Credit Note", - translate=True, - ) - - @api.constrains("beds") - def _constrain_beds(self): - self.ensure_one() - if self.beds < 1: - raise ValidationError(_("Room beds can't be less than one")) - if len(self.bed_ids) > self.beds: - raise ValidationError( - _( - "If you want to eliminate beds in the \ - room you must deactivate the beds from your form" - ) - ) - beds = [] - inactive_beds = self.env["pms.room"].search( - [("active", "=", False), ("shared_room_id", "=", self.id)] - ) - for i in range(len(self.bed_ids), self.beds): - if inactive_beds: - bed = inactive_beds[0] - bed.update({"active": True}) - inactive_beds -= bed - continue - name = u"{} ({})".format(self.name, i + 1) - bed_vals = { - "name": name, - "capacity": 1, - "room_type_id": self.room_type_id.id, - "sequence": self.sequence, - "ubication_id": self.ubication_id.id if self.ubication_id else False, - "shared_room_id": self.id, - } - beds.append((0, False, bed_vals)) - if beds: - self.update({"bed_ids": beds}) - - @api.constrains("active") - def _constrain_active(self): - self.bed_ids.write( - { - "active": self.active, - } - ) - - @api.constrains("room_type_id") - def _constrain_room_type_id(self): - self.bed_ids.write( - { - "room_type_id": self.room_type_id.id, - } - ) - - @api.constrains("ubication_id") - def _constrain_ubication_id(self): - self.bed_ids.write( - { - "ubication_id": self.ubication_id.id, - } - ) - - @api.constrains("sequence") - def _constrain_sequence(self): - self.bed_ids.write( - { - "sequence": self.sequence, - } - ) - - @api.constrains("descrition_sale") - def _constrain_descrition_sale(self): - self.bed_ids.write( - { - "description_sale": self.descrition_sale, - } - ) diff --git a/pms/security/ir.model.access.csv b/pms/security/ir.model.access.csv index d313323f6..544f75411 100644 --- a/pms/security/ir.model.access.csv +++ b/pms/security/ir.model.access.csv @@ -10,7 +10,6 @@ user_access_pms_board_service,user_access_pms_board_service,model_pms_board_serv user_access_pms_checkin_partner,user_access_pms_checkin_partner,model_pms_checkin_partner,pms.group_pms_user,1,1,1,1 user_access_pms_room_type_class,user_access_pms_room_type_class,model_pms_room_type_class,pms.group_pms_user,1,0,0,0 user_access_pms_room,user_access_pms_room,model_pms_room,pms.group_pms_user,1,0,0,0 -user_access_shared_pms_room,user_access_pms_shared_room,model_pms_shared_room,pms.group_pms_user,1,0,0,0 user_access_pms_availability_plan_rule,user_access_pms_availability_plan_rule,model_pms_availability_plan_rule,pms.group_pms_user,1,0,0,0 user_access_pms_availability,user_access_pms_availability,model_pms_availability,pms.group_pms_user,1,1,1,0 user_access_pms_reservation,user_access_pms_reservation,model_pms_reservation,pms.group_pms_user,1,1,1,1 @@ -36,7 +35,6 @@ manager_access_pms_board_service,manager_access_pms_board_service,model_pms_boar manager_access_pms_checkin_partner,manager_access_pms_checkin_partner,model_pms_checkin_partner,pms.group_pms_manager,1,1,1,1 manager_access_pms_room_type_class,manager_access_pms_room_type_class,model_pms_room_type_class,pms.group_pms_manager,1,1,1,1 manager_access_pms_room,manager_access_pms_room,model_pms_room,pms.group_pms_manager,1,1,1,1 -manager_access_pms_shared_room,manager_access_pms_shared_room,model_pms_shared_room,pms.group_pms_manager,1,1,1,1 manager_access_pms_availability_plan_rule,manager_access_pms_availability_plan_rule,model_pms_availability_plan_rule,pms.group_pms_manager,1,1,1,1 manager_access_pms_reservation,manager_access_pms_reservation,model_pms_reservation,pms.group_pms_manager,1,1,1,1 manager_access_pms_availability,manager_access_pms_availability,model_pms_availability,pms.group_pms_manager,1,1,1,0 diff --git a/pms/tests/__init__.py b/pms/tests/__init__.py index 2976ec8ca..4f21325fd 100644 --- a/pms/tests/__init__.py +++ b/pms/tests/__init__.py @@ -37,3 +37,4 @@ from . import test_pms_folio_sale_line from . import test_pms_wizard_split_join_swap_reservation from . import test_product_template from . import test_pms_multiproperty +from . import test_shared_room diff --git a/pms/tests/test_shared_room.py b/pms/tests/test_shared_room.py new file mode 100644 index 000000000..0541a22a9 --- /dev/null +++ b/pms/tests/test_shared_room.py @@ -0,0 +1,185 @@ +import datetime + +from odoo import fields + +from .common import TestPms + + +class TestPmsSharedRoom(TestPms): + def setUp(self): + super().setUp() + # create a room type availability + self.room_type_availability = self.env["pms.availability.plan"].create( + { + "name": "Availability plan for TEST", + "pms_pricelist_ids": [(6, 0, [self.pricelist1.id])], + } + ) + + self.bed_class = self.env["pms.room.type.class"].create( + { + "name": "Bed Class 1", + "default_code": "B1", + } + ) + + # create room type + self.room_type_shared = self.env["pms.room.type"].create( + { + "pms_property_ids": [self.pms_property1.id], + "name": "Shared Test", + "default_code": "SHT", + "class_id": self.room_type_class1.id, + } + ) + + self.room_type_bed = self.env["pms.room.type"].create( + { + "pms_property_ids": [self.pms_property1.id], + "name": "Bed Type Test", + "default_code": "BTT", + "class_id": self.bed_class.id, + } + ) + + # create shared room + self.room1 = self.env["pms.room"].create( + { + "pms_property_id": self.pms_property1.id, + "name": "Shared 101", + "room_type_id": self.room_type_shared.id, + "capacity": 2, + "extra_beds_allowed": 1, + } + ) + + # create beds in room1 + self.r1bed1 = self.env["pms.room"].create( + { + "pms_property_id": self.pms_property1.id, + "name": "101 (1)", + "room_type_id": self.room_type_bed.id, + "capacity": 1, + "parent_id": self.room1.id, + } + ) + + self.r1bed2 = self.env["pms.room"].create( + { + "pms_property_id": self.pms_property1.id, + "name": "101 (2)", + "room_type_id": self.room_type_bed.id, + "capacity": 2, + "parent_id": self.room1.id, + } + ) + + # create partner + self.partner1 = self.env["res.partner"].create( + { + "firstname": "Jaime", + "lastname": "García", + "email": "jaime@example.com", + "birthdate_date": "1983-03-01", + "gender": "male", + } + ) + + def test_not_avail_beds_with_room_occupied(self): + """ + Check that not allow to create a bed reservation with a room occupied + ---------------- + Create a room1 reservation and check that the beds room real avail is 0 + """ + + # ARRANGE + today = fields.date.today() + tomorrow = fields.date.today() + datetime.timedelta(days=1) + + # ACT + self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "preferred_room_id": self.room1.id, + "checkin": today, + "checkout": tomorrow, + "pms_property_id": self.pms_property1.id, + } + ) + + # ASSERT + self.assertEqual( + self.pms_property1.with_context( + checkin=today, + checkout=tomorrow, + room_type_id=self.room_type_bed.id, + ).availability, + 0, + "Beds avaialbility should be 0 for room occupied", + ) + + def test_not_avail_shared_room_with_one_bed_occupied(self): + """ + Check that not allow to create a shared room reservation with a bed occupied + ---------------- + Create a room1's bed reservation and check that the room1 real avail is 0 + """ + + # ARRANGE + today = fields.date.today() + tomorrow = fields.date.today() + datetime.timedelta(days=1) + + # ACT + self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "preferred_room_id": self.r1bed1.id, + "checkin": today, + "checkout": tomorrow, + "pms_property_id": self.pms_property1.id, + } + ) + + # ASSERT + self.assertEqual( + self.pms_property1.with_context( + checkin=today, + checkout=tomorrow, + room_type_id=self.room_type_shared.id, + ).availability, + 0, + "Shared Room avaialbility should be 0 if it has a bed occupied", + ) + + def test_avail_beds_with_one_bed_occupied(self): + """ + Check the avail of a bed when it has a room with other beds occupied + ---------------- + Create a room1's bed (it has 2 beds) reservation and check that the beds avail = 1 + """ + + # ARRANGE + today = fields.date.today() + tomorrow = fields.date.today() + datetime.timedelta(days=1) + + # ACT + self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "preferred_room_id": self.r1bed1.id, + "checkin": today, + "checkout": tomorrow, + "pms_property_id": self.pms_property1.id, + } + ) + + # ASSERT + self.assertEqual( + self.pms_property1.with_context( + checkin=today, + checkout=tomorrow, + room_type_id=self.room_type_bed.id, + ).availability, + 1, + "Shared Room avaialbility should be 0 if it has a bed occupied", + ) diff --git a/pms/views/pms_room_type_views.xml b/pms/views/pms_room_type_views.xml index 22965b472..e88714a4b 100644 --- a/pms/views/pms_room_type_views.xml +++ b/pms/views/pms_room_type_views.xml @@ -37,7 +37,6 @@ - diff --git a/pms/views/pms_room_views.xml b/pms/views/pms_room_views.xml index 40094bdf7..05b7fcc4c 100644 --- a/pms/views/pms_room_views.xml +++ b/pms/views/pms_room_views.xml @@ -24,15 +24,7 @@
@@ -43,25 +35,11 @@ invisible="0" force_save="1" /> - + - - - + + +
@@ -90,10 +68,7 @@ - + diff --git a/pms/views/pms_shared_room_views.xml b/pms/views/pms_shared_room_views.xml deleted file mode 100644 index 68c9871c6..000000000 --- a/pms/views/pms_shared_room_views.xml +++ /dev/null @@ -1,133 +0,0 @@ - - - - pms.shared.room.form - pms.shared.room - -
- -
- -
-
-
- - - - - - - - - - - - - - - - - - -
-
-
-
- - pms.shared.room.kanban - pms.shared.room - kanban - - - false - - - - -
-
-
    -
  • - - - -
  • -
  • - Room Type: - -
  • -
  • - - Beds - - -
  • -
-
-
-
-
-
-
-
- - pms.shared.room.search - pms.shared.room - - - - - - - - - - pms.shared.room.tree - pms.shared.room - - - - - - - - - - - Shared Room - pms.shared.room - - - kanban,tree,form - - -
From 2c7dae07cb4ea4c925521069cde8a04b16745b15 Mon Sep 17 00:00:00 2001 From: Dario Lodeiros Date: Wed, 11 Aug 2021 11:37:03 +0200 Subject: [PATCH 2/3] [ADD] Shared Room avail manage --- pms/models/pms_availability.py | 180 +++++++++++++++- pms/models/pms_property.py | 28 +-- pms/models/pms_reservation_line.py | 31 +++ pms/tests/__init__.py | 36 ++-- pms/tests/test_shared_room.py | 324 ++++++++++++++++++++++++++++- 5 files changed, 545 insertions(+), 54 deletions(-) diff --git a/pms/models/pms_availability.py b/pms/models/pms_availability.py index 3088f4274..3f705cefd 100644 --- a/pms/models/pms_availability.py +++ b/pms/models/pms_availability.py @@ -1,5 +1,7 @@ # Copyright 2021 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import datetime + from odoo import _, api, fields, models from odoo.exceptions import ValidationError @@ -55,6 +57,24 @@ class PmsAvailability(models.Model): readonly=True, compute="_compute_real_avail", ) + parent_avail_id = fields.Many2one( + string="Parent Avail", + help="Parent availability for this availability", + comodel_name="pms.availability", + ondelete="restrict", + compute="_compute_parent_avail_id", + store=True, + check_pms_properties=True, + ) + child_avail_ids = fields.One2many( + string="Child Avails", + help="Child availabilities for this availability", + comodel_name="pms.availability", + inverse_name="parent_avail_id", + compute="_compute_child_avail_ids", + store=True, + check_pms_properties=True, + ) _sql_constraints = [ ( @@ -69,11 +89,16 @@ class PmsAvailability(models.Model): "reservation_line_ids", "reservation_line_ids.occupies_availability", "room_type_id.total_rooms_count", + "parent_avail_id", + "parent_avail_id.reservation_line_ids", + "parent_avail_id.reservation_line_ids.occupies_availability", + "child_avail_ids", + "child_avail_ids.reservation_line_ids", + "child_avail_ids.reservation_line_ids.occupies_availability", ) def _compute_real_avail(self): for record in self: Rooms = self.env["pms.room"] - RoomLines = self.env["pms.reservation.line"] total_rooms = Rooms.search_count( [ ("room_type_id", "=", record.room_type_id.id), @@ -81,16 +106,159 @@ class PmsAvailability(models.Model): ] ) room_ids = record.room_type_id.mapped("room_ids.id") - rooms_not_avail = RoomLines.search_count( + count_rooms_not_avail = len( + record.get_rooms_not_avail( + checkin=record.date, + checkout=record.date + datetime.timedelta(1), + room_ids=room_ids, + pms_property_id=record.pms_property_id.id, + ) + ) + record.real_avail = total_rooms - count_rooms_not_avail + + @api.depends("reservation_line_ids", "reservation_line_ids.room_id") + def _compute_parent_avail_id(self): + for record in self: + parent_rooms = record.room_type_id.mapped("room_ids.parent_id.id") + if parent_rooms: + for room_id in parent_rooms: + room = self.env["pms.room"].browse(room_id) + parent_avail = self.env["pms.availability"].search( + [ + ("date", "=", record.date), + ("room_type_id", "=", room.room_type_id.id), + ("pms_property_id", "=", record.pms_property_id.id), + ] + ) + if parent_avail: + record.parent_avail_id = parent_avail + else: + record.parent_avail_id = self.env["pms.availability"].create( + { + "date": record.date, + "room_type_id": room.room_type_id.id, + "pms_property_id": record.pms_property_id.id, + } + ) + else: + record.parent_avail_id = False + + @api.depends("reservation_line_ids", "reservation_line_ids.room_id") + def _compute_child_avail_ids(self): + for record in self: + child_rooms = record.room_type_id.mapped("room_ids.child_ids.id") + if child_rooms: + for room_id in child_rooms: + room = self.env["pms.room"].browse(room_id) + child_avail = self.env["pms.availability"].search( + [ + ("date", "=", record.date), + ("room_type_id", "=", room.room_type_id.id), + ("pms_property_id", "=", record.pms_property_id.id), + ] + ) + if child_avail: + record.child_avail_ids = [(4, child_avail.id)] + else: + record.child_avail_ids = [ + ( + 0, + 0, + { + "date": record.date, + "room_type_id": room.room_type_id.id, + "pms_property_id": record.pms_property_id.id, + }, + ) + ] + else: + record.parent_avail_id = False + + @api.model + def get_rooms_not_avail( + self, checkin, checkout, room_ids, pms_property_id, current_lines=False + ): + RoomLines = self.env["pms.reservation.line"] + rooms = self.env["pms.room"].browse(room_ids) + occupied_room_ids = [] + for room in rooms.filtered("parent_id"): + if self.get_occupied_parent_rooms( + room=room.parent_id, + checkin=checkin, + checkout=checkout, + pms_property_id=room.pms_property_id.id, + ): + occupied_room_ids.append(room.id) + for room in rooms.filtered("child_ids"): + if self.get_occupied_child_rooms( + rooms=room.child_ids, + checkin=checkin, + checkout=checkout, + pms_property_id=room.pms_property_id.id, + ): + occupied_room_ids.append(room.id) + occupied_room_ids.extend( + RoomLines.search( [ - ("date", "=", record.date), + ("date", ">=", checkin), + ("date", "<=", checkout - datetime.timedelta(1)), ("room_id", "in", room_ids), - ("pms_property_id", "=", record.pms_property_id.id), + ("pms_property_id", "=", pms_property_id), + ("occupies_availability", "=", True), + ("id", "not in", current_lines if current_lines else []), + ] + ).mapped("room_id.id") + ) + return occupied_room_ids + + @api.model + def get_occupied_parent_rooms(self, room, checkin, checkout, pms_property_id): + RoomLines = self.env["pms.reservation.line"] + if ( + RoomLines.search_count( + [ + ("date", ">=", checkin), + ("date", "<=", checkout - datetime.timedelta(1)), + ("room_id", "=", room.id), + ("pms_property_id", "=", pms_property_id), ("occupies_availability", "=", True), - # ("id", "not in", current_lines if current_lines else []), ] ) - record.real_avail = total_rooms - rooms_not_avail + > 0 + ): + return True + if room.parent_id: + return self.get_occupied_parent_rooms( + room=room.parent_room_id, + checkin=checkin, + checkout=checkout, + ) + return False + + @api.model + def get_occupied_child_rooms(self, rooms, checkin, checkout, pms_property_id): + RoomLines = self.env["pms.reservation.line"] + if ( + RoomLines.search_count( + [ + ("date", ">=", checkin), + ("date", "<=", checkout - datetime.timedelta(1)), + ("room_id", "in", rooms.ids), + ("pms_property_id", "=", pms_property_id), + ("occupies_availability", "=", True), + ] + ) + > 0 + ): + return True + for room in rooms.filtered("child_ids"): + if self.get_occupied_child_rooms( + rooms=room.child_ids, + checkin=checkin, + checkout=checkout, + ): + return True + return False @api.constrains( "room_type_id", diff --git a/pms/models/pms_property.py b/pms/models/pms_property.py index 37f30f759..2656f4239 100644 --- a/pms/models/pms_property.py +++ b/pms/models/pms_property.py @@ -142,7 +142,6 @@ class PmsProperty(models.Model): pricelist_id = self.env.context.get("pricelist_id", False) room_type_id = self.env.context.get("room_type_id", False) - for pms_property in self: free_rooms = pms_property.get_real_free_rooms( checkin, checkout, current_lines @@ -191,7 +190,6 @@ class PmsProperty(models.Model): target_rooms = target_rooms.filtered( lambda r: r.room_type_id.id == room_type_id ) - capacity = self.env.context.get("capacity", False) if capacity: target_rooms = target_rooms.filtered(lambda r: r.capacity >= capacity) @@ -210,27 +208,20 @@ class PmsProperty(models.Model): lambda r: len(set(amenity_ids) - set(r.room_amenity_ids.ids)) == 0 ) - domain_avail = [ - ("date", ">=", checkin), - ("date", "<=", checkout - datetime.timedelta(1)), - ("pms_property_id", "=", self.id), - ] - if not current_lines: current_lines = [] - rooms_not_avail = ( - Avail.search(domain_avail) - .reservation_line_ids.filtered( - lambda l: l.occupies_availability and l.id and l.id not in current_lines - ) - .room_id.ids + rooms_not_avail_ids = Avail.get_rooms_not_avail( + checkin=checkin, + checkout=checkout, + room_ids=target_rooms.ids, + pms_property_id=self.id, + current_lines=current_lines, ) - domain_rooms = [("id", "in", target_rooms.ids)] - if rooms_not_avail: + if rooms_not_avail_ids: domain_rooms.append( - ("id", "not in", rooms_not_avail), + ("id", "not in", rooms_not_avail_ids), ) return self.env["pms.room"].search(domain_rooms) @@ -258,7 +249,7 @@ class PmsProperty(models.Model): ).date() room_type_id = self.env.context.get("room_type_id", False) pricelist_id = self.env.context.get("pricelist_id", False) - current_lines = self.env.context.get("current_lines", False) + current_lines = self.env.context.get("current_lines", []) pms_property = self.with_context( checkin=checkin, checkout=checkout, @@ -267,7 +258,6 @@ class PmsProperty(models.Model): pricelist_id=pricelist_id, ) count_free_rooms = len(pms_property.free_room_ids) - if current_lines and not isinstance(current_lines, list): current_lines = [current_lines] diff --git a/pms/models/pms_reservation_line.py b/pms/models/pms_reservation_line.py index 6331997fa..3a03a2a4e 100644 --- a/pms/models/pms_reservation_line.py +++ b/pms/models/pms_reservation_line.py @@ -470,3 +470,34 @@ class PmsReservationLine(models.Model): ) if duplicated: raise ValidationError(_("Duplicated reservation line date")) + + @api.constrains("room_id", "date") + def constrains_parent_room_avail(self): + for record in self: + if record.room_id and record.room_id.parent_id and record.date: + if self.env["pms.availability"].get_occupied_parent_rooms( + room=record.room_id.parent_id, + checkin=record.date, + checkout=record.date + datetime.timedelta(1), + pms_property_id=record.room_id.pms_property_id.id, + ): + raise ValidationError( + _("Room %s is occupied in this date by the parent room %s") + % record.room_id.display_name, + record.room_id.parent_id.display_name, + ) + + @api.constrains("room_id", "date") + def constrains_childs_room_avail(self): + for record in self: + if record.room_id and record.room_id.child_ids and record.date: + if self.env["pms.availability"].get_occupied_child_rooms( + rooms=record.room_id.child_ids, + checkin=record.date, + checkout=record.date + datetime.timedelta(1), + pms_property_id=record.room_id.pms_property_id.id, + ): + raise ValidationError( + _("Room %s is occupied in this date by the child rooms") + % record.room_id.display_name + ) diff --git a/pms/tests/__init__.py b/pms/tests/__init__.py index 4f21325fd..dc88cb8a9 100644 --- a/pms/tests/__init__.py +++ b/pms/tests/__init__.py @@ -19,22 +19,22 @@ # along with this program. If not, see . # ############################################################################## -from . import test_pms_reservation -from . import test_pms_pricelist -from . import test_pms_checkin_partner -from . import test_pms_sale_channel -from . import test_pms_folio -from . import test_pms_availability_plan_rules -from . import test_pms_room_type -from . import test_pms_room_type_class -from . import test_pms_board_service -from . import test_pms_wizard_massive_changes -from . import test_pms_booking_engine -from . import test_pms_res_users -from . import test_pms_room -from . import test_pms_folio_invoice -from . import test_pms_folio_sale_line -from . import test_pms_wizard_split_join_swap_reservation -from . import test_product_template -from . import test_pms_multiproperty +# from . import test_pms_reservation +# from . import test_pms_pricelist +# from . import test_pms_checkin_partner +# from . import test_pms_sale_channel +# from . import test_pms_folio +# from . import test_pms_availability_plan_rules +# from . import test_pms_room_type +# from . import test_pms_room_type_class +# from . import test_pms_board_service +# from . import test_pms_wizard_massive_changes +# from . import test_pms_booking_engine +# from . import test_pms_res_users +# from . import test_pms_room +# from . import test_pms_folio_invoice +# from . import test_pms_folio_sale_line +# from . import test_pms_wizard_split_join_swap_reservation +# from . import test_product_template +# from . import test_pms_multiproperty from . import test_shared_room diff --git a/pms/tests/test_shared_room.py b/pms/tests/test_shared_room.py index 0541a22a9..2408805c4 100644 --- a/pms/tests/test_shared_room.py +++ b/pms/tests/test_shared_room.py @@ -1,6 +1,7 @@ import datetime from odoo import fields +from odoo.exceptions import ValidationError from .common import TestPms @@ -24,7 +25,7 @@ class TestPmsSharedRoom(TestPms): ) # create room type - self.room_type_shared = self.env["pms.room.type"].create( + self.room_type_test = self.env["pms.room.type"].create( { "pms_property_ids": [self.pms_property1.id], "name": "Shared Test", @@ -47,9 +48,8 @@ class TestPmsSharedRoom(TestPms): { "pms_property_id": self.pms_property1.id, "name": "Shared 101", - "room_type_id": self.room_type_shared.id, + "room_type_id": self.room_type_test.id, "capacity": 2, - "extra_beds_allowed": 1, } ) @@ -85,7 +85,7 @@ class TestPmsSharedRoom(TestPms): } ) - def test_not_avail_beds_with_room_occupied(self): + def test_count_avail_beds_with_room_occupied(self): """ Check that not allow to create a bed reservation with a room occupied ---------------- @@ -118,7 +118,7 @@ class TestPmsSharedRoom(TestPms): "Beds avaialbility should be 0 for room occupied", ) - def test_not_avail_shared_room_with_one_bed_occupied(self): + def test_count_avail_shared_room_with_one_bed_occupied(self): """ Check that not allow to create a shared room reservation with a bed occupied ---------------- @@ -145,17 +145,132 @@ class TestPmsSharedRoom(TestPms): self.pms_property1.with_context( checkin=today, checkout=tomorrow, - room_type_id=self.room_type_shared.id, + room_type_id=self.room_type_test.id, ).availability, 0, "Shared Room avaialbility should be 0 if it has a bed occupied", ) - def test_avail_beds_with_one_bed_occupied(self): + def test_avail_in_room_type_with_shared_rooms(self): """ - Check the avail of a bed when it has a room with other beds occupied + Check that a shared room's bed occupied not + affect the avail on other rooms with the + same room type ---------------- - Create a room1's bed (it has 2 beds) reservation and check that the beds avail = 1 + Create other room like room_type_test (room2) + Create a room1's bed reservation and check that the room1 + Check that room_type_test real avail is 1 + """ + + # ARRANGE + today = fields.date.today() + tomorrow = fields.date.today() + datetime.timedelta(days=1) + self.room2 = self.env["pms.room"].create( + { + "pms_property_id": self.pms_property1.id, + "name": "Shared 102", + "room_type_id": self.room_type_test.id, + "capacity": 2, + } + ) + + # ACT + self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "preferred_room_id": self.r1bed1.id, + "checkin": today, + "checkout": tomorrow, + "pms_property_id": self.pms_property1.id, + } + ) + + # ASSERT + self.assertEqual( + self.pms_property1.with_context( + checkin=today, + checkout=tomorrow, + room_type_id=self.room_type_test.id, + ).availability, + 1, + "Room not shared affect by the shared room's avail with the same type", + ) + + def test_count_avail_beds_with_one_bed_occupied(self): + """ + Check the avail of a bed when it has + a room with other beds occupied + ---------------- + Create a room1's bed (it has 2 beds) + reservation and check that the beds avail = 1 + """ + + # ARRANGE + today = fields.date.today() + tomorrow = fields.date.today() + datetime.timedelta(days=1) + + # ACT + res1 = self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "preferred_room_id": self.r1bed1.id, + "checkin": today, + "checkout": tomorrow, + "pms_property_id": self.pms_property1.id, + } + ) + res1.flush() + # ASSERT + self.assertEqual( + self.pms_property1.with_context( + checkin=today, + checkout=tomorrow, + room_type_id=self.room_type_bed.id, + ).availability, + 1, + "Beds avaialbility should be 1 if it has 1 of 2 beds occupied", + ) + + def test_not_avail_beds_with_room_occupied(self): + """ + Check that not allow to select a bed with a room occupied + ---------------- + Create a room1 reservation and check that the beds are not available + """ + + # ARRANGE + today = fields.date.today() + tomorrow = fields.date.today() + datetime.timedelta(days=1) + + # ACT + self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "preferred_room_id": self.room1.id, + "checkin": today, + "checkout": tomorrow, + "pms_property_id": self.pms_property1.id, + } + ) + + # ASSERT + self.assertNotIn( + self.r1bed1.id, + self.pms_property1.with_context( + checkin=today, + checkout=tomorrow, + room_type_id=self.room_type_bed.id, + ).free_room_ids.ids, + "room's bed should not be available " "because the entire room is reserved", + ) + + def test_not_avail_shared_room_with_one_bed_occupied(self): + """ + Check that not allow to select a shared + room with a bed occupied + ---------------- + Create a room1's bed reservation and check + that the room1 real avail is not available """ # ARRANGE @@ -173,6 +288,193 @@ class TestPmsSharedRoom(TestPms): } ) + # ASSERT + self.assertNotIn( + self.room1.id, + self.pms_property1.with_context( + checkin=today, + checkout=tomorrow, + room_type_id=self.room_type_bed.id, + ).free_room_ids.ids, + "Entire Shared room should not be available " + "becouse it has a bed occupied", + ) + + def test_avail_beds_with_one_bed_occupied(self): + """ + Check the select of a bed when it has a + room with other beds occupied + ---------------- + Create a room1's bed (it has 2 beds) reservation + and check that the other bed is avail + """ + + # ARRANGE + today = fields.date.today() + tomorrow = fields.date.today() + datetime.timedelta(days=1) + + # ACT + self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "preferred_room_id": self.r1bed1.id, + "checkin": today, + "checkout": tomorrow, + "pms_property_id": self.pms_property1.id, + } + ) + + # ASSERT + self.assertIn( + self.r1bed2.id, + self.pms_property1.with_context( + checkin=today, + checkout=tomorrow, + room_type_id=self.room_type_bed.id, + ).free_room_ids.ids, + "The bed2 of the shared room should be available", + ) + + def test_not_allowed_reservation_in_bed_with_room_occuppied(self): + """ + Check the constrain that not allow to create a reservation in a bed in a + room with other reservation like shared + ---------------- + Create a room1's reservation and the try to create a reservation + in the room1's bed, we expect an error + """ + + # ARRANGE + today = fields.date.today() + tomorrow = fields.date.today() + datetime.timedelta(days=1) + + self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "preferred_room_id": self.room1.id, + "checkin": today, + "checkout": tomorrow, + "pms_property_id": self.pms_property1.id, + } + ) + + # ACT & ASSERT + with self.assertRaises( + ValidationError, + msg="Reservation created on a bed whose room was already occupied", + ): + r_test = self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "preferred_room_id": self.r1bed1.id, + "checkin": today, + "checkout": tomorrow, + "pms_property_id": self.pms_property1.id, + } + ) + r_test.flush() + + def test_not_allowed_reservation_in_shared_room_with_bed_occuppied(self): + """ + Check the constrain that not allow to create a reservation + in a shared room in a bed reservation + ---------------- + Create a room1's bed reservation and the try to create + a reservation in the room1, we expect an error + """ + + # ARRANGE + today = fields.date.today() + tomorrow = fields.date.today() + datetime.timedelta(days=1) + + self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "preferred_room_id": self.r1bed1.id, + "checkin": today, + "checkout": tomorrow, + "pms_property_id": self.pms_property1.id, + } + ) + + # ACT & ASSERT + with self.assertRaises( + ValidationError, + msg="Reservation created in a full shared " + "room that already had beds occupied", + ): + r_test = self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "preferred_room_id": self.room1.id, + "checkin": today, + "checkout": tomorrow, + "pms_property_id": self.pms_property1.id, + } + ) + r_test.flush() + + def check_room_shared_availability_released_when_canceling_bed_reservations(self): + """ + Check that check availability in shared room is + released when canceling bed reservations + ---------------- + Create a room1's bed reservation and then cancel it, + check that the room1 real avail is 1 + """ + + # ARRANGE + today = fields.date.today() + tomorrow = fields.date.today() + datetime.timedelta(days=1) + + # ACT + r1 = self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "preferred_room_id": self.r1bed1.id, + "checkin": today, + "checkout": tomorrow, + "pms_property_id": self.pms_property1.id, + } + ) + r1.action_cancel() + + # ASSERT + self.assertEqual( + self.pms_property1.with_context( + checkin=today, + checkout=tomorrow, + room_type_id=self.room_type_test.id, + ).availability, + 1, + "The parent room avail dont update " "when cancel child room reservation", + ) + + def check_bed_availability_released_when_canceling_parent_room_reservations(self): + """ + Check that check availability in child room is + released when canceling the parent rooms + ---------------- + Create a room1 reservation and then cancel it, + check that the beds real avail is 2 + """ + + # ARRANGE + today = fields.date.today() + tomorrow = fields.date.today() + datetime.timedelta(days=1) + + # ACT + r1 = self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "preferred_room_id": self.room1.id, + "checkin": today, + "checkout": tomorrow, + "pms_property_id": self.pms_property1.id, + } + ) + r1.action_cancel() + # ASSERT self.assertEqual( self.pms_property1.with_context( @@ -180,6 +482,6 @@ class TestPmsSharedRoom(TestPms): checkout=tomorrow, room_type_id=self.room_type_bed.id, ).availability, - 1, - "Shared Room avaialbility should be 0 if it has a bed occupied", + 2, + "The child room avail dont update when " "cancel parent room reservation", ) From d2a45b70ef89320fff967c9418fa04f61185e6a6 Mon Sep 17 00:00:00 2001 From: Dario Lodeiros Date: Wed, 11 Aug 2021 12:15:27 +0200 Subject: [PATCH 3/3] [IMP] Views Shared room --- pms/models/pms_reservation_line.py | 31 ------------------------- pms/models/pms_room.py | 28 +++++++++++++++++++++-- pms/tests/__init__.py | 36 +++++++++++++++--------------- pms/views/pms_room_views.xml | 26 +++++++++++++++++++++ 4 files changed, 70 insertions(+), 51 deletions(-) diff --git a/pms/models/pms_reservation_line.py b/pms/models/pms_reservation_line.py index 3a03a2a4e..6331997fa 100644 --- a/pms/models/pms_reservation_line.py +++ b/pms/models/pms_reservation_line.py @@ -470,34 +470,3 @@ class PmsReservationLine(models.Model): ) if duplicated: raise ValidationError(_("Duplicated reservation line date")) - - @api.constrains("room_id", "date") - def constrains_parent_room_avail(self): - for record in self: - if record.room_id and record.room_id.parent_id and record.date: - if self.env["pms.availability"].get_occupied_parent_rooms( - room=record.room_id.parent_id, - checkin=record.date, - checkout=record.date + datetime.timedelta(1), - pms_property_id=record.room_id.pms_property_id.id, - ): - raise ValidationError( - _("Room %s is occupied in this date by the parent room %s") - % record.room_id.display_name, - record.room_id.parent_id.display_name, - ) - - @api.constrains("room_id", "date") - def constrains_childs_room_avail(self): - for record in self: - if record.room_id and record.room_id.child_ids and record.date: - if self.env["pms.availability"].get_occupied_child_rooms( - rooms=record.room_id.child_ids, - checkin=record.date, - checkout=record.date + datetime.timedelta(1), - pms_property_id=record.room_id.pms_property_id.id, - ): - raise ValidationError( - _("Room %s is occupied in this date by the child rooms") - % record.room_id.display_name - ) diff --git a/pms/models/pms_room.py b/pms/models/pms_room.py index 59ad462ef..0c03d5cc7 100644 --- a/pms/models/pms_room.py +++ b/pms/models/pms_room.py @@ -60,7 +60,6 @@ class PmsRoom(models.Model): help="Child rooms of the room", comodel_name="pms.room", inverse_name="parent_id", - ondelete="restrict", check_pms_properties=True, ) ubication_id = fields.Many2one( @@ -87,7 +86,13 @@ class PmsRoom(models.Model): column2="amenity_id", check_pms_properties=True, ) - + is_shared_room = fields.Boolean( + string="Is a Shared Room", + help="allows you to reserve units " " smaller than the room itself (eg beds)", + compute="_compute_is_shared_room", + readonly=False, + store=True, + ) description_sale = fields.Text( string="Sale Description", help="A description of the Product that you want to communicate to " @@ -105,6 +110,14 @@ class PmsRoom(models.Model): ) ] + @api.depends("child_ids") + def _compute_is_shared_room(self): + for record in self: + if record.child_ids: + record.is_shared_room = True + elif not record.is_shared_room: + record.is_shared_room = False + def name_get(self): result = [] for room in self: @@ -130,6 +143,17 @@ class PmsRoom(models.Model): ) ) + @api.constrains("is_shared_room") + def _check_shared_room(self): + for record in self: + if record.is_shared_room and not record.child_ids: + raise ValidationError( + _( + "The reservation units are required \ + on shared rooms." + ) + ) + @api.model def _check_adults(self, reservation, service_line_ids=False): for line in reservation.reservation_line_ids: diff --git a/pms/tests/__init__.py b/pms/tests/__init__.py index dc88cb8a9..4f21325fd 100644 --- a/pms/tests/__init__.py +++ b/pms/tests/__init__.py @@ -19,22 +19,22 @@ # along with this program. If not, see . # ############################################################################## -# from . import test_pms_reservation -# from . import test_pms_pricelist -# from . import test_pms_checkin_partner -# from . import test_pms_sale_channel -# from . import test_pms_folio -# from . import test_pms_availability_plan_rules -# from . import test_pms_room_type -# from . import test_pms_room_type_class -# from . import test_pms_board_service -# from . import test_pms_wizard_massive_changes -# from . import test_pms_booking_engine -# from . import test_pms_res_users -# from . import test_pms_room -# from . import test_pms_folio_invoice -# from . import test_pms_folio_sale_line -# from . import test_pms_wizard_split_join_swap_reservation -# from . import test_product_template -# from . import test_pms_multiproperty +from . import test_pms_reservation +from . import test_pms_pricelist +from . import test_pms_checkin_partner +from . import test_pms_sale_channel +from . import test_pms_folio +from . import test_pms_availability_plan_rules +from . import test_pms_room_type +from . import test_pms_room_type_class +from . import test_pms_board_service +from . import test_pms_wizard_massive_changes +from . import test_pms_booking_engine +from . import test_pms_res_users +from . import test_pms_room +from . import test_pms_folio_invoice +from . import test_pms_folio_sale_line +from . import test_pms_wizard_split_join_swap_reservation +from . import test_product_template +from . import test_pms_multiproperty from . import test_shared_room diff --git a/pms/views/pms_room_views.xml b/pms/views/pms_room_views.xml index 05b7fcc4c..2fa9aca2d 100644 --- a/pms/views/pms_room_views.xml +++ b/pms/views/pms_room_views.xml @@ -38,6 +38,11 @@ + + @@ -66,6 +71,27 @@ + + + + + + + + + + + + + +