[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
This commit is contained in:
Nicolas (vin)
2023-01-30 22:27:14 +01:00
parent b444e5671a
commit ba8cb088e3
2 changed files with 49 additions and 15 deletions
@@ -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 !
# -------------------------------------------------------------------------
@@ -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
# -------------------------------------------------------------------------