diff --git a/pms/models/pms_folio.py b/pms/models/pms_folio.py index 1899af850..802eca458 100644 --- a/pms/models/pms_folio.py +++ b/pms/models/pms_folio.py @@ -140,7 +140,6 @@ class PmsFolio(models.Model): commission = fields.Float( string="Commission", readonly=True, - default=0, store=True, compute="_compute_commission", ) @@ -642,7 +641,7 @@ class PmsFolio(models.Model): or False ) - @api.depends("reservation_ids") + @api.depends("reservation_ids", "reservation_ids.commission_amount") def _compute_commission(self): for folio in self: for reservation in folio.reservation_ids: diff --git a/pms/models/pms_reservation.py b/pms/models/pms_reservation.py index 827ea949f..0d5488c53 100644 --- a/pms/models/pms_reservation.py +++ b/pms/models/pms_reservation.py @@ -109,6 +109,7 @@ class PmsReservation(models.Model): readonly=False, store=True, related="folio_id.agency_id", + depends=["folio_id.agency_id"], tracking=True, ) channel_type_id = fields.Many2one( @@ -376,16 +377,18 @@ class PmsReservation(models.Model): checkin = fields.Date( string="Check In", help="It is the checkin date of the reservation, ", - required=True, - default=lambda self: self._get_default_checkin(), + compute="_compute_checkin", + readonly=False, + store=True, copy=False, tracking=True, ) checkout = fields.Date( string="Check Out", help="It is the checkout date of the reservation, ", - required=True, - default=lambda self: self._get_default_checkout(), + compute="_compute_checkout", + readonly=False, + store=True, copy=False, tracking=True, ) @@ -924,6 +927,53 @@ class PmsReservation(models.Model): for reservation in self: reservation.access_url = "/my/reservations/%s" % (reservation.id) + @api.depends("reservation_line_ids") + def _compute_checkin(self): + """ + Allows to calculate the checkin by default or when the create + specifically indicates the lines of the reservation + """ + for record in self: + if record.reservation_line_ids: + checkin_line_date = min(record.reservation_line_ids.mapped("date")) + # check if the checkin was created directly as reservation_line_id: + if checkin_line_date != record.checkin: + record.checkin = checkin_line_date + elif not record.checkin: + # default checkout other folio reservations or today + if len(record.folio_id.reservation_ids) > 1: + record.checkin = record.folio_id.reservation_ids[0].checkin + else: + record.checkin = fields.date.today() + + @api.depends("reservation_line_ids", "checkin") + def _compute_checkout(self): + """ + Allows to calculate the checkout by default or when the create + specifically indicates the lines of the reservation + """ + for record in self: + if record.reservation_line_ids: + checkout_line_date = max( + record.reservation_line_ids.mapped("date") + ) + datetime.timedelta(days=1) + # check if the checkout was created directly as reservation_line_id: + if checkout_line_date != record.checkout: + record.checkout = checkout_line_date + # default checkout if checkin is set + elif record.checkin and not record.checkout: + if len(record.folio_id.reservation_ids) > 1: + record.checkin = record.folio_id.reservation_ids[0].checkout + else: + record.checkout = record.checkin + datetime.timedelta(days=1) + elif not record.checkout: + record.checkout = False + # date checking + if record.checkin and record.checkout and record.checkin >= record.checkout: + raise UserError( + _("The checkout date must be greater than the checkin date") + ) + @api.depends("pms_property_id", "folio_id") def _compute_arrival_hour(self): for record in self: @@ -975,7 +1025,7 @@ class PmsReservation(models.Model): for reservation in self: if reservation.commission_percent > 0: reservation.commission_amount = ( - reservation.price_total * reservation.commission_percent + reservation.price_total * reservation.commission_percent / 100 ) else: reservation.commission_amount = 0 @@ -1184,28 +1234,6 @@ class PmsReservation(models.Model): recs = self.search([]).filtered(lambda x: x.checkin_partner_pending_count > 0) return [("id", "in", [x.id for x in recs])] if recs else [] - def _get_default_checkin(self): - folio = False - if "folio_id" in self._context: - folio = self.env["pms.folio"].search( - [("id", "=", self._context["folio_id"])] - ) - if folio and folio.reservation_ids: - return folio.reservation_ids[0].checkin - else: - return fields.Date.today() - - def _get_default_checkout(self): - folio = False - if "folio_id" in self._context: - folio = self.env["pms.folio"].search( - [("id", "=", self._context["folio_id"])] - ) - if folio and folio.reservation_ids: - return folio.reservation_ids[0].checkout - else: - return fields.Date.today() + datetime.timedelta(1) - def _get_default_segmentation(self): folio = False segmentation_ids = False @@ -1234,6 +1262,20 @@ class PmsReservation(models.Model): ) ) + @api.constrains("reservation_line_ids") + def check_consecutive_dates(self): + """ + simply convert date objects to integers using the .toordinal() method + of datetime objects. The difference between the maximum and minimum value + of the set of ordinal dates is one more than the length of the set + """ + for record in self: + if record.reservation_line_ids and len(record.reservation_line_ids) > 1: + dates = record.reservation_line_ids.mapped("date") + date_ints = {d.toordinal() for d in dates} + if not (max(date_ints) - min(date_ints) == len(date_ints) - 1): + raise ValidationError(_("Reservation dates should be consecutives")) + # @api.constrains("checkin_partner_ids", "adults") # def _max_checkin_partner_ids(self): # for record in self: diff --git a/pms/models/pms_reservation_line.py b/pms/models/pms_reservation_line.py index c5f4eaab0..cfdfa06f8 100644 --- a/pms/models/pms_reservation_line.py +++ b/pms/models/pms_reservation_line.py @@ -210,8 +210,12 @@ class PmsReservationLine(models.Model): # if the preferred room is NOT available else: raise ValidationError( - _("%s: No room available.") - % (reservation.preferred_room_id.name) + _("%s: No room available in %s <-> %s.") + % ( + reservation.preferred_room_id.name, + line.reservation_id.checkin, + line.reservation_id.checkout, + ) ) # otherwise we assign the first of those diff --git a/pms/tests/test_pms_folio.py b/pms/tests/test_pms_folio.py index 7fdeef8c0..2fe41c34e 100644 --- a/pms/tests/test_pms_folio.py +++ b/pms/tests/test_pms_folio.py @@ -4,73 +4,34 @@ from freezegun import freeze_time from odoo import fields from odoo.exceptions import UserError -from odoo.tests import common -freeze_time("2000-02-02") +from .common import TestPms -class TestPmsFolio(common.SavepointCase): - def create_common_scenario(self): - # create a room type availability - self.room_type_availability = self.env["pms.availability.plan"].create( - {"name": "Availability plan for TEST"} - ) - # sequences - self.folio_sequence = self.env["ir.sequence"].create( - { - "name": "PMS Folio", - "code": "pms.folio", - "padding": 4, - "company_id": self.env.ref("base.main_company").id, - } - ) - self.reservation_sequence = self.env["ir.sequence"].create( - { - "name": "PMS Reservation", - "code": "pms.reservation", - "padding": 4, - "company_id": self.env.ref("base.main_company").id, - } - ) - self.checkin_sequence = self.env["ir.sequence"].create( - { - "name": "PMS Checkin", - "code": "pms.checkin.partner", - "padding": 4, - "company_id": self.env.ref("base.main_company").id, - } - ) - # create a property - self.property = self.env["pms.property"].create( - { - "name": "MY PMS TEST", - "company_id": self.env.ref("base.main_company").id, - "default_pricelist_id": self.env.ref("product.list0").id, - "folio_sequence_id": self.folio_sequence.id, - "reservation_sequence_id": self.reservation_sequence.id, - "checkin_sequence_id": self.checkin_sequence.id, - } - ) +class TestPmsFolio(TestPms): - # create room type class - self.room_type_class = self.env["pms.room.type.class"].create( - {"name": "Room", "default_code": "ROOM"} - ) + # SetUp and Common Scenarios methods + + def setUp(self): + """ + - common + room_type_double with 2 rooms (double1 and double2) in pms_property1 + """ + super().setUp() # create room type self.room_type_double = self.env["pms.room.type"].create( { - "pms_property_ids": [self.property.id], + "pms_property_ids": [self.pms_property1.id], "name": "Double Test", "default_code": "DBL_Test", - "class_id": self.room_type_class.id, + "class_id": self.room_type_class1.id, "price": 25, } ) # create room - self.room1 = self.env["pms.room"].create( + self.double1 = self.env["pms.room"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "name": "Double 101", "room_type_id": self.room_type_double.id, "capacity": 2, @@ -78,9 +39,9 @@ class TestPmsFolio(common.SavepointCase): ) # create room - self.room2 = self.env["pms.room"].create( + self.double2 = self.env["pms.room"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "name": "Double 102", "room_type_id": self.room_type_double.id, "capacity": 2, @@ -88,233 +49,344 @@ class TestPmsFolio(common.SavepointCase): ) def create_multiproperty_scenario(self): - self.create_common_scenario() - 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, - "folio_sequence_id": self.folio_sequence.id, - "reservation_sequence_id": self.reservation_sequence.id, - "checkin_sequence_id": self.checkin_sequence.id, - } - ) - - self.property2 = self.env["pms.property"].create( + """ + Just 2 properties to majors + """ + self.pms_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, - "folio_sequence_id": self.folio_sequence.id, - "reservation_sequence_id": self.reservation_sequence.id, - "checkin_sequence_id": self.checkin_sequence.id, } ) - self.property3 = self.env["pms.property"].create( + self.pms_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, - "folio_sequence_id": self.folio_sequence.id, - "reservation_sequence_id": self.reservation_sequence.id, - "checkin_sequence_id": self.checkin_sequence.id, } ) - def test_commission_and_partner_correct(self): - # ARRANGE - self.create_common_scenario() - PmsFolio = self.env["pms.folio"] - PmsReservation = self.env["pms.reservation"] + def create_sale_channel_scenario(self): + """ + Method to simplified scenario on sale channel tests: + - create a sale_channel1 like indirect + - create a agency1 like sale_channel1 agency + """ PmsPartner = self.env["res.partner"] PmsSaleChannel = self.env["pms.sale.channel"] - # ACT - saleChannel = PmsSaleChannel.create( + + self.sale_channel1 = PmsSaleChannel.create( {"name": "saleChannel1", "channel_type": "indirect"} ) - agency = PmsPartner.create( + self.agency1 = PmsPartner.create( { "name": "partner1", "is_agency": True, "invoice_to_agency": True, "default_commission": 15, - "sale_channel_id": saleChannel.id, + "sale_channel_id": self.sale_channel1.id, } ) - folio = PmsFolio.create( + def create_configuration_accounting_scenario(self): + """ + Method to simplified scenario to payments and accounting: + # REVIEW: + - Use new property with odoo demo data company to avoid account configuration + - Emule SetUp with new property: + - create demo_room_type_double + - Create 2 rooms room_type_double + """ + self.pms_property_demo = self.env["pms.property"].create( { - "agency_id": agency.id, - "pms_property_id": self.property.id, + "name": "Property Based on Comapany Demo", + "company_id": self.env.ref("base.main_company").id, + "default_pricelist_id": self.env.ref("product.list0").id, } ) - - reservation = PmsReservation.create( + # create room type + self.demo_room_type_double = self.env["pms.room.type"].create( { - "checkin": datetime.datetime.now(), - "checkout": datetime.datetime.now() + datetime.timedelta(days=3), - "agency_id": agency.id, - "folio_id": folio.id, + "pms_property_ids": [self.pms_property_demo.id], + "name": "Double Test", + "default_code": "Demo_DBL_Test", + "class_id": self.room_type_class1.id, + "price": 25, } ) - - commission = 0 - for reservation in folio.reservation_ids: - commission += reservation.commission_amount - - # ASSERT - self.assertEqual( - folio.commission, - commission, - "Folio commission don't math with his reservation commission", - ) - if folio.agency_id: - self.assertEqual( - folio.agency_id, folio.partner_id, "Agency has to be the partner" - ) - - def test_compute_folio_priority(self): - self.create_common_scenario() - r1 = self.env["pms.reservation"].create( + # create rooms + self.double1 = self.env["pms.room"].create( { - "checkin": fields.date.today(), - "checkout": fields.date.today() + datetime.timedelta(days=1), - "room_type_id": self.room_type_double.id, - "partner_id": self.env.ref("base.res_partner_12").id, - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property_demo.id, + "name": "Double 101", + "room_type_id": self.demo_room_type_double.id, + "capacity": 2, + } + ) + self.double2 = self.env["pms.room"].create( + { + "pms_property_id": self.pms_property_demo.id, + "name": "Double 102", + "room_type_id": self.demo_room_type_double.id, + "capacity": 2, + } + ) + + # TestCases: Sale Channels + + def test_default_agency_commission(self): + """ + Check the total commission of a folio with agency based on the + reservation night price and the preconfigured commission in the agency. + ------- + Agency with 15% default commision, folio with one reservation + and 3 nights at 20$ by night (60$ total) + """ + # ARRANGE + self.create_sale_channel_scenario() + commission = (20 + 20 + 20) * 0.15 + + # ACT + folio1 = self.env["pms.folio"].create( + { + "agency_id": self.agency1.id, + "pms_property_id": self.pms_property1.id, } ) - r1.allowed_checkin = False self.env["pms.reservation"].create( { - "folio_id": r1.folio_id.id, + "folio_id": folio1.id, + "room_type_id": self.room_type_double.id, + "reservation_line_ids": [ + ( + 0, + False, + { + "date": fields.date.today(), + "price": 20, + }, + ), + ( + 0, + False, + { + "date": fields.date.today() + datetime.timedelta(days=1), + "price": 20, + }, + ), + ( + 0, + False, + { + "date": fields.date.today() + datetime.timedelta(days=2), + "price": 20, + }, + ), + ], + } + ) + # ASSERT + self.assertEqual( + commission, folio1.commission, "The folio compute commission is wrong" + ) + + def test_reservation_agency_without_partner(self): + """ + Check that a reservation / folio created with an agency + and without a partner will automatically take the partner. + ------- + Create the folio1 and the reservation1, only set agency_id, + and the partner_id should be the agency itself. + """ + # ARRANGE + self.create_sale_channel_scenario() + + # ACT + folio1 = self.env["pms.folio"].create( + { + "agency_id": self.agency1.id, + "pms_property_id": self.pms_property1.id, + } + ) + + reservation1 = self.env["pms.reservation"].create( + { + "room_type_id": self.room_type_double.id, "checkin": fields.date.today(), "checkout": fields.date.today() + datetime.timedelta(days=1), - "room_type_id": self.room_type_double.id, - "partner_id": self.env.ref("base.res_partner_12").id, - "pms_property_id": self.property.id, + "folio_id": folio1.id, } ) + # ASSERT self.assertEqual( - r1.priority, - r1.folio_id.max_reservation_prior, - "The max. reservation priority on the whole folio is incorrect", + reservation1.agency_id, folio1.partner_id, "Agency has to be the partner" ) + # TestCases: Priority + + def test_compute_folio_priority(self): + """ + Check the priority of a folio based on its reservations + #TODO: Commented test waiting to redefine the priority calculation + """ + # reservation1 = self.env["pms.reservation"].create( + # { + # "checkin": fields.date.today(), + # "checkout": fields.date.today() + datetime.timedelta(days=1), + # "room_type_id": self.room_type_double.id, + # "partner_id": self.env.ref("base.res_partner_12").id, + # "pms_property_id": self.property.id, + # } + # ) + # reservation1.allowed_checkin = False + + # self.env["pms.reservation"].create( + # { + # "folio_id": reservation1.folio_id.id, + # "checkin": fields.date.today(), + # "checkout": fields.date.today() + datetime.timedelta(days=1), + # "room_type_id": self.room_type_double.id, + # "partner_id": self.env.ref("base.res_partner_12").id, + # "pms_property_id": self.property.id, + # } + # ) + + # self.assertEqual( + # reservation1.priority, + # reservation1.folio_id.max_reservation_prior, + # "The max. reservation priority on the whole folio is incorrect", + # ) + + # TestCases: Payments + @freeze_time("2000-02-02") def test_full_pay_folio(self): - # TEST CASE - # Folio is paid after execute - # + """ + After making the payment of a folio for the entire amount, + check that there is nothing pending. + ----- + We create a reservation (autocalculates the amounts) and + then make the payment using the do_payment method of the folio, + directly indicating the pending amount on the folio of the newly + created reservation + """ # ARRANGE - self.create_common_scenario() - r_test = self.env["pms.reservation"].create( + self.create_configuration_accounting_scenario() + reservation1 = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property_demo.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=1), "adults": 2, "partner_id": self.env.ref("base.res_partner_12").id, - "room_type_id": self.room_type_double.id, + "room_type_id": self.demo_room_type_double.id, } ) + + # ACTION self.env["pms.folio"].do_payment( - self.env["account.journal"].browse( - r_test.folio_id.pms_property_id._get_payment_methods().ids[0] + journal=self.env["account.journal"].browse( + reservation1.folio_id.pms_property_id._get_payment_methods().ids[0] ), - self.env["account.journal"] - .browse(r_test.folio_id.pms_property_id._get_payment_methods().ids[0]) + receivable_account=self.env["account.journal"] + .browse(reservation1.folio_id.pms_property_id._get_payment_methods().ids[0]) .suspense_account_id, - self.env.user, - r_test.folio_id.pending_amount, - r_test.folio_id, - partner=r_test.partner_id, + user=self.env.user, + amount=reservation1.folio_id.pending_amount, + folio=reservation1.folio_id, + partner=reservation1.partner_id, date=fields.date.today(), ) - self.assertFalse(r_test.folio_id.pending_amount) + # ASSERT + self.assertFalse( + reservation1.folio_id.pending_amount, + "The pending amount of a folio paid in full has not been zero", + ) + + @freeze_time("2000-02-02") def test_partial_pay_folio(self): - # TEST CASE - # Folio is partially paid after execute - # + """ + After making the payment of a folio for the partial amount, + We check that the pending amount is the one that corresponds to it. + ----- + We create a reservation (autocalculates the amounts) and + then make the payment using the do_payment method of the folio, + directly indicating the pending amount on the folio MINUS 1$ + of the newly created reservation + """ # ARRANGE + self.create_configuration_accounting_scenario() left_to_pay = 1 - self.create_common_scenario() - r_test = self.env["pms.reservation"].create( + reservation1 = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property_demo.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=1), "adults": 2, "partner_id": self.env.ref("base.res_partner_12").id, - "room_type_id": self.room_type_double.id, + "room_type_id": self.demo_room_type_double.id, } ) + + # ACTION self.env["pms.folio"].do_payment( - self.env["account.journal"].browse( - r_test.folio_id.pms_property_id._get_payment_methods().ids[0] + journal=self.env["account.journal"].browse( + reservation1.folio_id.pms_property_id._get_payment_methods().ids[0] ), - self.env["account.journal"] - .browse(r_test.folio_id.pms_property_id._get_payment_methods().ids[0]) + receivable_account=self.env["account.journal"] + .browse(reservation1.folio_id.pms_property_id._get_payment_methods().ids[0]) .suspense_account_id, - self.env.user, - r_test.folio_id.pending_amount - left_to_pay, - r_test.folio_id, - partner=r_test.partner_id, + user=self.env.user, + amount=reservation1.folio_id.pending_amount - left_to_pay, + folio=reservation1.folio_id, + partner=reservation1.partner_id, date=fields.date.today(), ) - self.assertEqual(r_test.folio_id.pending_amount, left_to_pay) - def test_closure_reason_property(self): + # ASSERT + self.assertEqual( + reservation1.folio_id.pending_amount, + left_to_pay, + "The pending amount on a partially paid folio it \ + does not correspond to the amount that it should", + ) + + # TestCases: Property Consistencies + + def test_folio_closure_reason_consistency_properties(self): + """ + Check the multioproperty consistencia between + clousure reasons and folios + ------- + create multiproperty scenario (3 properties in total) and + a new clousure reason in pms_property1 and pms_property2, then, create + a new folio in property3 and try to set the clousure_reason + waiting a error property consistency. + """ + # ARRANGE 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), + (4, self.pms_property1.id), + (4, self.pms_property2.id), ], } ) - with self.assertRaises(UserError): + # ACTION & ASSERT + with self.assertRaises( + UserError, + msg="Folio created with clousure_reason_id with properties inconsistence", + ): self.env["pms.folio"].create( { - "pms_property_id": self.property3.id, + "pms_property_id": self.pms_property3.id, "closure_reason_id": cl_reason.id, } ) - - def _test_compute_currency(self): - self.create_common_scenario() - self.currency1 = self.env["res.currency"].create( - { - "name": "currency1", - "symbol": "C", - } - ) - self.pricelist = self.env["product.pricelist"].create( - { - "name": "pricelist 1", - "pms_property_ids": [ - (4, self.property.id), - ], - "currency_id": self.currency1.id, - } - ) - self.reservation1 = self.env["pms.reservation"].create( - { - "pms_property_id": self.property.id, - "checkin": datetime.datetime.now(), - "checkout": datetime.datetime.now() + datetime.timedelta(days=1), - "partner_id": self.env.ref("base.res_partner_12").id, - "pricelist_id": self.pricelist.id, - } - ) - self.assertEqual( - self.currency1.id, - self.reservation1.folio_id.currency_id.id, - "Currency must match", - ) diff --git a/pms/tests/test_pms_folio_invoice.py b/pms/tests/test_pms_folio_invoice.py index f2da8e3b6..67754d22b 100644 --- a/pms/tests/test_pms_folio_invoice.py +++ b/pms/tests/test_pms_folio_invoice.py @@ -6,10 +6,7 @@ from odoo.tests import common class TestPmsFolioInvoice(common.SavepointCase): def setUp(self): super(TestPmsFolioInvoice, self).setUp() - - def create_common_scenario(self): - # create a room type availability - # sequences + # create a room type availability and sequences self.folio_sequence = self.env["ir.sequence"].create( { "name": "PMS Folio", @@ -95,7 +92,6 @@ class TestPmsFolioInvoice(common.SavepointCase): def test_invoice_full_folio(self): # ARRANGE - self.create_common_scenario() r1 = self.env["pms.reservation"].create( { "pms_property_id": self.property.id, @@ -121,7 +117,6 @@ class TestPmsFolioInvoice(common.SavepointCase): def test_invoice_partial_folio_by_steps(self): # ARRANGE - self.create_common_scenario() r1 = self.env["pms.reservation"].create( { "pms_property_id": self.property.id, @@ -162,7 +157,6 @@ class TestPmsFolioInvoice(common.SavepointCase): def test_invoice_partial_folio_diferent_partners(self): # ARRANGE - self.create_common_scenario() r1 = self.env["pms.reservation"].create( { "pms_property_id": self.property.id, @@ -208,7 +202,6 @@ class TestPmsFolioInvoice(common.SavepointCase): def test_invoice_partial_folio_wrong_qtys(self): # ARRANGE - self.create_common_scenario() r1 = self.env["pms.reservation"].create( { "pms_property_id": self.property.id, diff --git a/pms/tests/test_pms_folio_sale_line.py b/pms/tests/test_pms_folio_sale_line.py index 8e80126e0..500031caa 100644 --- a/pms/tests/test_pms_folio_sale_line.py +++ b/pms/tests/test_pms_folio_sale_line.py @@ -1,43 +1,31 @@ import datetime +from odoo import fields + from .common import TestPms class TestPmsFolioSaleLine(TestPms): - def create_common_scenario(self): - # create a room type availability - self.room_type_availability = self.env["pms.availability.plan"].create( - {"name": "Availability plan for TEST"} - ) - - # create a property - self.property = self.env["pms.property"].create( - { - "name": "MY PMS TEST", - "company_id": self.env.ref("base.main_company").id, - "default_pricelist_id": self.env.ref("product.list0").id, - } - ) - - # create room type class - self.room_type_class = self.env["pms.room.type.class"].create( - {"name": "Room", "default_code": "ROOM"} - ) + def setUp(self): + """ + - common + room_type_avalability_plan + """ + super().setUp() # create room type self.room_type_double = self.env["pms.room.type"].create( { - "pms_property_ids": [self.property.id], + "pms_property_ids": [self.pms_property1.id], "name": "Double Test", "default_code": "DBL_Test", - "class_id": self.room_type_class.id, + "class_id": self.room_type_class1.id, "price": 25, } ) # create room self.room1 = self.env["pms.room"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "name": "Double 101", "room_type_id": self.room_type_double.id, "capacity": 2, @@ -46,21 +34,49 @@ class TestPmsFolioSaleLine(TestPms): # RESERVATION LINES def test_comp_fsl_rooms_all_same_group(self): - # TEST CASE - # 2-night reservation and same price, discount & cancel_discount for - # all nights - # should generate just 1 reservation sale line - + """ + check the grouping of the reservation lines on the sale line folio + when the price, discount match- + ------------ + reservation with 3 nights with the same price, + should generate just 1 reservation sale line + """ # ARRANGE expected_sale_lines = 1 - self.create_common_scenario() # ACT r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, - "checkin": datetime.datetime.now(), - "checkout": datetime.datetime.now() + datetime.timedelta(days=3), + "pms_property_id": self.pms_property1.id, + "reservation_line_ids": [ + ( + 0, + False, + { + "date": fields.date.today(), + "price": 20, + "discount": 10, + }, + ), + ( + 0, + False, + { + "date": fields.date.today() + datetime.timedelta(days=1), + "price": 20, + "discount": 10, + }, + ), + ( + 0, + False, + { + "date": fields.date.today() + datetime.timedelta(days=2), + "price": 20, + "discount": 10, + }, + ), + ], "adults": 2, "room_type_id": self.room_type_double.id, "partner_id": self.env.ref("base.res_partner_12").id, @@ -81,10 +97,9 @@ class TestPmsFolioSaleLine(TestPms): # ARRANGE expected_sale_lines = 2 - self.create_common_scenario() r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -112,10 +127,9 @@ class TestPmsFolioSaleLine(TestPms): # ARRANGE expected_sale_lines = 2 - self.create_common_scenario() r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -143,10 +157,9 @@ class TestPmsFolioSaleLine(TestPms): # ARRANGE expected_sale_lines = 2 - self.create_common_scenario() r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -175,10 +188,9 @@ class TestPmsFolioSaleLine(TestPms): # ARRANGE expected_sale_lines = 1 - self.create_common_scenario() r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -207,10 +219,9 @@ class TestPmsFolioSaleLine(TestPms): # Should keep the same reservation sales line record. # ARRANGE - self.create_common_scenario() r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -242,10 +253,9 @@ class TestPmsFolioSaleLine(TestPms): # Should keep the same reservation sales line record. # ARRANGE - self.create_common_scenario() r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -278,10 +288,9 @@ class TestPmsFolioSaleLine(TestPms): # Should keep the same reservation sales line record. # ARRANGE - self.create_common_scenario() r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -315,7 +324,6 @@ class TestPmsFolioSaleLine(TestPms): # ARRANGE expected_board_service_sale_lines = 1 - self.create_common_scenario() product_test1 = self.env["product.product"].create( { "name": "Test Product 1", @@ -345,7 +353,7 @@ class TestPmsFolioSaleLine(TestPms): # ACT r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -375,7 +383,6 @@ class TestPmsFolioSaleLine(TestPms): # ARRANGE expected_board_service_sale_lines = 2 - self.create_common_scenario() product_test1 = self.env["product.product"].create( { "name": "Test Product 1", @@ -404,7 +411,7 @@ class TestPmsFolioSaleLine(TestPms): ) r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -435,7 +442,6 @@ class TestPmsFolioSaleLine(TestPms): # ARRANGE expected_board_service_sale_lines = 2 - self.create_common_scenario() product_test1 = self.env["product.product"].create( { "name": "Test Product 1", @@ -464,7 +470,7 @@ class TestPmsFolioSaleLine(TestPms): ) r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -498,7 +504,6 @@ class TestPmsFolioSaleLine(TestPms): # ARRANGE expected_board_service_sale_lines = 2 - self.create_common_scenario() product_test1 = self.env["product.product"].create( { "name": "Test Product 1", @@ -527,7 +532,7 @@ class TestPmsFolioSaleLine(TestPms): ) r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -561,7 +566,6 @@ class TestPmsFolioSaleLine(TestPms): # ARRANGE expected_board_service_sale_lines = 1 - self.create_common_scenario() product_test1 = self.env["product.product"].create( { "name": "Test Product 1", @@ -590,7 +594,7 @@ class TestPmsFolioSaleLine(TestPms): ) r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -623,7 +627,6 @@ class TestPmsFolioSaleLine(TestPms): # Should keep the same board service sales line record. # ARRANGE - self.create_common_scenario() product_test1 = self.env["product.product"].create( { "name": "Test Product 1", @@ -651,7 +654,7 @@ class TestPmsFolioSaleLine(TestPms): ) r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -684,7 +687,6 @@ class TestPmsFolioSaleLine(TestPms): # Should keep the same board service sales line record. # ARRANGE - self.create_common_scenario() product_test1 = self.env["product.product"].create( { "name": "Test Product 1", @@ -716,7 +718,7 @@ class TestPmsFolioSaleLine(TestPms): r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -751,7 +753,6 @@ class TestPmsFolioSaleLine(TestPms): # Should keep the same board service sales line record. # ARRANGE - self.create_common_scenario() product_test1 = self.env["product.product"].create( { "name": "Test Product 1", @@ -783,7 +784,7 @@ class TestPmsFolioSaleLine(TestPms): r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -821,7 +822,6 @@ class TestPmsFolioSaleLine(TestPms): # ARRANGE expected_extra_service_sale_lines = 1 - self.create_common_scenario() product_test1 = self.env["product.product"].create( { "name": "Test Product 1", @@ -832,7 +832,7 @@ class TestPmsFolioSaleLine(TestPms): ) r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -871,7 +871,6 @@ class TestPmsFolioSaleLine(TestPms): # ARRANGE expected_extra_service_sale_lines = 2 - self.create_common_scenario() product_test1 = self.env["product.product"].create( { "name": "Test Product 1", @@ -882,7 +881,7 @@ class TestPmsFolioSaleLine(TestPms): ) r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -923,7 +922,6 @@ class TestPmsFolioSaleLine(TestPms): # ARRANGE expected_extra_service_sale_lines = 2 - self.create_common_scenario() product_test1 = self.env["product.product"].create( { "name": "Test Product 1", @@ -934,7 +932,7 @@ class TestPmsFolioSaleLine(TestPms): ) r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -976,7 +974,6 @@ class TestPmsFolioSaleLine(TestPms): # ARRANGE expected_extra_service_sale_lines = 2 - self.create_common_scenario() product_test1 = self.env["product.product"].create( { "name": "Test Product 1", @@ -987,7 +984,7 @@ class TestPmsFolioSaleLine(TestPms): ) r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -1030,7 +1027,6 @@ class TestPmsFolioSaleLine(TestPms): # ARRANGE expected_extra_service_sale_lines = 1 - self.create_common_scenario() product_test1 = self.env["product.product"].create( { "name": "Test Product 1", @@ -1041,7 +1037,7 @@ class TestPmsFolioSaleLine(TestPms): ) r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -1082,7 +1078,6 @@ class TestPmsFolioSaleLine(TestPms): # Should keep the same reservation service sales line record. # ARRANGE - self.create_common_scenario() product_test1 = self.env["product.product"].create( { "name": "Test Product 1", @@ -1093,7 +1088,7 @@ class TestPmsFolioSaleLine(TestPms): ) r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -1134,7 +1129,6 @@ class TestPmsFolioSaleLine(TestPms): # Should keep the same reservation service sales line record. # ARRANGE - self.create_common_scenario() product_test1 = self.env["product.product"].create( { "name": "Test Product 1", @@ -1145,7 +1139,7 @@ class TestPmsFolioSaleLine(TestPms): ) r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -1187,7 +1181,6 @@ class TestPmsFolioSaleLine(TestPms): # Should keep the same reservation service sales line record. # ARRANGE - self.create_common_scenario() product_test1 = self.env["product.product"].create( { "name": "Test Product 1", @@ -1198,7 +1191,7 @@ class TestPmsFolioSaleLine(TestPms): ) r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -1220,7 +1213,7 @@ class TestPmsFolioSaleLine(TestPms): # ACT r_test.service_ids.filtered( - lambda x: x.id == extra_service + lambda x: x.id == extra_service.id ).service_line_ids.price_unit = 50 r_test.service_ids.service_line_ids.flush() @@ -1242,7 +1235,6 @@ class TestPmsFolioSaleLine(TestPms): # ARRANGE expected_folio_service_sale_lines = 1 - self.create_common_scenario() product_test1 = self.env["product.product"].create( { "name": "Test Product 1", @@ -1250,7 +1242,7 @@ class TestPmsFolioSaleLine(TestPms): ) r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, @@ -1289,7 +1281,6 @@ class TestPmsFolioSaleLine(TestPms): # ARRANGE expected_folio_service_sale_lines = 2 - self.create_common_scenario() product_test1 = self.env["product.product"].create( { "name": "Test Product 1", @@ -1303,7 +1294,7 @@ class TestPmsFolioSaleLine(TestPms): r_test = self.env["pms.reservation"].create( { - "pms_property_id": self.property.id, + "pms_property_id": self.pms_property1.id, "checkin": datetime.datetime.now(), "checkout": datetime.datetime.now() + datetime.timedelta(days=3), "adults": 2, diff --git a/pms/tests/test_pms_reservation.py b/pms/tests/test_pms_reservation.py index 0d6f181aa..7949b2c2e 100644 --- a/pms/tests/test_pms_reservation.py +++ b/pms/tests/test_pms_reservation.py @@ -150,6 +150,84 @@ class TestPmsReservations(common.SavepointCase): } ) + @freeze_time("1980-11-01") + def test_reservation_dates_not_consecutive(self): + """ + Check the constrain if not consecutive dates + ---------------- + Create correct reservation set 3 reservation lines consecutives (nights) + """ + # ARRANGE + self.create_common_scenario() + customer = self.env.ref("base.res_partner_12") + today = fields.date.today() + tomorrow = fields.date.today() + datetime.timedelta(days=1) + three_days_later = fields.date.today() + datetime.timedelta(days=3) + + # ACT & ASSERT + with self.assertRaises( + ValidationError, + msg="Error, it has been allowed to create a reservation with non-consecutive days", + ): + self.env["pms.reservation"].create( + { + "room_type_id": self.room_type_double.id, + "partner_id": customer.id, + "pms_property_id": self.property.id, + "reservation_line_ids": [ + (0, False, {"date": today}), + (0, False, {"date": tomorrow}), + (0, False, {"date": three_days_later}), + ], + } + ) + + @freeze_time("1980-11-01") + def test_reservation_dates_compute_checkin_out(self): + """ + Check the reservation creation with specific reservation lines + anc compute checkin checkout + ---------------- + Create reservation with correct reservation lines and check + the checkin and checkout fields. Take into account that the + checkout of the reservation must be the day after the last night + (view checkout assertEqual) + """ + # ARRANGE + self.create_common_scenario() + customer = self.env.ref("base.res_partner_12") + today = fields.date.today() + tomorrow = fields.date.today() + datetime.timedelta(days=1) + two_days_later = fields.date.today() + datetime.timedelta(days=2) + + # ACT + reservation = self.env["pms.reservation"].create( + { + "room_type_id": self.room_type_double.id, + "partner_id": customer.id, + "pms_property_id": self.property.id, + "reservation_line_ids": [ + (0, False, {"date": today}), + (0, False, {"date": tomorrow}), + (0, False, {"date": two_days_later}), + ], + } + ) + + # ASSERT + self.assertEqual( + reservation.checkin, + today, + "The calculated checkin of the reservation does \ + not correspond to the first day indicated in the dates", + ) + self.assertEqual( + reservation.checkout, + two_days_later + datetime.timedelta(days=1), + "The calculated checkout of the reservation does \ + not correspond to the last day indicated in the dates", + ) + @freeze_time("1980-11-01") def test_create_reservation_start_date(self): # TEST CASE