From ba8cb088e34795c76d058ee5592f90cf1406d0ea Mon Sep 17 00:00:00 2001 From: "Nicolas (vin)" Date: Thu, 19 Jan 2023 12:09:21 +0000 Subject: [PATCH] [FIX] account_edi_ubl_cii: tax import supports rounding A common use case for fiduciary is to use the available tax edition tool on the tax total of an invoice to round the total of an invoice. This is not supported by ubl, which will re-compute the taxes upon import and thus lose that manual rounding. This change aims to correct that, by post-processing the invoice after creation in order to correct the tax amount to the one in the XML if the different isn't superior to 0.05. Task id #3138441 X-original-commit: 5bc87804bf5bba49ffd939c844439b87e08b499e Part-of: odoo/odoo#111324 --- .../models/account_edi_common.py | 9 +++ .../models/account_edi_xml_ubl_20.py | 55 ++++++++++++++----- 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/addons/account_edi_ubl_cii/models/account_edi_common.py b/addons/account_edi_ubl_cii/models/account_edi_common.py index 1b528f7ee6d..01bc3bb4cf9 100644 --- a/addons/account_edi_ubl_cii/models/account_edi_common.py +++ b/addons/account_edi_ubl_cii/models/account_edi_common.py @@ -266,6 +266,12 @@ class AccountEdiCommon(models.AbstractModel): default_journal_id=journal.id, )._get_edi_creation() as invoice: logs = self._import_fill_invoice_form(journal, tree, invoice, qty_factor) + + # For UBL, we should override the computed tax amount if it is less than 0.05 different of the one in the xml. + # In order to support use case where the tax total is adapted for rounding purpose. + # This has to be done after the first import in order to let Odoo compute the taxes before overriding if needed. + with invoice.with_context(account_predictive_bills_disable_prediction=True)._get_edi_creation() as invoice: + self._correct_invoice_tax_amount(tree, invoice) if invoice: if logs: body = _( @@ -595,6 +601,9 @@ class AccountEdiCommon(models.AbstractModel): invoice_line_form.tax_ids = inv_line_vals['taxes'] return logs + def _correct_invoice_tax_amount(self, tree, invoice): + pass # To be implemented by the format if needed + # ------------------------------------------------------------------------- # Check xml using the free API from Ph. Helger, don't abuse it ! # ------------------------------------------------------------------------- diff --git a/addons/account_edi_ubl_cii/models/account_edi_xml_ubl_20.py b/addons/account_edi_ubl_cii/models/account_edi_xml_ubl_20.py index c9b9cd3f679..979c2b6a8d8 100644 --- a/addons/account_edi_ubl_cii/models/account_edi_xml_ubl_20.py +++ b/addons/account_edi_ubl_cii/models/account_edi_xml_ubl_20.py @@ -487,11 +487,28 @@ class AccountEdiXmlUBL20(models.AbstractModel): logs.append(_("Could not retrieve currency: %s. Did you enable the multicurrency option " "and activate the currency ?", currency_code_node.text)) + # ==== invoice_date ==== + + invoice_date_node = tree.find('./{*}IssueDate') + if invoice_date_node is not None and invoice_date_node.text: + invoice.invoice_date = invoice_date_node.text + + # ==== invoice_date_due ==== + + for xpath in ('./{*}DueDate', './/{*}PaymentDueDate'): + invoice_date_due_node = tree.find(xpath) + if invoice_date_due_node is not None and invoice_date_due_node.text: + invoice.invoice_date_due = invoice_date_due_node.text + break + # ==== Reference ==== ref_node = tree.find('./{*}ID') if ref_node is not None: - invoice.ref = ref_node.text + if invoice.is_sale_document(include_receipts=True) and invoice.quick_edit_mode: + invoice.name = ref_node.text + else: + invoice.ref = ref_node.text # === Note/narration ==== @@ -512,20 +529,6 @@ class AccountEdiXmlUBL20(models.AbstractModel): if payment_reference_node is not None: invoice.payment_reference = payment_reference_node.text - # ==== invoice_date ==== - - invoice_date_node = tree.find('./{*}IssueDate') - if invoice_date_node is not None and invoice_date_node.text: - invoice.invoice_date = invoice_date_node.text - - # ==== invoice_date_due ==== - - for xpath in ('./{*}DueDate', './/{*}PaymentDueDate'): - invoice_date_due_node = tree.find(xpath) - if invoice_date_due_node is not None and invoice_date_due_node.text: - invoice.invoice_date_due = invoice_date_due_node.text - break - # ==== invoice_incoterm_id ==== incoterm_code_node = tree.find('./{*}TransportExecutionTerms/{*}DeliveryTerms/{*}ID') @@ -596,6 +599,28 @@ class AccountEdiXmlUBL20(models.AbstractModel): tax_nodes += elem.findall('.//{*}TaxSubtotal/{*}Percent') return self._import_fill_invoice_line_taxes(journal, tax_nodes, invoice_line, inv_line_vals, logs) + def _correct_invoice_tax_amount(self, tree, invoice): + """ The tax total may have been modified for rounding purpose, if so we should use the imported tax and not + the computed one """ + # For each tax in our tax total, get the amount as well as the total in the xml. + for elem in tree.findall('.//{*}TaxTotal/{*}TaxSubtotal'): + percentage = elem.find('.//{*}TaxCategory/{*}Percent') + amount = elem.find('.//{*}TaxAmount') + if (percentage is not None and percentage.text is not None) and (amount is not None and amount.text is not None): + tax_percent = float(percentage.text) + # Compare the result with our tax total on the invoice, and apply correction if needed. + # First look for taxes matching the percentage in the xml. + taxes = invoice.line_ids.tax_line_id.filtered(lambda tax: tax.amount == tax_percent) + # If we found taxes with the correct amount, look for a tax line using it, and correct it as needed. + if taxes: + tax_total = float(amount.text) + tax_line = invoice.line_ids.filtered(lambda line: line.tax_line_id in taxes)[:1] + if tax_line: + sign = -1 if invoice.is_inbound(include_receipts=True) else 1 + tax_line_amount = abs(tax_line.amount_currency) + if abs(tax_total - tax_line_amount) <= 0.05: + tax_line.amount_currency = tax_total * sign + # ------------------------------------------------------------------------- # IMPORT : helpers # -------------------------------------------------------------------------