From ef60556ed6667214ea3264b161724a2ec802a79b Mon Sep 17 00:00:00 2001 From: Eric Antones Date: Tue, 16 Feb 2021 17:26:15 +0100 Subject: [PATCH] [IMP] pms: make the room type class code required and unique (#52) --- pms/demo/pms_master_data.xml | 2 + pms/models/pms_room_type_class.py | 71 +++- pms/tests/__init__.py | 1 + pms/tests/test_pms_folio.py | 4 +- pms/tests/test_pms_pricelist.py | 2 +- pms/tests/test_pms_pricelist_priority.py | 2 +- pms/tests/test_pms_reservation.py | 4 +- pms/tests/test_pms_room.py | 2 +- pms/tests/test_pms_room_type.py | 1 + .../test_pms_room_type_availability_rules.py | 2 +- pms/tests/test_pms_room_type_class.py | 386 ++++++++++++++++++ pms/tests/test_pms_wizard_folio.py | 2 +- pms/tests/test_pms_wizard_massive_changes.py | 2 +- 13 files changed, 468 insertions(+), 13 deletions(-) create mode 100644 pms/tests/test_pms_room_type_class.py diff --git a/pms/demo/pms_master_data.xml b/pms/demo/pms_master_data.xml index f838f6dfe..a19bd690e 100644 --- a/pms/demo/pms_master_data.xml +++ b/pms/demo/pms_master_data.xml @@ -92,9 +92,11 @@ Room + RO Conference + CO diff --git a/pms/models/pms_room_type_class.py b/pms/models/pms_room_type_class.py index ba448c839..f78e9a1ad 100644 --- a/pms/models/pms_room_type_class.py +++ b/pms/models/pms_room_type_class.py @@ -1,7 +1,9 @@ # 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 fields, models +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError class PmsRoomTypeClass(models.Model): @@ -22,10 +24,69 @@ class PmsRoomTypeClass(models.Model): "pms.property", string="Properties", required=False, ondelete="restrict" ) room_type_ids = fields.One2many("pms.room.type", "class_id", "Types") - code_class = fields.Char("Code") + code_class = fields.Char("Code", required=True) active = fields.Boolean("Active", default=True) sequence = fields.Integer("Sequence", default=0) - _sql_constraints = [ - ("code_class_unique", "unique(code_class)", "Room Class Code must be unique!") - ] + @api.model + def get_unique_by_property_code(self, pms_property_id, code_class=None): + """ + :param pms_property_id: property ID + :param code_class: room type code (optional) + :return: - recordset of + - all the pms.room.type.class of the pms_property_id + if code_class not defined + - one or 0 pms.room.type.class if code_class defined + - ValidationError if more than one code_class found by + the same pms_property_id + """ + # TODO: similiar code as room.type -> unify + domain = [] + if code_class: + domain += ["&", ("code_class", "=", code_class)] + domain += [ + "|", + ("pms_property_ids", "in", pms_property_id), + ("pms_property_ids", "=", False), + ] + records = self.search(domain) + res, res_priority = {}, {} + for rec in records: + res_priority.setdefault(rec.code_class, -1) + priority = rec.pms_property_ids and 1 or 0 + if priority > res_priority[rec.code_class]: + res.setdefault(rec.code_class, rec.id) + res[rec.code_class], res_priority[rec.code_class] = rec.id, priority + elif priority == res_priority[rec.code_class]: + raise ValidationError( + _( + "Integrity error: There's multiple room types " + "with the same code %s and properties" + ) + % rec.code_class + ) + return self.browse(list(res.values())) + + @api.constrains("code_class", "pms_property_ids") + def _check_code_property_uniqueness(self): + # TODO: similiar code as room.type -> unify + msg = _( + "Already exists another room type class with the same code and properties" + ) + for rec in self: + if not rec.pms_property_ids: + if self.search( + [ + ("id", "!=", rec.id), + ("code_class", "=", rec.code_class), + ("pms_property_ids", "=", False), + ] + ): + raise ValidationError(msg) + else: + for pms_property in rec.pms_property_ids: + other = rec.get_unique_by_property_code( + pms_property.id, rec.code_class + ) + if other and other != rec: + raise ValidationError(msg) diff --git a/pms/tests/__init__.py b/pms/tests/__init__.py index 2b46906e6..ae06865ac 100644 --- a/pms/tests/__init__.py +++ b/pms/tests/__init__.py @@ -27,6 +27,7 @@ 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 +from . import test_pms_room_type_class from . import test_pms_wizard_massive_changes from . import test_pms_wizard_folio from . import test_pms_res_users diff --git a/pms/tests/test_pms_folio.py b/pms/tests/test_pms_folio.py index 079fe2ebc..c4e2c0cca 100644 --- a/pms/tests/test_pms_folio.py +++ b/pms/tests/test_pms_folio.py @@ -26,7 +26,9 @@ class TestPmsFolio(TestHotel): ) # create room type class - self.room_type_class = self.env["pms.room.type.class"].create({"name": "Room"}) + self.room_type_class = self.env["pms.room.type.class"].create( + {"name": "Room", "code_class": "ROOM"} + ) # create room type self.room_type_double = self.env["pms.room.type"].create( diff --git a/pms/tests/test_pms_pricelist.py b/pms/tests/test_pms_pricelist.py index 3e42862f5..7f201405e 100644 --- a/pms/tests/test_pms_pricelist.py +++ b/pms/tests/test_pms_pricelist.py @@ -31,7 +31,7 @@ class TestPmsPricelist(common.TransactionCase): } ) self.room_type_class = self.env["pms.room.type.class"].create( - {"name": "Room Class"} + {"name": "Room Class", "code_class": "ROOM"} ) self.room_type = self.env["pms.room.type"].create( diff --git a/pms/tests/test_pms_pricelist_priority.py b/pms/tests/test_pms_pricelist_priority.py index 635f3a67c..fc07803bc 100644 --- a/pms/tests/test_pms_pricelist_priority.py +++ b/pms/tests/test_pms_pricelist_priority.py @@ -38,7 +38,7 @@ class TestPmsPricelistRules(common.TransactionCase): ) self.room_type_class = self.env["pms.room.type.class"].create( - {"name": "Room Class"} + {"name": "Room Class", "code_class": "ROOM"} ) self.room_type = self.env["pms.room.type"].create( diff --git a/pms/tests/test_pms_reservation.py b/pms/tests/test_pms_reservation.py index 7e0b671f7..31bb389b9 100644 --- a/pms/tests/test_pms_reservation.py +++ b/pms/tests/test_pms_reservation.py @@ -26,7 +26,9 @@ class TestPmsReservations(TestHotel): ) # create room type class - self.room_type_class = self.env["pms.room.type.class"].create({"name": "Room"}) + self.room_type_class = self.env["pms.room.type.class"].create( + {"name": "Room", "code_class": "ROOM"} + ) # create room type self.room_type_double = self.env["pms.room.type"].create( diff --git a/pms/tests/test_pms_room.py b/pms/tests/test_pms_room.py index 29223a39d..2c3dec771 100644 --- a/pms/tests/test_pms_room.py +++ b/pms/tests/test_pms_room.py @@ -28,7 +28,7 @@ class TestPmsRoom(common.TransactionCase): ) self.room_type_class = self.env["pms.room.type.class"].create( - {"name": "Room Class"} + {"name": "Room Class", "code_class": "ROOM"} ) self.room_type = self.env["pms.room.type"].create( diff --git a/pms/tests/test_pms_room_type.py b/pms/tests/test_pms_room_type.py index 362f43368..f04696a94 100644 --- a/pms/tests/test_pms_room_type.py +++ b/pms/tests/test_pms_room_type.py @@ -704,6 +704,7 @@ class TestRoomTypeCodePropertyUniqueness(TestRoomType): room_type_class = self.env["pms.room.type.class"].create( { "name": "Room Type Class", + "code_class": "ROOM", "pms_property_ids": [ (4, self.p2.id), ], diff --git a/pms/tests/test_pms_room_type_availability_rules.py b/pms/tests/test_pms_room_type_availability_rules.py index 111ef0dca..60e92a600 100644 --- a/pms/tests/test_pms_room_type_availability_rules.py +++ b/pms/tests/test_pms_room_type_availability_rules.py @@ -41,7 +41,7 @@ class TestPmsRoomTypeAvailabilityRules(TestHotel): ) # pms.room.type.class self.test_room_type_class = self.env["pms.room.type.class"].create( - {"name": "Room"} + {"name": "Room", "code_class": "ROOM"} ) # pms.room.type diff --git a/pms/tests/test_pms_room_type_class.py b/pms/tests/test_pms_room_type_class.py new file mode 100644 index 000000000..0a54d336a --- /dev/null +++ b/pms/tests/test_pms_room_type_class.py @@ -0,0 +1,386 @@ +# 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 TestRoomTypeClass(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"), + } + ) + 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"), + } + ) + + +class TestRoomTypeClassCodePropertyIntegrity(TestRoomTypeClass): + # external integrity + def test_external_case_01(self): + """ + PRE: - room type class cl1 exists + - cl1 has code c1 + - cl1 has property p1 + - p1 has company m1 + ACT: - create a new cl2 class + - cl2 has code c1 + - cl2 has property p1 + - p1 has company m1 + POST: - Integrity error: the room type already exists + - cl2 not created + """ + # ARRANGE + # cl1 + self.env["pms.room.type.class"].create( + { + "name": "Room type class cl1", + "code_class": "c1", + "pms_property_ids": [(6, 0, [self.p1.id])], + } + ) + + # ACT & ASSERT + with self.assertRaises( + ValidationError, msg="The room type class has been created and it shouldn't" + ): + # cl2 + self.env["pms.room.type.class"].create( + { + "name": "Room type class cl2", + "code_class": "c1", + "pms_property_ids": [(6, 0, [self.p1.id])], + } + ) + + def test_external_case_02(self): + """ + PRE: - room type class cl1 exists + - cl1 has code c1 + - cl1 has property p1 + - p1 has company m1 + ACT: - create a new cl2 class + - cl2 has code c1 + - cl2 has property p1, p2, p3 + - p1, p2 has company m1 + - p3 has company m2 + POST: - Integrity error: the room type class already exists + - cl2 not created + """ + # ARRANGE + # cl1 + self.env["pms.room.type.class"].create( + { + "name": "Room type class cl1", + "code_class": "c1", + "pms_property_ids": [(6, 0, [self.p1.id])], + } + ) + + # ACT & ASSERT + with self.assertRaises( + ValidationError, msg="The room type class has been created and it shouldn't" + ): + # cl2 + self.env["pms.room.type.class"].create( + { + "name": "Room type class cl2", + "code_class": "c1", + "pms_property_ids": [(6, 0, [self.p1.id, self.p2.id, self.p3.id])], + } + ) + + +class TestRoomTypeClassCodePropertyUniqueness(TestRoomTypeClass): + # test with one room type class + def test_single_case_01(self): + """ + PRE: - room type class cl1 exists + - cl1 has code c1 + - cl1 has 2 properties p1 and p2 + - p1 and p2 have the same company m1 + ACT: - search room type class with code c1 and property p1 + - p1 has company m1 + POST: - only cl1 room type class found + """ + # ARRANGE + cl1 = self.env["pms.room.type.class"].create( + { + "name": "Room type class cl1", + "code_class": "c1", + "pms_property_ids": [(6, 0, [self.p1.id, self.p3.id])], + } + ) + + # ACT + room_type_class = self.env["pms.room.type.class"].get_unique_by_property_code( + self.p1.id, "c1" + ) + + # ASSERT + self.assertEqual( + room_type_class.id, cl1.id, "Expected room type class not found" + ) + + def test_single_case_02(self): + """ + PRE: - room type class cl1 exists + - cl1 has code c1 + - cl1 has 2 properties p1 and p3 + - p1 and p2 have different companies + - p1 have company m1 and p3 have company m2 + ACT: - search room type class with code c1 and property p1 + - p1 has company m1 + POST: - only cl1 room type found + """ + # ARRANGE + cl1 = self.env["pms.room.type.class"].create( + { + "name": "Room type class cl1", + "code_class": "c1", + "pms_property_ids": [(6, 0, [self.p1.id, self.p3.id])], + } + ) + + # ACT + room_type_class = self.env["pms.room.type.class"].get_unique_by_property_code( + self.p1.id, "c1" + ) + + # ASSERT + self.assertEqual( + room_type_class.id, cl1.id, "Expected room type class not found" + ) + + def test_single_case_03(self): + """ + PRE: - room type class cl1 exists + - cl1 has code c1 + - cl1 with 2 properties p1 and p2 + - p1 and p2 have same company m1 + ACT: - search room type class with code c1 and property p3 + - p3 have company m2 + POST: - no room type found + """ + # ARRANGE + # cl1 + self.env["pms.room.type.class"].create( + { + "name": "Room type class cl1", + "code_class": "c1", + "pms_property_ids": [(6, 0, [self.p1.id, self.p2.id])], + } + ) + + # ACT + room_type_class = self.env["pms.room.type.class"].get_unique_by_property_code( + self.p3.id, "c1" + ) + + # ASSERT + self.assertFalse( + room_type_class, "Room type class found but it should not have found any" + ) + + def test_single_case_04(self): + """ + PRE: - room type class cl1 exists + - cl1 has code c1 + - cl1 properties are null + ACT: - search room type class with code c1 and property p1 + - p1 have company m1 + POST: - only cl1 room type class found + """ + # ARRANGE + cl1 = self.env["pms.room.type.class"].create( + { + "name": "Room type class cl1", + "code_class": "c1", + "pms_property_ids": False, + } + ) + + # ACT + room_type_class = self.env["pms.room.type.class"].get_unique_by_property_code( + self.p1.id, "c1" + ) + + # ASSERT + self.assertEqual( + room_type_class.id, cl1.id, "Expected room type class not found" + ) + + # tests with more than one room type class + def test_multiple_case_01(self): + """ + PRE: - room type class cl1 exists + - cl1 has code c1 + - cl1 has 2 properties p1 and p2 + - p1 and p2 have the same company m1 + - room type class cl2 exists + - cl2 has code c1 + - cl2 has no properties + ACT: - search room type class with code c1 and property p1 + - p1 have company m1 + POST: - only cl1 room type class found + """ + # ARRANGE + cl1 = self.env["pms.room.type.class"].create( + { + "name": "Room type class cl1", + "code_class": "c1", + "pms_property_ids": [(6, 0, [self.p1.id, self.p3.id])], + } + ) + # cl2 + self.env["pms.room.type.class"].create( + { + "name": "Room type class cl2", + "code_class": "c1", + "pms_property_ids": False, + } + ) + + # ACT + room_type_class = self.env["pms.room.type.class"].get_unique_by_property_code( + self.p1.id, "c1" + ) + + # ASSERT + self.assertEqual( + room_type_class.id, cl1.id, "Expected room type class not found" + ) + + def test_multiple_case_02(self): + """ + PRE: - room type class cl1 exists + - cl1 has code c1 + - cl1 has property p1 + - p1 have the company m1 + - room type class cl2 exists + - cl2 has code c1 + - cl2 has no properties + ACT: - search room type class with code c1 and property p2 + - p2 have company m1 + POST: - only cl1 room type class found + """ + # ARRANGE + # cl1 + self.env["pms.room.type.class"].create( + { + "name": "Room type class cl1", + "code_class": "c1", + "pms_property_ids": [(6, 0, [self.p1.id])], + } + ) + cl2 = self.env["pms.room.type.class"].create( + { + "name": "Room type class cl2", + "code_class": "c1", + "pms_property_ids": False, + } + ) + + # ACT + room_type_class = self.env["pms.room.type.class"].get_unique_by_property_code( + self.p2.id, "c1" + ) + + # ASSERT + self.assertEqual( + room_type_class.id, cl2.id, "Expected room type class not found" + ) + + def test_multiple_case_03(self): + """ + PRE: - room type class cl1 exists + - cl1 has code c1 + - cl1 has property p1 + - p1 have the company m1 + - room type class cl2 exists + - cl2 has code c1 + - cl2 has no properties + ACT: - search room type class with code c1 and property p3 + - p3 have company m2 + POST: - only cl2 room type class found + """ + # ARRANGE + # cl1 + self.env["pms.room.type.class"].create( + { + "name": "Room type class cl1", + "code_class": "c1", + "pms_property_ids": [(6, 0, [self.p1.id])], + } + ) + cl2 = self.env["pms.room.type.class"].create( + { + "name": "Room type class cl2", + "code_class": "c1", + "pms_property_ids": False, + } + ) + + # ACT + room_type_class = self.env["pms.room.type.class"].get_unique_by_property_code( + self.p3.id, "c1" + ) + + # ASSERT + self.assertEqual( + room_type_class.id, cl2.id, "Expected room type class not found" + ) + + def test_multiple_case_04(self): + """ + PRE: - room type class cl1 exists + - cl1 has code c1 + - cl1 has property p1 + - p1 have the company m1 + - room type cl2 exists + - cl2 has code c1 + - cl2 has no properties + ACT: - search room type class with code c1 and property p3 + - p3 have company m2 + POST: - r2 room type class found + """ + # ARRANGE + # cl1 + self.env["pms.room.type.class"].create( + { + "name": "Room type class cl1", + "code_class": "c1", + "pms_property_ids": [(6, 0, [self.p1.id])], + } + ) + cl2 = self.env["pms.room.type.class"].create( + { + "name": "Room type class cl2", + "code_class": "c1", + "pms_property_ids": False, + } + ) + + # ACT + room_type_class = self.env["pms.room.type.class"].get_unique_by_property_code( + self.p3.id, "c1" + ) + + # ASSERT + self.assertEqual(room_type_class.id, cl2.id, "Expected room type not found") diff --git a/pms/tests/test_pms_wizard_folio.py b/pms/tests/test_pms_wizard_folio.py index 27d783017..6fb60a273 100644 --- a/pms/tests/test_pms_wizard_folio.py +++ b/pms/tests/test_pms_wizard_folio.py @@ -41,7 +41,7 @@ class TestPmsWizardMassiveChanges(TestHotel): # CREATION OF ROOM TYPE CLASS self.test_room_type_class = self.env["pms.room.type.class"].create( - {"name": "Room"} + {"name": "Room", "code_class": "ROOM"} ) self.test_room_type_class.flush() diff --git a/pms/tests/test_pms_wizard_massive_changes.py b/pms/tests/test_pms_wizard_massive_changes.py index def56b5ab..bc947b20e 100644 --- a/pms/tests/test_pms_wizard_massive_changes.py +++ b/pms/tests/test_pms_wizard_massive_changes.py @@ -34,7 +34,7 @@ class TestPmsWizardMassiveChanges(TestHotel): ) # pms.room.type.class self.test_room_type_class = self.env["pms.room.type.class"].create( - {"name": "Room"} + {"name": "Room", "code_class": "ROOM"} ) # pms.room.type