From e6063a172bc4e22d5199819876a38426ba0442c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dar=C3=ADo=20Lodeiros?= Date: Sun, 5 Feb 2023 20:37:13 +0100 Subject: [PATCH] [IMP]pms: autoinvoicing folio cron improvements --- pms/__manifest__.py | 2 + pms/data/queue_data.xml | 9 ++ pms/data/queue_job_function_data.xml | 15 ++++ pms/models/folio_sale_line.py | 4 +- pms/models/pms_folio.py | 67 +++++++++++---- pms/models/pms_property.py | 122 ++++++++++++++------------- 6 files changed, 142 insertions(+), 77 deletions(-) create mode 100644 pms/data/queue_data.xml create mode 100644 pms/data/queue_job_function_data.xml diff --git a/pms/__manifest__.py b/pms/__manifest__.py index 501bdd552..d05633ccf 100644 --- a/pms/__manifest__.py +++ b/pms/__manifest__.py @@ -46,6 +46,8 @@ "report/invoice.xml", # "templates/pms_email_template.xml", "data/menus.xml", + "data/queue_data.xml", + "data/queue_job_function_data.xml", "wizards/wizard_payment_folio.xml", "wizards/folio_make_invoice_advance_views.xml", "wizards/pms_booking_engine_views.xml", diff --git a/pms/data/queue_data.xml b/pms/data/queue_data.xml new file mode 100644 index 000000000..d7020bd8a --- /dev/null +++ b/pms/data/queue_data.xml @@ -0,0 +1,9 @@ + + + + + autoinvoing folios + + + + diff --git a/pms/data/queue_job_function_data.xml b/pms/data/queue_job_function_data.xml new file mode 100644 index 000000000..da067a8cd --- /dev/null +++ b/pms/data/queue_job_function_data.xml @@ -0,0 +1,15 @@ + + + + + autoinvoice_folio + + + + + + autovalidate_folio_invoice + + + + diff --git a/pms/models/folio_sale_line.py b/pms/models/folio_sale_line.py index 9d18795ca..236dd1c9e 100644 --- a/pms/models/folio_sale_line.py +++ b/pms/models/folio_sale_line.py @@ -539,9 +539,9 @@ class FolioSaleLine(models.Model): where Total_sol depends on the invoice policy of the product. Note: Draft invoice are ignored on purpose, the 'to invoice' amount should - come only from the SO lines. + come only from the folio lines. """ - for line in self: + for line in self.filtered("invoice_lines"): amount_to_invoice = 0.0 if line.state != "draft": # Note: do not use price_subtotal field as it returns diff --git a/pms/models/pms_folio.py b/pms/models/pms_folio.py index 3ceeacced..8a533a50b 100644 --- a/pms/models/pms_folio.py +++ b/pms/models/pms_folio.py @@ -285,6 +285,13 @@ class PmsFolio(models.Model): compute="_compute_get_invoiced", search="_search_invoice_ids", ) + untaxed_amount_to_invoice = fields.Monetary( + string="Amount to invoice", + help="The amount to invoice", + readonly=True, + store=True, + compute="_compute_untaxed_amount_to_invoice", + ) payment_state = fields.Selection( string="Payment Status", help="The state of the payment", @@ -493,6 +500,9 @@ class PmsFolio(models.Model): "nothin to invoice, even when there may be ordered " "quantities pending to invoice.", copy=False, + compute="_compute_force_nothing_to_invoice", + readonly=False, + store=True, ) internal_comment = fields.Text( string="Internal Folio Notes", @@ -555,11 +565,6 @@ class PmsFolio(models.Model): compute="_compute_days_to_checkout", search="_search_days_to_checkout", ) - autoinvoice_date = fields.Date( - string="Autoinvoice Date", - compute="_compute_autoinvoice_date", - store=True, - ) invoice_to_agency = fields.Boolean( string="Invoice Agency", help="""Indicates if agency invoices partner @@ -1024,7 +1029,7 @@ class PmsFolio(models.Model): for folio in self: folio.access_url = "/my/folios/%s" % (folio.id) - @api.depends("state", "sale_line_ids.invoice_status") + @api.depends("state", "sale_line_ids.invoice_status", "force_nothing_to_invoice") def _compute_get_invoice_status(self): """ Compute the invoice status of a Folio. Possible statuses: @@ -1057,21 +1062,41 @@ class PmsFolio(models.Model): line_invoice_status = [ d[1] for d in line_invoice_status_all if d[0] == order.id ] - if order.force_nothing_to_invoice: - order.invoice_status = "no" - elif any( + if not order.force_nothing_to_invoice and any( invoice_status == "to_invoice" for invoice_status in line_invoice_status ): order.invoice_status = "to_invoice" elif any(inv.state == "draft" for inv in order.move_ids): order.invoice_status = "to_confirm" - elif line_invoice_status and all( + elif line_invoice_status and any( invoice_status == "invoiced" for invoice_status in line_invoice_status ): - order.invoice_status = "invoiced" + if ( + all( + invoice_status == "invoiced" + for invoice_status in line_invoice_status + ) + or order.force_nothing_to_invoice + ): + order.invoice_status = "invoiced" + else: + order.invoice_status = "no" else: order.invoice_status = "no" + @api.depends("untaxed_amount_to_invoice", "amount_total") + def _compute_force_nothing_to_invoice(self): + # If the invoice amount and amount total are the same, + # and the qty to invoice is not 0, we force nothing to invoice + for order in self: + if ( + order.untaxed_amount_to_invoice <= 0 + and sum(order.sale_line_ids.mapped("qty_to_invoice")) != 0 + ): + order.force_nothing_to_invoice = True + else: + order.force_nothing_to_invoice = False + @api.depends("partner_id", "partner_id.name", "agency_id", "reservation_type") def _compute_partner_name(self): for record in self: @@ -1174,6 +1199,12 @@ class PmsFolio(models.Model): / sum(folio.reservation_ids.mapped("adults")) ) + def _compute_untaxed_amount_to_invoice(self): + for folio in self: + folio.untaxed_amount_to_invoice = sum( + folio.mapped("sale_line_ids.untaxed_amount_to_invoice") + ) + # TODO: Add return_ids to depends @api.depends( "amount_total", @@ -1851,11 +1882,9 @@ class PmsFolio(models.Model): invoice_vals_list = self._get_group_vals_list(invoice_vals_list) partner_invoice = self.env["res.partner"].browse(partner_invoice_id) - partner_invoice_policy = ( - self.pms_property_id.default_invoicing_policy - if partner_invoice.invoicing_policy == "property" - else partner_invoice.invoicing_policy - ) + partner_invoice_policy = self.pms_property_id.default_invoicing_policy + if partner_invoice and partner_invoice.invoicing_policy != "property": + partner_invoice_policy = partner_invoice.invoicing_policy invoice_date = False if date: invoice_date = date @@ -1899,11 +1928,13 @@ class PmsFolio(models.Model): "Please contact your administrator to unlock it." ) ) - if invoice_date < datetime.date.today(): + if invoice_date < datetime.date.today() and not self._context.get( + "autoinvoice" + ): invoice_date = datetime.date.today() key_field = ( "invoice_date" - if invoice_date == fields.Date.today() + if invoice_date <= fields.Date.today() else "invoice_date_due" ) for vals in invoice_vals_list: diff --git a/pms/models/pms_property.py b/pms/models/pms_property.py index 803dcd6d7..61601a8c4 100644 --- a/pms/models/pms_property.py +++ b/pms/models/pms_property.py @@ -661,7 +661,7 @@ class PmsProperty(models.Model): return True @api.model - def autoinvoicing(self, offset=0): + def autoinvoicing(self, offset=0, with_delay=False): """ This method is used to invoicing automatically the folios and validate the draft invoices created by the folios @@ -689,58 +689,11 @@ class PmsProperty(models.Model): ) ) for folio in folios_to_invoice: - try: - # REVIEW: folio sale line "_compute_auotinvoice_date" sometimes - # dont work in services (probably cache issue¿?), we ensure that the date is - # set or recompute this - for line in folio.sale_line_ids.filtered( - lambda l: not l.autoinvoice_date - ): - line._compute_autoinvoice_date() - # REVIEW: Reverse downpayment invoices if the downpayment is not included - # in the service invoice (qty_to_invoice < 0) - downpayment_invoices = ( - folio.sale_line_ids.filtered( - lambda l: l.is_downpayment and l.qty_to_invoice < 0 - ) - .mapped("invoice_lines") - .mapped("move_id") - .filtered(lambda i: i.is_simplified_invoice) - ) - if downpayment_invoices: - downpayment_invoices._reverse_moves(cancel=True) - invoices = folio.with_context(autoinvoice=True)._create_invoices( - grouped=True, - final=True, - ) - for invoice in invoices: - if ( - invoice.amount_total - > invoice.pms_property_id.max_amount_simplified_invoice - and invoice.journal_id.is_simplified_invoice - ): - hosts_to_invoice = ( - invoice.folio_ids.partner_invoice_ids.filtered( - lambda p: p._check_enought_invoice_data() - ).mapped("id") - ) - if hosts_to_invoice: - invoice.partner_id = hosts_to_invoice[0] - invoice.journal_id = ( - invoice.pms_property_id.journal_normal_invoice_id - ) - else: - mens = _( - "The total amount of the simplified invoice is higher than the " - "maximum amount allowed for simplified invoices, and dont have " - "enought data in hosts to create a normal invoice." - ) - if self.folio_ids: - self.folio_ids.message_post(body=mens) - raise ValidationError(mens) - invoice.action_post() - except Exception as e: - folio.message_post(body=_("Error in autoinvoicing folio: " + str(e))) + if with_delay: + self.with_delay().autoinvoice_folio(folio) + else: + self.autoinvoice_folio(folio) + draft_invoices_to_post = self.env["account.move"].search( [ ("state", "=", "draft"), @@ -749,12 +702,67 @@ class PmsProperty(models.Model): ] ) for invoice in draft_invoices_to_post: - try: - invoice.action_post() - except Exception as e: - invoice.message_post(body=_("Error in autovalidate invoice: " + str(e))) + if with_delay: + self.with_delay().autovalidate_folio_invoice(invoice) + else: + self.autovalidate_folio_invoice(invoice) return True + def autovalidate_folio_invoice(self, invoice): + try: + invoice.action_post() + except Exception as e: + invoice.message_post(body=_("Error in autovalidate invoice: " + str(e))) + + def autoinvoice_folio(self, folio): + try: + # REVIEW: folio sale line "_compute_auotinvoice_date" sometimes + # dont work in services (probably cache issue¿?), we ensure that the date is + # set or recompute this + for line in folio.sale_line_ids.filtered(lambda l: not l.autoinvoice_date): + line._compute_autoinvoice_date() + # REVIEW: Reverse downpayment invoices if the downpayment is not included + # in the service invoice (qty_to_invoice < 0) + downpayment_invoices = ( + folio.sale_line_ids.filtered( + lambda l: l.is_downpayment and l.qty_to_invoice < 0 + ) + .mapped("invoice_lines") + .mapped("move_id") + .filtered(lambda i: i.is_simplified_invoice) + ) + if downpayment_invoices: + downpayment_invoices._reverse_moves(cancel=True) + invoices = folio.with_context(autoinvoice=True)._create_invoices( + grouped=True, + final=True, + ) + for invoice in invoices: + if ( + invoice.amount_total + > invoice.pms_property_id.max_amount_simplified_invoice + and invoice.journal_id.is_simplified_invoice + ): + hosts_to_invoice = invoice.folio_ids.partner_invoice_ids.filtered( + lambda p: p._check_enought_invoice_data() + ).mapped("id") + if hosts_to_invoice: + invoice.partner_id = hosts_to_invoice[0] + invoice.journal_id = ( + invoice.pms_property_id.journal_normal_invoice_id + ) + else: + mens = _( + "The total amount of the simplified invoice is higher than the " + "maximum amount allowed for simplified invoices, and dont have " + "enought data in hosts to create a normal invoice." + ) + folio.message_post(body=mens) + raise ValidationError(mens) + invoice.action_post() + except Exception as e: + folio.message_post(body=_("Error in autoinvoicing folio: " + str(e))) + @api.constrains("journal_normal_invoice_id") def _check_journal_normal_invoice(self): for pms_property in self.filtered("journal_normal_invoice_id"):