diff --git a/pms/models/pms_folio.py b/pms/models/pms_folio.py index d58d701d7..46f793f54 100644 --- a/pms/models/pms_folio.py +++ b/pms/models/pms_folio.py @@ -564,17 +564,21 @@ class PmsFolio(models.Model): target_lines = folio_lines_to_invoice if self._context.get("lines_auto_add") and folio_partner_invoice_id: - if folio_partner_invoice_id.default_invoice_lines == "overnights": + folio_partner_invoice = self.env["res.partner"].browse( + folio_partner_invoice_id + ) + if folio_partner_invoice.default_invoice_lines == "overnights": target_lines = target_lines.filtered( - lambda r: r.is_board_service or r.reservation_id.overnight_room + lambda r: r.is_board_service + or (r.reservation_line_ids and r.reservation_id.overnight_room) ) - elif folio_partner_invoice_id.default_invoice_lines == "reservations": + elif folio_partner_invoice.default_invoice_lines == "reservations": target_lines = target_lines.filtered( - lambda r: r.is_board_service or r.reservation_id + lambda r: r.is_board_service or r.reservation_line_ids ) - elif folio_partner_invoice_id.default_invoice_lines == "services": + elif folio_partner_invoice.default_invoice_lines == "services": target_lines = target_lines.filtered( - lambda r: not r.is_board_service or r.service_id + lambda r: not r.is_board_service or r.service_line_ids ) groups_invoice_lines = [ { @@ -659,7 +663,7 @@ class PmsFolio(models.Model): (0, 0, invoice_line_id) for invoice_line_id in invoice_lines_vals ] - invoice_vals_list.append(invoice_vals) + invoice_vals_list.append(invoice_vals) return invoice_vals_list def _get_tax_amount_by_group(self): @@ -684,7 +688,7 @@ class PmsFolio(models.Model): ] return res - @api.depends("partner_id", "invoice_status", "last_checkout") + @api.depends("partner_id", "invoice_status", "last_checkout", "partner_invoice_ids") def _compute_autoinvoice_date(self): self.autoinvoice_date = False for record in self.filtered(lambda r: r.invoice_status == "to_invoice"): @@ -706,18 +710,18 @@ class PmsFolio(models.Model): if not partner or partner.invoicing_policy == "property" else partner.margin_days_autoinvoice ) - return self.checkout + timedelta(days=margin_days) + return self.last_checkout + timedelta(days=margin_days) if invoicing_policy == "month_day": month_day = ( self.pms_property_id.invoicing_month_day if not partner or partner.invoicing_policy == "property" else partner.invoicing_month_day ) - if self.checkout.day <= month_day: - self.autoinvoice_date = self.checkout.replace(day=month_day) + if self.last_checkout.day <= month_day: + self.autoinvoice_date = self.last_checkout.replace(day=month_day) else: self.autoinvoice_date = ( - self.checkout + relativedelta.relativedelta(months=1) + self.last_checkout + relativedelta.relativedelta(months=1) ).replace(day=month_day) @api.depends("reservation_ids", "reservation_ids.state") @@ -1605,7 +1609,6 @@ class PmsFolio(models.Model): lines_to_invoice=lines_to_invoice, partner_invoice_id=partner_invoice_id, ) - if not invoice_vals_list: raise self._nothing_to_invoice_error() @@ -1685,7 +1688,7 @@ class PmsFolio(models.Model): # However, he should not be able to create an invoice from scratch. moves = self.env["account.move"] for invoice_vals in invoice_vals_list: - if invoice_vals["partner_id"]: + if invoice_vals["move_type"] == "out_invoice": move = ( self.env["account.move"] .sudo() @@ -1727,11 +1730,6 @@ class PmsFolio(models.Model): (making sure to call super() to establish a clean extension chain). """ self.ensure_one() - if not partner_invoice_id: - partner_invoice_id = ( - self.partner_invoice_ids[0].id if self.partner_invoice_ids else False - ) - journal = self._get_folio_default_journal(partner_invoice_id) if not journal: journal = ( diff --git a/pms/models/pms_property.py b/pms/models/pms_property.py index 634570170..6aa918749 100644 --- a/pms/models/pms_property.py +++ b/pms/models/pms_property.py @@ -143,7 +143,7 @@ class PmsProperty(models.Model): is_canceled_auto_mail = fields.Boolean(string="Auto Send Cancellation Mail") default_invoicing_policy = fields.Selection( - string="Invoicing Policy", + string="Default Invoicing Policy", selection=[ ("manual", "Manual"), ("checkout", "Checkout"), diff --git a/pms/models/pms_reservation.py b/pms/models/pms_reservation.py index 9367b7ea3..3cc624c89 100644 --- a/pms/models/pms_reservation.py +++ b/pms/models/pms_reservation.py @@ -1851,8 +1851,23 @@ class PmsReservation(models.Model): record = super(PmsReservation, self).create(vals) if record.preconfirm and record.state == "draft": record.confirm() + + record._check_services(vals) + return record + def write(self, vals): + asset = super(PmsReservation, self).write(vals) + self._check_services(vals) + return asset + + def _check_services(self, vals): + # If we create a reservation with board service and other service at the same time, + # compute_service_ids dont run (compute with readonly to False), + # and we must force it to compute the services linked with the board service: + if "board_service_room_id" in vals and "service_ids" in vals: + self._compute_service_ids() + def update_prices(self): self.ensure_one() for line in self.reservation_line_ids: diff --git a/pms/tests/test_pms_folio_invoice.py b/pms/tests/test_pms_folio_invoice.py index c64412a17..dc7cceca0 100644 --- a/pms/tests/test_pms_folio_invoice.py +++ b/pms/tests/test_pms_folio_invoice.py @@ -147,7 +147,7 @@ class TestPmsFolioInvoice(TestPms): ] = 1 r1.folio_id._create_invoices( lines_to_invoice=dict_lines, - partner_invoice_id=self.env.ref("base.res_partner_1"), + partner_invoice_id=self.env.ref("base.res_partner_1").id, ) # test does not work without invalidating cache @@ -165,7 +165,7 @@ class TestPmsFolioInvoice(TestPms): ] = 2 r1.folio_id._create_invoices( lines_to_invoice=dict_lines, - partner_invoice_id=self.env.ref("base.res_partner_12"), + partner_invoice_id=self.env.ref("base.res_partner_12").id, ) self.assertNotEqual( r1.folio_id.move_ids.mapped("partner_id")[0], @@ -579,14 +579,156 @@ class TestPmsFolioInvoice(TestPms): "The quantity of board services to be invoice is wrong", ) + def test_autoinvoice_folio_checkout_property_policy(self): + """ + Test create and invoice the cron by property preconfig automation + -------------------------------------- + Set property default_invoicing_policy to checkout with 0 days with + margin, and check that the folio autoinvoice date is set to last checkout + folio date + """ + # ARRANGE + self.property.default_invoicing_policy = "checkout" + self.property.margin_days_autoinvoice = 0 + + # ACT + self.reservation1 = self.env["pms.reservation"].create( + { + "pms_property_id": self.property.id, + "checkin": datetime.date.today(), + "checkout": datetime.date.today() + datetime.timedelta(days=3), + "adults": 2, + "room_type_id": self.room_type_double.id, + "partner_id": self.partner_id.id, + } + ) + + # ASSERT + self.assertEqual( + datetime.date.today() + datetime.timedelta(days=3), + self.reservation1.folio_id.autoinvoice_date, + "The autoinvoice date in folio with property checkout policy is wrong", + ) + + def test_autoinvoice_folio_checkout_partner_policy(self): + """ + Test create and invoice the cron by partner preconfig automation + -------------------------------------- + Set partner invoicing_policy to checkout with 2 days with + margin, and check that the folio autoinvoice date is set to last checkout + folio date + 2 days + """ + # ARRANGE + self.partner_id.invoicing_policy = "checkout" + self.partner_id.margin_days_autoinvoice = 2 + + # ACT + self.reservation1 = self.env["pms.reservation"].create( + { + "pms_property_id": self.property.id, + "checkin": datetime.date.today(), + "checkout": datetime.date.today() + datetime.timedelta(days=3), + "adults": 2, + "room_type_id": self.room_type_double.id, + "partner_id": self.partner_id.id, + } + ) + + # ASSERT + self.assertEqual( + datetime.date.today() + datetime.timedelta(days=5), + self.reservation1.folio_id.autoinvoice_date, + "The autoinvoice date in folio with property checkout policy is wrong", + ) + + def test_autoinvoice_folio_overnights_partner_policy(self): + """ + Test create and invoice the cron by partner preconfig automation + with only overnights reservations (included board services) + -------------------------------------- + Set partner invoicing_policy to checkout, create a reservation + with room, board service and normal service, run autoinvoicing + method and check that only room and board service was invoiced + in partner1, + + """ + # ARRANGE + self.partner_id.invoicing_policy = "checkout" + self.partner_id.margin_days_autoinvoice = 0 + self.partner_id.default_invoice_lines = "overnights" + self.product1 = self.env["product.product"].create( + { + "name": "Test Product 1", + } + ) + + self.product2 = self.env["product.product"].create( + { + "name": "Test Product 2", + } + ) + + self.service = self.env["pms.service"].create( + { + "is_board_service": False, + "product_id": self.product2.id, + } + ) + + self.board_service1 = self.env["pms.board.service"].create( + { + "name": "Test Board Service 1", + "default_code": "CB1", + } + ) + + self.board_service_line1 = self.env["pms.board.service.line"].create( + { + "product_id": self.product1.id, + "pms_board_service_id": self.board_service1.id, + "amount": 10, + } + ) + + self.board_service_room_type1 = self.env["pms.board.service.room.type"].create( + { + "pms_room_type_id": self.room_type_double.id, + "pms_board_service_id": self.board_service1.id, + } + ) + # ACT + self.reservation1 = self.env["pms.reservation"].create( + { + "pms_property_id": self.property.id, + "checkin": datetime.date.today() - datetime.timedelta(days=3), + "checkout": datetime.date.today(), + "adults": 2, + "room_type_id": self.room_type_double.id, + "partner_id": self.partner_id.id, + "board_service_room_id": self.board_service_room_type1.id, + "service_ids": [(4, self.service.id)], + } + ) + self.property.autoinvoicing() + + # ASSERT + overnight_sale_lines = self.reservation1.folio_id.sale_line_ids.filtered( + lambda line: line.reservation_line_ids or line.is_board_service + ) + partner_invoice = self.reservation1.folio_id.move_ids.filtered( + lambda inv: inv.partner_id == self.partner_id + ) + self.assertEqual( + partner_invoice.mapped("line_ids.folio_line_ids.id"), + overnight_sale_lines.ids, + "Billed services and overnights invoicing wrong compute", + ) + def _test_invoice_line_group_by_room_type_sections(self): """Test create and invoice from the Folio, and check qty invoice/to invoice, and the grouped invoice lines by room type, by one line by unit prices/qty with nights""" - def _test_autoinvoice_folio(self): - """ Test create and invoice the cron by partner preconfig automation """ - def _test_downpayment(self): """Test invoice qith a way of downpaument and check dowpayment's folio line is created and also check a total amount of invoice is diff --git a/pms/views/account_bank_statement_views.xml b/pms/views/account_bank_statement_views.xml index 8b0f18ca4..2d41ad417 100644 --- a/pms/views/account_bank_statement_views.xml +++ b/pms/views/account_bank_statement_views.xml @@ -4,9 +4,10 @@ account.bank.statement + - - + +