diff --git a/pms/models/pms_cancelation_rule.py b/pms/models/pms_cancelation_rule.py index 5d079e8f4..ccfae8a6b 100644 --- a/pms/models/pms_cancelation_rule.py +++ b/pms/models/pms_cancelation_rule.py @@ -10,7 +10,7 @@ class PmsCancelationRule(models.Model): _description = "Cancelation Rules" # Fields declaration - name = fields.Char("Amenity Name", translate=True, required=True) + name = fields.Char(string="Cancelation Rule", translate=True, required=True) pricelist_ids = fields.One2many( "product.pricelist", "cancelation_rule_id", "Pricelist that use this rule" ) diff --git a/pms/models/pms_room_type.py b/pms/models/pms_room_type.py index 105ffdaa6..d2c2c1f8f 100644 --- a/pms/models/pms_room_type.py +++ b/pms/models/pms_room_type.py @@ -1,5 +1,6 @@ # Copyright 2017 Alexandre Díaz # Copyright 2017 Dario Lodeiros +# Copyright 2021 Eric Antones # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import _, api, fields, models @@ -70,20 +71,84 @@ class PmsRoomType(models.Model): "Use `-1` for managing no quota.", ) - _sql_constraints = [ - ( - "code_type_pms_unique", - "unique(code_type)", - "Room Type Code must be unique", - ), - ] - # Constraints and onchanges @api.depends("room_ids", "room_ids.active") def _compute_total_rooms(self): for record in self: record.total_rooms_count = len(record.room_ids) + @api.model + def get_unique_by_code_property(self, code_type, pms_property_id): + """ + :param code_type: room type code + :param pms_property_id: property ID + :return: one or 0 room types ValidationError if more than one found + """ + company_id = self.env["pms.property"].browse(pms_property_id).company_id.id + records = self.search( + [ + "&", + ("code_type", "=", code_type), + "|", + ("pms_property_ids", "in", pms_property_id), + "|", + "&", + ("pms_property_ids", "=", False), + ("company_id", "=", company_id), + "&", + ("pms_property_ids", "=", False), + ("company_id", "=", False), + ] + ) + res, res_priority = self, -1 + for rec in records: + priority = (rec.pms_property_ids and 2) or (rec.company_id and 1 or 0) + if priority > res_priority: + res, res_priority = rec, priority + elif priority == res_priority: + raise ValidationError( + _( + "Integrity error: There's multiple room types " + "with the same code and properties" + ) + ) + return res + + @api.constrains("pms_property_ids", "company_id") + def _check_property_company_integrity(self): + for rec in self: + if rec.company_id and rec.pms_property_ids: + property_companies = rec.pms_property_ids.mapped("company_id") + if len(property_companies) > 1 or rec.company_id != property_companies: + raise ValidationError( + _( + "The company of the properties must match " + "the company on the room type" + ) + ) + + @api.constrains("code_type", "pms_property_ids", "company_id") + def _check_code_property_company_uniqueness(self): + msg = _("Already exists another room type with the same code and properties") + for rec in self: + if not rec.pms_property_ids: + if self.search( + [ + ("id", "!=", rec.id), + ("code_type", "=", rec.code_type), + ("pms_property_ids", "=", False), + ("company_id", "=", rec.company_id.id), + ] + ): + raise ValidationError(msg) + else: + for pms_property in rec.pms_property_ids: + if ( + rec.get_unique_by_code_property(rec.code_type, pms_property.id) + != rec + ): + raise ValidationError(msg) + # ORM Overrides @api.model def create(self, vals): diff --git a/pms/static/src/js/inherited_abstract_web_client.js b/pms/static/src/js/inherited_abstract_web_client.js index fc736a237..249700140 100644 --- a/pms/static/src/js/inherited_abstract_web_client.js +++ b/pms/static/src/js/inherited_abstract_web_client.js @@ -11,15 +11,14 @@ odoo.define("pms.AbstractWebClient", function (require) { var current_pms_property_id = session.user_pms_properties.current_pms_property[0]; if (!state.pms_pids) { - state.pms_pids = - utils.get_cookie("pms_pids") !== null - ? utils.get_cookie("pms_pids") - : String(current_pms_property_id); + state.pms_pids = utils.get_cookie("pms_pids") + ? utils.get_cookie("pms_pids") + : String(current_pms_property_id); } var statePmsPropertyIDS = _.map(state.pms_pids.split(","), function ( pms_pid ) { - return parseInt(pms_pid); + return parseInt(pms_pid, 10); }); var userPmsPropertyIDS = _.map( session.user_pms_properties.allowed_pms_properties, diff --git a/pms/static/src/js/views/list/list_controller.js b/pms/static/src/js/views/list/list_controller.js index 347a9fd25..58b995764 100644 --- a/pms/static/src/js/views/list/list_controller.js +++ b/pms/static/src/js/views/list/list_controller.js @@ -13,7 +13,8 @@ odoo.define("pms.ListController", function (require) { ListController.include({ renderButtons: function () { - this._super.apply(this, arguments); // Sets this.$buttons + // Sets this.$buttons + this._super.apply(this, arguments); var self = this; if (this.modelName === "pms.reservation") { this.$buttons.append( diff --git a/pms/static/src/js/widgets/switch_property_menu.js b/pms/static/src/js/widgets/switch_property_menu.js index e0dd880c1..56b4e2e95 100644 --- a/pms/static/src/js/widgets/switch_property_menu.js +++ b/pms/static/src/js/widgets/switch_property_menu.js @@ -50,7 +50,7 @@ odoo.define("web.SwitchPmsMenu", function (require) { ) .split(",") .map(function (id) { - return parseInt(id); + return parseInt(id, 10); }); this.user_pms_properties = session.user_pms_properties.allowed_pms_properties; @@ -74,9 +74,9 @@ odoo.define("web.SwitchPmsMenu", function (require) { */ _onSwitchPmsPropertyClick: function (ev) { if ( - ev.type == "keydown" && - ev.which != $.ui.keyCode.ENTER && - ev.which != $.ui.keyCode.SPACE + ev.type === "keydown" && + ev.which !== $.ui.keyCode.ENTER && + ev.which !== $.ui.keyCode.SPACE ) { return; } @@ -124,9 +124,9 @@ odoo.define("web.SwitchPmsMenu", function (require) { */ _onTogglePmsPropertyClick: function (ev) { if ( - ev.type == "keydown" && - ev.which != $.ui.keyCode.ENTER && - ev.which != $.ui.keyCode.SPACE + ev.type === "keydown" && + ev.which !== $.ui.keyCode.ENTER && + ev.which !== $.ui.keyCode.SPACE ) { return; } diff --git a/pms/tests/__init__.py b/pms/tests/__init__.py index 00f69c03e..be9caec85 100644 --- a/pms/tests/__init__.py +++ b/pms/tests/__init__.py @@ -26,3 +26,4 @@ from . import test_pms_checkin_partner from . import test_pms_sale_channel from . import test_pms_folio from . import test_pms_room_type_availability_rules +from . import test_pms_room_type \ No newline at end of file diff --git a/pms/tests/test_pms_room_type.py b/pms/tests/test_pms_room_type.py new file mode 100644 index 000000000..f65b5fc92 --- /dev/null +++ b/pms/tests/test_pms_room_type.py @@ -0,0 +1,678 @@ +# Copyright 2021 Eric Antones +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.exceptions import ValidationError +from odoo.tests.common import SavepointCase + + +class TestRoomType(SavepointCase): + def setUp(self): + super().setUp() + self.p1 = self.browse_ref("pms.main_pms_property") + self.m1 = self.p1.company_id + self.p2 = self.env["pms.property"].create( + { + "name": "p2", + "company_id": self.m1.id, + "default_pricelist_id": self.ref("product.list0"), + "default_restriction_id": self.ref( + "pms.main_pms_room_type_restriction" + ), + } + ) + self.m2 = self.env["res.company"].create( + { + "name": "Company m2", + } + ) + self.p3 = self.env["pms.property"].create( + { + "name": "p3", + "company_id": self.m2.id, + "default_pricelist_id": self.ref("product.list0"), + "default_restriction_id": self.ref( + "pms.main_pms_room_type_restriction" + ), + } + ) + + +class TestRoomTypeCodePropertyIntegrity(TestRoomType): + # internal integrity + def test_internal_case_01(self): + """ + PRE: - room type r1 does not exists + ACT: - create a new r1 room + - r1 has code c1 + - r1 has property p1 + - p1 has company m1 + - r1 has company m1 + POST: - r1 created + """ + # ARRANGE & ACT & ASSERT + try: + # r1 + self.env["pms.room.type"].create( + { + "name": "Room type r1", + "code_type": "c1", + "pms_property_ids": [(6, 0, [self.p1.id])], + "company_id": self.m1.id, + } + ) + except ValidationError: + self.fail("Room type not created when it should") + + def test_internal_case_02(self): + """ + PRE: - room type r1 does not exists + ACT: - create a new r1 room + - r1 has code c1 + - r1 has property p1 + - p1 has company m1 + - r1 has company m2 + POST: - Integrity error, p1 has company m1 and room type m2 + - r1 not created + """ + # ARRANGE & ACT & ASSERT + with self.assertRaises( + ValidationError, msg="The room type has been created and it shouldn't" + ): + # r1 + self.env["pms.room.type"].create( + { + "name": "Room type r1", + "code_type": "c1", + "pms_property_ids": [(6, 0, [self.p1.id])], + "company_id": self.m2.id, + } + ) + + def test_internal_case_03(self): + """ + PRE: - room type r1 does not exists + ACT: - create a new r1 room + - r1 has code c1 + - r1 has property p1 and p3 + - p1 has company m1 + - p3 has company m2 + - r1 has company m2 + POST: - Integrity error, p1 has company m1 and room type m2 + - r1 not created + """ + # ARRANGE & ACT & ASSERT + with self.assertRaises( + ValidationError, msg="The room type has been created and it shouldn't" + ): + # r1 + self.env["pms.room.type"].create( + { + "name": "Room type r1", + "code_type": "c1", + "pms_property_ids": [(6, 0, [self.p1.id, self.p3.id])], + "company_id": self.m2.id, + } + ) + + def test_internal_case_04(self): + """ + PRE: - room type r1 does not exists + ACT: - create a new r1 room + - r1 has code c1 + - r1 has property p1 and p3 + - p1 has company m1 + - p3 has company m2 + - r1 has no company + POST: - r1 created + """ + # ARRANGE & ACT & ASSERT + try: + # r1 + self.env["pms.room.type"].create( + { + "name": "Room type r1", + "code_type": "c1", + "pms_property_ids": [(6, 0, [self.p1.id, self.p3.id])], + "company_id": False, + } + ) + except ValidationError: + self.fail("Room type not created when it should") + + # external integrity + def test_external_case_01(self): + """ + PRE: - room type r1 exists + - r1 has code c1 + - r1 has property p1 + - p1 has company m1 + - r1 has no company + ACT: - create a new r2 room + - r2 has code c1 + - r2 has property p1 + - p1 has company m1 + - r2 has no company + POST: - Integrity error: the room type already exists + - r2 not created + """ + # ARRANGE + # r1 + self.env["pms.room.type"].create( + { + "name": "Room type r1", + "code_type": "c1", + "pms_property_ids": [(6, 0, [self.p1.id])], + "company_id": False, + } + ) + + # ACT & ASSERT + with self.assertRaises( + ValidationError, msg="The room type has been created and it shouldn't" + ): + # r2 + self.env["pms.room.type"].create( + { + "name": "Room type r1", + "code_type": "c1", + "pms_property_ids": [(6, 0, [self.p1.id])], + "company_id": False, + } + ) + + def test_external_case_02(self): + """ + PRE: - room type r1 exists + - r1 has code c1 + - r1 has property p1 + - p1 has company m1 + - r1 has no company + ACT: - create a new r2 room + - r2 has code c1 + - r2 has property p1 + - p1 has company m1 + - r2 has company m1 + POST: - Integrity error: the room type already exists + - r2 not created + """ + # ARRANGE + # r1 + self.env["pms.room.type"].create( + { + "name": "Room type r1", + "code_type": "c1", + "pms_property_ids": [(6, 0, [self.p1.id])], + "company_id": False, + } + ) + + # ACT & ASSERT + with self.assertRaises( + ValidationError, msg="The room type has been created and it shouldn't" + ): + # r2 + self.env["pms.room.type"].create( + { + "name": "Room type r1", + "code_type": "c1", + "pms_property_ids": [(6, 0, [self.p1.id])], + "company_id": self.m1.id, + } + ) + + def test_external_case_03(self): + """ + PRE: - room type r1 exists + - r1 has code c1 + - r1 has property p1 + - p1 has company m1 + - r1 has company m1 + ACT: - create a new r2 room + - r2 has code c1 + - r2 has property p1, p2, p3 + - p1, p2 has company m1 + - p3 has company m2 + - r2 has no company + POST: - Integrity error: the room type already exists + - r2 not created + """ + # ARRANGE + # r1 + self.env["pms.room.type"].create( + { + "name": "Room type r1", + "code_type": "c1", + "pms_property_ids": [(6, 0, [self.p1.id])], + "company_id": self.m1.id, + } + ) + + # ACT & ASSERT + with self.assertRaises( + ValidationError, msg="The room type has been created and it shouldn't" + ): + # r2 + self.env["pms.room.type"].create( + { + "name": "Room type r1", + "code_type": "c1", + "pms_property_ids": [(6, 0, [self.p1.id, self.p2.id, self.p3.id])], + "company_id": False, + } + ) + + +class TestRoomTypeCodePropertyUniqueness(TestRoomType): + # test with one room type + def test_single_case_01(self): + """ + PRE: - room type r1 exists + - r1 has code c1 + - r1 with 2 properties p1 and p2 + - p1 and p2 have the same company m1 + - r1 has no company + ACT: - search room type with code c1 and property p1 + - p1 has company m1 + POST: - only r1 room type found + """ + # ARRANGE + r1 = self.env["pms.room.type"].create( + { + "name": "Room type r1", + "code_type": "c1", + "pms_property_ids": [(6, 0, [self.p1.id, self.p3.id])], + "company_id": False, + } + ) + + # ACT + room_type = self.env["pms.room.type"].get_unique_by_code_property( + "c1", self.p1.id + ) + + # ASSERT + self.assertEqual(room_type.id, r1.id, "Expected room type not found") + + def test_single_case_02(self): + """ + PRE: - room type r1 exists + - r1 has code c1 + - r1 with 2 properties p1 and p3 + - p1 and p2 have differmt companies + - p1 have company m1 and p3 have company m2 + - r1 has no company + ACT: - search room type with code c1 and property p1 + - p1 has company m1 + POST: - only r1 room type found + """ + # ARRANGE + r1 = self.env["pms.room.type"].create( + { + "name": "Room type r1", + "code_type": "c1", + "pms_property_ids": [(6, 0, [self.p1.id, self.p3.id])], + "company_id": False, + } + ) + + # ACT + room_type = self.env["pms.room.type"].get_unique_by_code_property( + "c1", self.p1.id + ) + + # ASSERT + self.assertEqual(room_type.id, r1.id, "Expected room type not found") + + def test_single_case_03(self): + """ + PRE: - room type r1 exists + - r1 has code c1 + - r1 with 2 properties p1 and p2 + - p1 and p2 have same company m1 + - r1 has no company + ACT: - search room type with code c1 and property p3 + - p3 have company m2 + POST: - no room type found + """ + # ARRANGE + # r1 + self.env["pms.room.type"].create( + { + "name": "Room type r1", + "code_type": "c1", + "pms_property_ids": [(6, 0, [self.p1.id, self.p2.id])], + "company_id": False, + } + ) + + # ACT + room_type = self.env["pms.room.type"].get_unique_by_code_property( + "c1", self.p3.id + ) + + # ASSERT + self.assertFalse(room_type, "Room type found but it should have not found any") + + def test_single_case_04(self): + """ + PRE: - room type r1 exists + - r1 has code c1 + - r1 properties are null + - r1 company is m1 + ACT: - search room type with code c1 and property p1 + - p1 have company m1 + POST: - only r1 room type found + """ + # ARRANGE + r1 = self.env["pms.room.type"].create( + { + "name": "Room type r1", + "code_type": "c1", + "pms_property_ids": False, + "company_id": self.m1.id, + } + ) + + # ACT + room_type = self.env["pms.room.type"].get_unique_by_code_property( + "c1", self.p1.id + ) + + # ASSERT + self.assertEqual(room_type.id, r1.id, "Expected room type not found") + + def test_single_case_05(self): + """ + PRE: - room type r1 exists + - r1 has code c1 + - r1 properties are null + - r1 company is m1 + ACT: - search room type with code c1 and property p3 + - p3 have company m2 + POST: - no room type found + """ + # ARRANGE + # r1 + self.env["pms.room.type"].create( + { + "name": "Room type r1", + "code_type": "c1", + "pms_property_ids": False, + "company_id": self.m1.id, + } + ) + + # ACT + room_type = self.env["pms.room.type"].get_unique_by_code_property( + "c1", self.p3.id + ) + + # ASSERT + self.assertFalse(room_type, "Room type found but it should have not found any") + + # tests with more than one room type + def test_multiple_case_01(self): + """ + PRE: - room type r1 exists + - r1 has code c1 + - r1 with 2 properties p1 and p2 + - p1 and p2 have the same company m1 + - r1 has no company + - room type r2 exists + - r2 has code c1 + - r2 has no properties + - r2 has no company + ACT: - search room type with code c1 and property p1 + - p1 have company m1 + POST: - only r1 room type found + """ + # ARRANGE + r1 = self.env["pms.room.type"].create( + { + "name": "Room type r1", + "code_type": "c1", + "pms_property_ids": [(6, 0, [self.p1.id, self.p3.id])], + "company_id": False, + } + ) + # r2 + self.env["pms.room.type"].create( + { + "name": "Room type r2", + "code_type": "c1", + "pms_property_ids": False, + "company_id": False, + } + ) + + # ACT + room_type = self.env["pms.room.type"].get_unique_by_code_property( + "c1", self.p1.id + ) + + # ASSERT + self.assertEqual(room_type.id, r1.id, "Expected room type not found") + + def test_multiple_case_02(self): + """ + PRE: - room type r1 exists + - r1 has code c1 + - r1 has property p1 + - p1 have the company m1 + - r1 has no company + - room type r2 exists + - r2 has code c1 + - r2 has no properties + - r2 has no company + ACT: - search room type with code c1 and property p2 + - p2 have company m1 + POST: - only r1 room type found + """ + # ARRANGE + # r1 + self.env["pms.room.type"].create( + { + "name": "Room type r1", + "code_type": "c1", + "pms_property_ids": [(6, 0, [self.p1.id])], + "company_id": False, + } + ) + r2 = self.env["pms.room.type"].create( + { + "name": "Room type r2", + "code_type": "c1", + "pms_property_ids": False, + "company_id": False, + } + ) + + # ACT + room_type = self.env["pms.room.type"].get_unique_by_code_property( + "c1", self.p2.id + ) + + # ASSERT + self.assertEqual(room_type.id, r2.id, "Expected room type not found") + + def test_multiple_case_03(self): + """ + PRE: - room type r1 exists + - r1 has code c1 + - r1 has property p1 + - p1 have the company m1 + - r1 has no company + - room type r2 exists + - r2 has code c1 + - r2 has no properties + - r2 has no company + ACT: - search room type with code c1 and property p3 + - p3 have company m2 + POST: - only r2 room type found + """ + # ARRANGE + # r1 + self.env["pms.room.type"].create( + { + "name": "Room type r1", + "code_type": "c1", + "pms_property_ids": [(6, 0, [self.p1.id])], + "company_id": False, + } + ) + r2 = self.env["pms.room.type"].create( + { + "name": "Room type r2", + "code_type": "c1", + "pms_property_ids": False, + "company_id": False, + } + ) + + # ACT + room_type = self.env["pms.room.type"].get_unique_by_code_property( + "c1", self.p3.id + ) + + # ASSERT + self.assertEqual(room_type.id, r2.id, "Expected room type not found") + + def test_multiple_case_04(self): + """ + PRE: - room type r1 exists + - r1 has code c1 + - r1 has property p1 + - p1 have the company m1 + - r1 has no company + - room type r2 exists + - r2 has code c1 + - r2 has no properties + - r2 has company m1 + ACT: - search room type with code c1 and property p3 + - p3 have company m2 + POST: - no room type found + """ + # ARRANGE + # r1 + self.env["pms.room.type"].create( + { + "name": "Room type r1", + "code_type": "c1", + "pms_property_ids": [(6, 0, [self.p1.id])], + "company_id": False, + } + ) + # r2 + self.env["pms.room.type"].create( + { + "name": "Room type r2", + "code_type": "c1", + "pms_property_ids": False, + "company_id": self.m1.id, + } + ) + + # ACT + room_type = self.env["pms.room.type"].get_unique_by_code_property( + "c1", self.p3.id + ) + + # ASSERT + self.assertFalse(room_type, "Room type found but it should have not found any") + + def test_multiple_case_05(self): + """ + PRE: - room type r1 exists + - r1 has code c1 + - r1 has property p1 + - p1 have the company m1 + - r1 has no company + - room type r2 exists + - r2 has code c1 + - r2 has no properties + - r2 has company m2 + ACT: - search room type with code c1 and property p3 + - p3 have company m2 + POST: - r2 room type found + """ + # ARRANGE + # r1 + self.env["pms.room.type"].create( + { + "name": "Room type r1", + "code_type": "c1", + "pms_property_ids": [(6, 0, [self.p1.id])], + "company_id": False, + } + ) + r2 = self.env["pms.room.type"].create( + { + "name": "Room type r2", + "code_type": "c1", + "pms_property_ids": False, + "company_id": self.m2.id, + } + ) + + # ACT + room_type = self.env["pms.room.type"].get_unique_by_code_property( + "c1", self.p3.id + ) + + # ASSERT + self.assertEqual(room_type.id, r2.id, "Expected room type not found") + + def test_multiple_case_06(self): + """ + PRE: - room type r1 exists + - r1 has code c1 + - r1 has property p1 + - p1 have the company m1 + - r1 has no company + - room type r2 exists + - r2 has code c1 + - r2 has no properties + - r2 has company m1 + - room type r3 exists + - r3 has code c1 + - r3 has no properties + - r3 has no company + ACT: - search room type with code c1 and property p3 + - p3 have company m2 + POST: - r3 room type found + """ + # ARRANGE + # r1 + self.env["pms.room.type"].create( + { + "name": "Room type r1", + "code_type": "c1", + "pms_property_ids": [(6, 0, [self.p1.id])], + "company_id": False, + } + ) + # r2 + self.env["pms.room.type"].create( + { + "name": "Room type r2", + "code_type": "c1", + "pms_property_ids": False, + "company_id": self.m1.id, + } + ) + r3 = self.env["pms.room.type"].create( + { + "name": "Room type r3", + "code_type": "c1", + "pms_property_ids": False, + "company_id": False, + } + ) + + # ACT + room_type = self.env["pms.room.type"].get_unique_by_code_property( + "c1", self.p3.id + ) + + # ASSERT + self.assertEqual(room_type.id, r3.id, "Expected room type not found") diff --git a/pms/views/pms_floor_views.xml b/pms/views/pms_floor_views.xml index 6eb084474..384c5f9ca 100644 --- a/pms/views/pms_floor_views.xml +++ b/pms/views/pms_floor_views.xml @@ -35,7 +35,7 @@ tree,form + pms.room_type.form @@ -28,6 +32,7 @@ +