diff --git a/pms/demo/pms_reservation.xml b/pms/demo/pms_reservation.xml index fb6c62c86..af310f088 100644 --- a/pms/demo/pms_reservation.xml +++ b/pms/demo/pms_reservation.xml @@ -10,6 +10,7 @@ + @@ -18,6 +19,7 @@ + @@ -41,6 +43,7 @@ + @@ -49,6 +52,7 @@ 1 + @@ -57,6 +61,7 @@ 2 + @@ -65,6 +70,7 @@ 1 + @@ -84,6 +90,7 @@ + @@ -101,6 +108,7 @@ + @@ -110,6 +118,7 @@ + @@ -118,6 +127,7 @@ 1 + @@ -127,6 +137,7 @@ 1 + @@ -136,6 +147,7 @@ + @@ -144,6 +156,7 @@ 1 + @@ -154,6 +167,7 @@ + @@ -169,6 +183,7 @@ /> + @@ -179,6 +194,7 @@ + @@ -187,6 +203,7 @@ 2 + @@ -196,6 +213,7 @@ + @@ -211,6 +229,7 @@ /> + @@ -220,6 +239,7 @@ + @@ -229,6 +249,7 @@ 1 + @@ -238,6 +259,7 @@ + @@ -246,6 +268,7 @@ 2 + @@ -256,6 +279,7 @@ + @@ -272,6 +296,7 @@ /> + @@ -288,6 +313,7 @@ + @@ -304,6 +330,7 @@ /> + @@ -328,6 +355,7 @@ + @@ -336,6 +364,7 @@ + @@ -343,6 +372,7 @@ + @@ -350,6 +380,7 @@ + diff --git a/pms/models/pms_folio.py b/pms/models/pms_folio.py index eff7eca71..bdce61a81 100644 --- a/pms/models/pms_folio.py +++ b/pms/models/pms_folio.py @@ -17,6 +17,7 @@ class PmsFolio(models.Model): _description = "PMS Folio" _inherit = ["mail.thread", "mail.activity.mixin", "portal.mixin"] _order = "date_order" + _check_company_auto = True # Default Methods ang Gets def name_get(self): @@ -60,6 +61,7 @@ class PmsFolio(models.Model): readonly=False, states={"done": [("readonly", True)]}, help="Room reservation detail.", + check_company=True, ) number_of_rooms = fields.Integer( "Number of Rooms", @@ -73,6 +75,10 @@ class PmsFolio(models.Model): states={"done": [("readonly", True)]}, help="Services detail provide to customer and it will " "include in main Invoice.", + check_company=True, + domain="['|'," + "('pms_property_id','=',pms_property_id)," + "('pms_property_id','=',False)]", ) sale_line_ids = fields.One2many( "folio.sale.line", @@ -121,6 +127,9 @@ class PmsFolio(models.Model): store=True, readonly=False, help="Pricelist for current folio.", + domain="['|'," + "(pms_property_id, 'in', 'pms_property_ids')," + "('pms_property_ids','=',False)]", ) commission = fields.Float( string="Commission", @@ -231,7 +240,12 @@ class PmsFolio(models.Model): fiscal_position_id = fields.Many2one( "account.fiscal.position", string="Fiscal Position" ) - closure_reason_id = fields.Many2one("room.closure.reason") + closure_reason_id = fields.Many2one( + "room.closure.reason", + domain="['|'," + "(pms_property_id, 'in', 'pms_property_ids')," + "('pms_property_ids', '=', False)]", + ) segmentation_ids = fields.Many2many( "res.partner.category", string="Segmentation", ondelete="restrict" ) @@ -482,11 +496,13 @@ class PmsFolio(models.Model): @api.depends("partner_id") def _compute_partner_invoice_ids(self): - for folio in self: + for folio in self.filtered("partner_id"): folio.partner_invoice_ids = False addr = folio.partner_id.address_get(["invoice"]) if not addr["invoice"] in folio.partner_invoice_ids.ids: folio.partner_invoice_ids = [(4, addr["invoice"])] + # Avoid CacheMissing + self.filtered(lambda f: not f.partner_invoice_ids).partner_invoice_ids = False @api.depends("partner_id") def _compute_payment_term_id(self): @@ -1249,6 +1265,18 @@ class PmsFolio(models.Model): _("The Sale Channel does not correspond to the agency's") ) + @api.constrains( + "closure_reason_id", + ) + def _check_property_integrity(self): + for rec in self: + if rec.pms_property_id: + if ( + rec.pms_property_id.id + not in rec.closure_reason_id.pms_property_ids.ids + ): + raise ValidationError(_("Property not allowed")) + @api.model def _prepare_down_payment_section_line(self, **optional_values): """ diff --git a/pms/models/pms_reservation.py b/pms/models/pms_reservation.py index 835437dfd..507906805 100644 --- a/pms/models/pms_reservation.py +++ b/pms/models/pms_reservation.py @@ -82,6 +82,7 @@ class PmsReservation(models.Model): tracking=True, ondelete="restrict", copy=False, + check_company=True, ) board_service_room_id = fields.Many2one( "pms.board.service.room.type", @@ -90,6 +91,10 @@ class PmsReservation(models.Model): store=True, readonly=False, tracking=True, + domain="[" + "'|'," + "('pms_property_ids', 'in', pms_property_id)," + "('pms_property_ids', '=', False)]", ) room_type_id = fields.Many2one( "pms.room.type", @@ -100,6 +105,9 @@ class PmsReservation(models.Model): store=True, readonly=False, copy=False, + domain="['|'," + "('pms_property_ids', 'in', pms_property_id)," + "('pms_property_ids', '=', False)]", ) partner_id = fields.Many2one( "res.partner", @@ -128,12 +136,20 @@ class PmsReservation(models.Model): store=True, readonly=False, ) - closure_reason_id = fields.Many2one(related="folio_id.closure_reason_id") + closure_reason_id = fields.Many2one( + related="folio_id.closure_reason_id", + domain="['|'," + "(pms_property_id, 'in', 'pms_property_ids')," + "('pms_property_ids', '=', False)]", + ) company_id = fields.Many2one( related="folio_id.company_id", string="Company", store=True, readonly=True ) pms_property_id = fields.Many2one( "pms.property", + related="folio_id.pms_property_id", + store=True, + readonly=False, default=lambda self: self.env.user.get_active_property_ids()[0], ) reservation_line_ids = fields.One2many( @@ -150,6 +166,10 @@ class PmsReservation(models.Model): compute="_compute_service_ids", store=True, readonly=False, + check_company=True, + domain="['|'," + "('pms_property_id', '=', pms_property_id)," + "('pms_property_id', '=', False)]", ) pricelist_id = fields.Many2one( "product.pricelist", @@ -159,6 +179,9 @@ class PmsReservation(models.Model): store=True, readonly=False, tracking=True, + domain="['|'," + "('pms_property_ids', 'in', pms_property_id)," + "('pms_property_ids', '=', False)]", ) show_update_pricelist = fields.Boolean( string="Has Pricelist Changed", @@ -1325,6 +1348,45 @@ class PmsReservation(models.Model): if record.agency_id and not record.agency_id.is_agency: raise ValidationError(_("booking agency with wrong configuration: ")) + @api.constrains("pms_property_id", "preferred_room_id") + def _check_room_property_integrity(self): + for record in self: + if record.pms_property_id and record.preferred_room_id.pms_property_id: + if record.pms_property_id != record.preferred_room_id.pms_property_id: + raise ValidationError( + _("Property doesn't match with room property") + ) + + @api.constrains("pms_property_id", "room_type_id") + def _check_room_type_property_integrity(self): + for record in self: + if record.pms_property_id and record.room_type_id.pms_property_ids: + if ( + record.pms_property_id.id + not in record.room_type_id.pms_property_ids.ids + ): + raise ValidationError(_("Property isn't allowed in Room Type")) + + @api.constrains("pms_property_id", "pricelist_id") + def _check_pricelist_property_integrity(self): + for record in self: + if record.pms_property_id and record.pricelist_id.pms_property_ids: + if ( + record.pms_property_id.id + not in record.pricelist_id.pms_property_ids.ids + ): + raise ValidationError(_("Property isn't allowed in Pricelist")) + + @api.constrains("pms_property_id", "board_service_room_id") + def _check_board_service_property_integrity(self): + for record in self: + if record.pms_property_id and record.board_service_room_id.pms_property_ids: + if ( + record.pms_property_id.id + not in record.board_service_room_id.pms_property_ids.ids + ): + raise ValidationError(_("Property isn't allowed in Board Service")) + # Action methods def open_folio(self): @@ -1409,15 +1471,16 @@ class PmsReservation(models.Model): def create(self, vals): if "folio_id" in vals: folio = self.env["pms.folio"].browse(vals["folio_id"]) - elif "partner_id" in vals or "agency_id" in vals: + elif "pms_property_id" in vals and ( + "partner_id" in vals or "agency_id" in vals + ): folio_vals = { - "partner_id": int(vals.get("partner_id")) - if vals.get("partner_id") - else False, - "agency_id": int(vals.get("agency_id")) - if vals.get("agency_id") - else False, + "pms_property_id": vals["pms_property_id"], } + if vals.get("partner_id"): + folio_vals["partner_id"] = vals.get("partner_id") + elif vals.get("agency_id"): + folio_vals["agency_id"] = vals.get("agency_id") # Create the folio in case of need # (To allow to create reservations direct) folio = self.env["pms.folio"].create(folio_vals) @@ -1427,6 +1490,10 @@ class PmsReservation(models.Model): "reservation_type": vals.get("reservation_type"), } ) + else: + raise ValidationError( + _("The client and Property are mandatory in the reservation") + ) if vals.get("name", _("New")) == _("New") or "name" not in vals: vals["name"] = self.env["ir.sequence"]._next_sequence_property( pms_property_id=folio.pms_property_id.id, diff --git a/pms/models/pms_room_type.py b/pms/models/pms_room_type.py index a7f2e3d6a..b46b4122e 100644 --- a/pms/models/pms_room_type.py +++ b/pms/models/pms_room_type.py @@ -137,7 +137,7 @@ class PmsRoomType(models.Model): ) return self.browse(list(res.values())) - @api.constrains("pms_property_ids") + @api.constrains("pms_property_ids", "class_id") def _check_integrity_property_class(self): for record in self: if record.pms_property_ids and record.class_id.pms_property_ids: diff --git a/pms/tests/test_pms_folio.py b/pms/tests/test_pms_folio.py index 47f43094e..c07bc1cc8 100644 --- a/pms/tests/test_pms_folio.py +++ b/pms/tests/test_pms_folio.py @@ -3,6 +3,7 @@ import datetime from freezegun import freeze_time from odoo import fields +from odoo.exceptions import ValidationError from .common import TestHotel @@ -60,8 +61,34 @@ class TestPmsFolio(TestHotel): } ) + def create_multiproperty_scenario(self): + self.property1 = self.env["pms.property"].create( + { + "name": "Property_1", + "company_id": self.env.ref("base.main_company").id, + "default_pricelist_id": self.env.ref("product.list0").id, + } + ) + + self.property2 = self.env["pms.property"].create( + { + "name": "Property_2", + "company_id": self.env.ref("base.main_company").id, + "default_pricelist_id": self.env.ref("product.list0").id, + } + ) + + self.property3 = self.env["pms.property"].create( + { + "name": "Property_3", + "company_id": self.env.ref("base.main_company").id, + "default_pricelist_id": self.env.ref("product.list0").id, + } + ) + def test_commission_and_partner_correct(self): # ARRANGE + self.create_common_scenario() PmsFolio = self.env["pms.folio"] PmsReservation = self.env["pms.reservation"] PmsPartner = self.env["res.partner"] @@ -83,6 +110,7 @@ class TestPmsFolio(TestHotel): folio = PmsFolio.create( { "agency_id": agency.id, + "pms_property_id": self.property.id, } ) @@ -202,3 +230,23 @@ class TestPmsFolio(TestHotel): date=fields.date.today(), ) self.assertEqual(r_test.folio_id.pending_amount, left_to_pay) + + def test_closure_reason_property(self): + self.create_multiproperty_scenario() + cl_reason = self.env["room.closure.reason"].create( + { + "name": "closure_reason_test", + "pms_property_ids": [ + (4, self.property1.id), + (4, self.property2.id), + ], + } + ) + + with self.assertRaises(ValidationError): + self.env["pms.folio"].create( + { + "pms_property_id": self.property3.id, + "closure_reason_id": cl_reason.id, + } + ) diff --git a/pms/tests/test_pms_reservation.py b/pms/tests/test_pms_reservation.py index b0dbcbf95..28d744d6d 100644 --- a/pms/tests/test_pms_reservation.py +++ b/pms/tests/test_pms_reservation.py @@ -69,6 +69,40 @@ class TestPmsReservations(TestHotel): ) self.demo_user = self.env.ref("base.user_admin") + def create_multiproperty_scenario(self): + self.property1 = self.env["pms.property"].create( + { + "name": "Property_1", + "company_id": self.env.ref("base.main_company").id, + "default_pricelist_id": self.env.ref("product.list0").id, + } + ) + + self.property2 = self.env["pms.property"].create( + { + "name": "Property_2", + "company_id": self.env.ref("base.main_company").id, + "default_pricelist_id": self.env.ref("product.list0").id, + } + ) + + self.property3 = self.env["pms.property"].create( + { + "name": "Property_3", + "company_id": self.env.ref("base.main_company").id, + "default_pricelist_id": self.env.ref("product.list0").id, + } + ) + self.room_type_class = self.env["pms.room.type.class"].create( + {"name": "Room Class", "code_class": "RCTEST"} + ) + + self.board_service = self.env["pms.board.service"].create( + { + "name": "Board Service Test", + } + ) + @freeze_time("1980-11-01") def test_create_reservation_start_date(self): # TEST CASE @@ -788,3 +822,84 @@ class TestPmsReservations(TestHotel): self.assertEqual( r1.state, "done", "The reservation status should be done after checkout." ) + + def test_multiproperty_checks(self): + """ + # TEST CASE + Multiproperty checks in reservation + +---------------+------+------+------+----+----+ + | reservation | property1 | + +---------------+------+------+------+----+----+ + | room | property2 | + | room_type | property2, property3 | + | board_service | property2, property3 | + | pricelist | property2, property3 | + +---------------+------+------+------+----+----+ + """ + # ARRANGE + self.create_multiproperty_scenario() + host = self.env["res.partner"].create( + { + "name": "Miguel", + "phone": "654667733", + "email": "miguel@example.com", + } + ) + self.reservation_test = self.env["pms.reservation"].create( + { + "checkin": fields.date.today(), + "checkout": fields.date.today() + datetime.timedelta(days=1), + "pms_property_id": self.property1.id, + "partner_id": host.id, + } + ) + + room_type_test = self.env["pms.room.type"].create( + { + "pms_property_ids": [ + (4, self.property3.id), + (4, self.property2.id), + ], + "name": "Single", + "code_type": "SIN", + "class_id": self.room_type_class.id, + "list_price": 30, + } + ) + + room = self.env["pms.room"].create( + { + "name": "Room 101", + "pms_property_id": self.property2.id, + "room_type_id": room_type_test.id, + } + ) + + pricelist = self.env["product.pricelist"].create( + { + "name": "pricelist_test", + "pms_property_ids": [ + (4, self.property2.id), + (4, self.property3.id), + ], + } + ) + + board_service_room_type = self.env["pms.board.service.room.type"].create( + { + "pms_board_service_id": self.board_service.id, + "pms_room_type_id": room_type_test.id, + "pms_property_ids": [self.property2.id, self.property3.id], + } + ) + test_cases = [ + {"preferred_room_id": room.id}, + {"room_type_id": room_type_test.id}, + {"pricelist_id": pricelist.id}, + {"board_service_room_id": board_service_room_type.id}, + ] + + for test_case in test_cases: + with self.subTest(k=test_case): + with self.assertRaises(ValidationError): + self.reservation_test.write(test_case) diff --git a/pms/tests/test_pms_sale_channel.py b/pms/tests/test_pms_sale_channel.py index 1cab21ead..d5eadd372 100644 --- a/pms/tests/test_pms_sale_channel.py +++ b/pms/tests/test_pms_sale_channel.py @@ -9,8 +9,19 @@ from .common import TestHotel @freeze_time("2010-01-01") class TestPmsSaleChannel(TestHotel): + def create_common_scenario(self): + # create a property + self.property = self.env["pms.property"].create( + { + "name": "MY PROPERTY TEST", + "company_id": self.env.ref("base.main_company").id, + "default_pricelist_id": self.env.ref("product.list0").id, + } + ) + def test_not_agency_as_agency(self): # ARRANGE + self.create_common_scenario() PmsReservation = self.env["pms.reservation"] not_agency = self.env["res.partner"].create( {"name": "partner1", "is_agency": False} @@ -23,11 +34,13 @@ class TestPmsSaleChannel(TestHotel): "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "agency_id": not_agency.id, + "pms_property_id": self.property.id, } ) def test_channel_type_id_only_directs(self): # ARRANGE + self.create_common_scenario() PmsReservation = self.env["pms.reservation"] PmsSaleChannel = self.env["pms.sale.channel"] # ACT @@ -39,6 +52,7 @@ class TestPmsSaleChannel(TestHotel): "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "channel_type_id": salechannel.id, "partner_id": partner1.id, + "pms_property_id": self.property.id, } ) # ASSERT @@ -50,6 +64,7 @@ class TestPmsSaleChannel(TestHotel): def test_agency_id_is_agency(self): # ARRANGE + self.create_common_scenario() PmsReservation = self.env["pms.reservation"] PmsSaleChannel = self.env["pms.sale.channel"] salechannel = PmsSaleChannel.create( @@ -68,6 +83,7 @@ class TestPmsSaleChannel(TestHotel): "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "agency_id": agency.id, + "pms_property_id": self.property.id, } ) # ASSERT diff --git a/pms/wizards/wizard_folio.py b/pms/wizards/wizard_folio.py index 030dd464d..7ee1b9ffc 100644 --- a/pms/wizards/wizard_folio.py +++ b/pms/wizards/wizard_folio.py @@ -145,7 +145,7 @@ class FolioWizard(models.TransientModel): "room_type_id": line.room_type_id.id, "partner_id": folio.partner_id.id, "pricelist_id": folio.pricelist_id.id, - "pms_property_id": record.pms_property_id.id, + "pms_property_id": folio.pms_property_id.id, } ) res.reservation_line_ids.discount = record.discount * 100 diff --git a/pms_l10n_es/tests/test_partner.py b/pms_l10n_es/tests/test_partner.py index d912b7ac0..3c17d603d 100644 --- a/pms_l10n_es/tests/test_partner.py +++ b/pms_l10n_es/tests/test_partner.py @@ -9,8 +9,18 @@ from odoo.tests import common @freeze_time("2011-03-16") class TestResPartner(common.SavepointCase): + def create_common_scenario(self): + self.property_test = self.property = self.env["pms.property"].create( + { + "name": "My property test", + "company_id": self.env.ref("base.main_company").id, + "default_pricelist_id": self.env.ref("product.list0").id, + } + ) + def test_check_precheckin_state(self): # arrange + self.create_common_scenario() today = fields.date.today() partner = self.env["res.partner"].create( { @@ -29,6 +39,7 @@ class TestResPartner(common.SavepointCase): "checkout": today + datetime.timedelta(days=3), "partner_id": partner.id, "adults": 1, + "pms_property_id": self.property_test.id, } # action reservation = self.env["pms.reservation"].create(reservation_vals) @@ -46,8 +57,9 @@ class TestResPartner(common.SavepointCase): ) def test_error_action_on_board(self): - today = fields.date.today() # arrange + self.create_common_scenario() + today = fields.date.today() partner = self.env["res.partner"].create( { "name": "partner1", @@ -58,6 +70,7 @@ class TestResPartner(common.SavepointCase): "checkout": today + datetime.timedelta(days=3), "partner_id": partner.id, "adults": 1, + "pms_property_id": self.property_test.id, } # action reservation = self.env["pms.reservation"].create(reservation_vals) @@ -74,7 +87,7 @@ class TestResPartner(common.SavepointCase): def test_right_action_on_board(self): # arrange - # arrange + self.create_common_scenario() today = fields.date.today() partner = self.env["res.partner"].create( { @@ -93,6 +106,7 @@ class TestResPartner(common.SavepointCase): "checkout": today + datetime.timedelta(days=3), "partner_id": partner.id, "adults": 1, + "pms_property_id": self.property_test.id, } # action reservation = self.env["pms.reservation"].create(reservation_vals)