details: https://code.tryton.org/tryton/commit/f76f971c8b48
branch: default
user: Cédric Krier <[email protected]>
date: Sat Mar 14 00:46:13 2026 +0100
description:
Fill invoice source amounts instead of checking amounts from UBL
Closes #14624
diffstat:
modules/edocument_ubl/CHANGELOG | 1 +
modules/edocument_ubl/edocument.py | 148
++++-----
modules/edocument_ubl/message.xml | 6 -
modules/edocument_ubl/tests/scenario_ubl_2_credit_note_parse.rst | 5 +
modules/edocument_ubl/tests/scenario_ubl_2_invoice_parse.rst | 5 +
modules/edocument_ubl/tests/scenario_ubl_2_invoice_parse_trivial.rst | 4 +
6 files changed, 85 insertions(+), 84 deletions(-)
diffs (260 lines):
diff -r 5d5cf986829c -r f76f971c8b48 modules/edocument_ubl/CHANGELOG
--- a/modules/edocument_ubl/CHANGELOG Fri Mar 13 23:53:22 2026 +0100
+++ b/modules/edocument_ubl/CHANGELOG Sat Mar 14 00:46:13 2026 +0100
@@ -1,3 +1,4 @@
+* Fill invoice source amounts instead of checking amounts
* Filter the MIME type of the additional documents allowed by the specification
* Use VATEX as tax exemption reason code
* Add support for Python 3.14
diff -r 5d5cf986829c -r f76f971c8b48 modules/edocument_ubl/edocument.py
--- a/modules/edocument_ubl/edocument.py Fri Mar 13 23:53:22 2026 +0100
+++ b/modules/edocument_ubl/edocument.py Sat Mar 14 00:46:13 2026 +0100
@@ -282,7 +282,6 @@
invoice, attachments = cls.parser(namespace)(root)
invoice.save()
invoice.update_taxes()
- cls.checker(namespace)(root, invoice)
attachments = list(attachments)
for attachment in attachments:
attachment.resource = invoice
@@ -299,15 +298,6 @@
}.get(namespace)
@classmethod
- def checker(cls, namespace):
- return {
- 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2': (
- cls._check_invoice_2),
- 'urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2': (
- cls._check_credit_note_2),
- }.get(namespace)
-
- @classmethod
def _parse_invoice_2(cls, root):
pool = Pool()
Invoice = pool.get('account.invoice')
@@ -406,6 +396,41 @@
'./{*}LegalMonetaryTotal/{*}PayableRoundingAmount')
is not None)):
invoice.cash_rounding = True
+
+ if tax_exlusive_amount := root.findtext(
+ './{*}LegalMonetaryTotal/{*}TaxExclusiveAmount'):
+ untaxed_amount = Decimal(tax_exlusive_amount)
+
+ if allowance_total_amount := root.findtext(
+ './{*}LegalMonetaryTotal/{*}AllowanceTotalAmount'):
+ allowance_total_amount = Decimal(allowance_total_amount)
+ untaxed_amount -= allowance_total_amount
+
+ if charge_total_amount := root.findtext(
+ './{*}LegalMonetaryTotal/{*}ChargeTotalAmount'):
+ charge_total_amount = Decimal(charge_total_amount)
+ untaxed_amount += charge_total_amount
+
+ invoice.source_untaxed_amount = untaxed_amount
+
+ tax_amount = sum(Decimal(amount.text) for amount in root.iterfind(
+ './{*}TaxTotal/{*}TaxAmount'))
+ invoice.source_tax_amount = tax_amount
+
+ payable_amount = Decimal(
+ root.findtext('./{*}LegalMonetaryTotal/{*}PayableAmount'))
+ prepaid_amount = Decimal(
+ root.findtext('./{*}LegalMonetaryTotal/{*}PrepaidAmount')
+ or 0)
+ total_amount = payable_amount + prepaid_amount
+ if not getattr(invoice, 'cash_rounding', False):
+ payable_rounding_amount = Decimal(
+ root.findtext(
+ './{*}LegalMonetaryTotal/{*}PayableRoundingAmount')
+ or 0)
+ total_amount -= payable_rounding_amount
+ invoice.source_total_amount = total_amount
+
return invoice, cls._parse_2_attachments(root)
@classmethod
@@ -682,6 +707,41 @@
'./{*}LegalMonetaryTotal/{*}PayableRoundingAmount')
is not None):
invoice.cash_rounding = True
+
+ if tax_exlusive_amount := root.findtext(
+ './{*}LegalMonetaryTotal/{*}TaxExclusiveAmount'):
+ untaxed_amount = Decimal(tax_exlusive_amount)
+
+ if allowance_total_amount := root.findtext(
+ './{*}LegalMonetaryTotal/{*}AllowanceTotalAmount'):
+ allowance_total_amount = Decimal(allowance_total_amount)
+ untaxed_amount -= allowance_total_amount
+
+ if charge_total_amount := root.findtext(
+ './{*}LegalMonetaryTotal/{*}ChargeTotalAmount'):
+ charge_total_amount = Decimal(charge_total_amount)
+ untaxed_amount += charge_total_amount
+
+ invoice.source_untaxed_amount = -untaxed_amount
+
+ tax_amount = sum(Decimal(amount.text) for amount in root.iterfind(
+ './{*}TaxTotal/{*}TaxAmount'))
+ invoice.source_tax_amount = -tax_amount
+
+ payable_amount = Decimal(
+ root.findtext('./{*}LegalMonetaryTotal/{*}PayableAmount'))
+ prepaid_amount = Decimal(
+ root.findtext('./{*}LegalMonetaryTotal/{*}PrepaidAmount')
+ or 0)
+ total_amount = payable_amount + prepaid_amount
+ if not getattr(invoice, 'cash_rounding', False):
+ payable_rounding_amount = Decimal(
+ root.findtext(
+ './{*}LegalMonetaryTotal/{*}PayableRoundingAmount')
+ or 0)
+ total_amount -= payable_rounding_amount
+ invoice.source_total_amount = -total_amount
+
return invoice, cls._parse_2_attachments(root)
@classmethod
@@ -1259,74 +1319,6 @@
attachment.name = name
yield attachment
- @classmethod
- def _check_invoice_2(cls, root, invoice):
- pool = Pool()
- Lang = pool.get('ir.lang')
- lang = Lang.get()
-
- payable_amount = Decimal(
- root.findtext('./{*}LegalMonetaryTotal/{*}PayableAmount'))
- prepaid_amount = Decimal(
- root.findtext('./{*}LegalMonetaryTotal/{*}PrepaidAmount')
- or 0)
- amount = payable_amount + prepaid_amount
- if not getattr(invoice, 'cash_rounding', False):
- payable_rounding_amount = Decimal(
- root.findtext(
- './{*}LegalMonetaryTotal/{*}PayableRoundingAmount')
- or 0)
- amount -= payable_rounding_amount
- if invoice.total_amount != amount:
- raise InvoiceError(gettext(
- 'edocument_ubl.msg_invoice_total_amount_different',
- invoice=invoice.rec_name,
- total_amount=lang.format_number(invoice.total_amount),
- amount=lang.format_number(amount)))
-
- tax_total = sum(Decimal(amount.text) for amount in root.iterfind(
- './{*}TaxTotal/{*}TaxAmount'))
- if invoice.tax_amount != tax_total:
- raise InvoiceError(gettext(
- 'edocument_ubl.msg_invoice_tax_amount_different',
- invoice=invoice.rec_name,
- tax_amount=lang.format_number(invoice.tax_amount),
- tax_total=lang.format_number(tax_total)))
-
- @classmethod
- def _check_credit_note_2(cls, root, invoice):
- pool = Pool()
- Lang = pool.get('ir.lang')
- lang = Lang.get()
-
- payable_amount = Decimal(
- root.findtext('./{*}LegalMonetaryTotal/{*}PayableAmount'))
- prepaid_amount = Decimal(
- root.findtext('./{*}LegalMonetaryTotal/{*}PrepaidAmount')
- or 0)
- amount = payable_amount + prepaid_amount
- if not getattr(invoice, 'cash_rounding', False):
- payable_rounding_amount = Decimal(
- root.findtext(
- './{*}LegalMonetaryTotal/{*}PayableRoundingAmount')
- or 0)
- amount -= payable_rounding_amount
- if -invoice.total_amount != amount:
- raise InvoiceError(gettext(
- 'edocument_ubl.msg_invoice_total_amount_different',
- invoice=invoice.rec_name,
- total_amount=lang.format_number(-invoice.total_amount),
- amount=lang.format_number(amount)))
-
- tax_total = sum(Decimal(amount.text) for amount in root.iterfind(
- './{*}TaxTotal/{*}TaxAmount'))
- if -invoice.tax_amount != tax_total:
- raise InvoiceError(gettext(
- 'edocument_ubl.msg_invoice_tax_amount_different',
- invoice=invoice.rec_name,
- tax_amount=lang.format_number(-invoice.tax_amount),
- tax_total=lang.format_number(tax_total)))
-
class Invoice_Bank(metaclass=PoolMeta):
__name__ = 'edocument.ubl.invoice'
diff -r 5d5cf986829c -r f76f971c8b48 modules/edocument_ubl/message.xml
--- a/modules/edocument_ubl/message.xml Fri Mar 13 23:53:22 2026 +0100
+++ b/modules/edocument_ubl/message.xml Sat Mar 14 00:46:13 2026 +0100
@@ -24,11 +24,5 @@
<field name="text">Could not find tax for:
%(tax_category)s</field>
</record>
- <record model="ir.message" id="msg_invoice_total_amount_different">
- <field name="text">The total amount %(total_amount)s of the
invoice "%(invoice)s" is different from the amount %(amount)s.</field>
- </record>
- <record model="ir.message" id="msg_invoice_tax_amount_different">
- <field name="text">The tax amount %(tax_amount)s of the invoice
"%(invoice)s" is different from the tax total %(tax_total)s.</field>
- </record>
</data>
</tryton>
diff -r 5d5cf986829c -r f76f971c8b48
modules/edocument_ubl/tests/scenario_ubl_2_credit_note_parse.rst
--- a/modules/edocument_ubl/tests/scenario_ubl_2_credit_note_parse.rst Fri Mar
13 23:53:22 2026 +0100
+++ b/modules/edocument_ubl/tests/scenario_ubl_2_credit_note_parse.rst Sat Mar
14 00:46:13 2026 +0100
@@ -79,6 +79,11 @@
... Decimal('-1729.00') if cash_rounding else Decimal('-1728.70'))
>>> invoice.tax_amount
Decimal('-292.20')
+ >>> assertEqual(invoice.source_untaxed_amount, Decimal('-1436.50'))
+ >>> assertEqual(invoice.source_tax_amount, Decimal('-292.20'))
+ >>> assertEqual(
+ ... invoice.source_total_amount,
+ ... Decimal('-1729.00') if cash_rounding else Decimal('-1728.70'))
>>> len(invoice.lines)
7
diff -r 5d5cf986829c -r f76f971c8b48
modules/edocument_ubl/tests/scenario_ubl_2_invoice_parse.rst
--- a/modules/edocument_ubl/tests/scenario_ubl_2_invoice_parse.rst Fri Mar
13 23:53:22 2026 +0100
+++ b/modules/edocument_ubl/tests/scenario_ubl_2_invoice_parse.rst Sat Mar
14 00:46:13 2026 +0100
@@ -79,6 +79,11 @@
... Decimal('1729.00') if cash_rounding else Decimal('1728.70'))
>>> invoice.tax_amount
Decimal('292.20')
+ >>> assertEqual(invoice.source_untaxed_amount, Decimal('1436.50'))
+ >>> assertEqual(invoice.source_tax_amount, Decimal('292.20'))
+ >>> assertEqual(
+ ... invoice.source_total_amount,
+ ... Decimal('1729.00') if cash_rounding else Decimal('1728.70'))
>>> len(invoice.lines)
7
diff -r 5d5cf986829c -r f76f971c8b48
modules/edocument_ubl/tests/scenario_ubl_2_invoice_parse_trivial.rst
--- a/modules/edocument_ubl/tests/scenario_ubl_2_invoice_parse_trivial.rst
Fri Mar 13 23:53:22 2026 +0100
+++ b/modules/edocument_ubl/tests/scenario_ubl_2_invoice_parse_trivial.rst
Sat Mar 14 00:46:13 2026 +0100
@@ -5,6 +5,7 @@
Imports::
>>> import datetime as dt
+ >>> from decimal import Decimal
>>> from proteus import Model
>>> from trytond.modules.account.tests.tools import create_chart
@@ -46,6 +47,9 @@
>>> assertEqual(invoice.company, company)
>>> invoice.total_amount
Decimal('100.00')
+ >>> invoice.source_untaxed_amount
+ >>> assertEqual(invoice.source_tax_amount, Decimal('0.00'))
+ >>> assertEqual(invoice.source_total_amount, Decimal('100.00'))
>>> line, = invoice.lines
>>> line.description
'Cotter pin, MIL-SPEC'