details:   https://code.tryton.org/tryton/commit/07a0ab59f30e
branch:    default
user:      Cédric Krier <[email protected]>
date:      Fri Jan 23 23:49:58 2026 +0100
description:
        Search unit of measure with enough digits for the quantity in UBL 
invoice and credit note

        Closes #14541
diffstat:

 modules/edocument_ubl/edocument.py |  54 +++++++++++++++++++++++++++++++++----
 modules/edocument_ubl/message.xml  |   3 +-
 2 files changed, 50 insertions(+), 7 deletions(-)

diffs (126 lines):

diff -r ba15a67c6c7d -r 07a0ab59f30e modules/edocument_ubl/edocument.py
--- a/modules/edocument_ubl/edocument.py        Fri Jan 30 11:20:26 2026 +0100
+++ b/modules/edocument_ubl/edocument.py        Fri Jan 23 23:49:58 2026 +0100
@@ -18,7 +18,7 @@
 from genshi.template.astutil import ASTCodeGenerator, ASTTransformer
 from lxml import etree
 
-from trytond.i18n import gettext
+from trytond.i18n import gettext, ngettext
 from trytond.model import Model
 from trytond.modules.product import round_price
 from trytond.pool import Pool, PoolMeta
@@ -392,26 +392,48 @@
         if (invoiced_quantity := invoice_line.find('./{*}InvoicedQuantity')
                 ) is not None:
             line.quantity = float(invoiced_quantity.text)
+            digits = -Decimal(invoiced_quantity.text).as_tuple().exponent
             if (unit_code := invoiced_quantity.get('unitCode')) not in {
                     None, 'ZZ', 'XZZ'}:
                 try:
                     line.unit, = UoM.search([
                             ('unece_code', '=', unit_code),
-                            ], limit=1)
+                            ('digits', '>=', digits),
+                            ],
+                        order=[('digits', 'ASC')],
+                        limit=1)
                 except ValueError:
-                    raise InvoiceError(gettext(
+                    raise InvoiceError(ngettext(
                             'edocument_ubl.msg_unit_not_found',
-                            code=unit_code))
+                            digits,
+                            code=unit_code,
+                            digits=digits))
             else:
                 line.unit = None
         else:
             line.quantity = 1
             line.unit = None
+            digits = 0
 
         line.product = cls._parse_2_item(
             invoice_line.find('./{*}Item'), supplier=supplier)
         if line.product:
             line.on_change_product()
+            if line.unit and line.unit.digits < digits:
+                # Search the unit with enough digits
+                try:
+                    line.unit, = UoM.search([
+                            ('category', '=', line.unit.category),
+                            ('digits', '>=', digits),
+                            ['OR',
+                                ('factor', '=', line.unit.factor),
+                                ('rate', '=', line.unit.rate),
+                                ],
+                            ],
+                        order=[('digits', 'ASC')],
+                        limit=1)
+                except ValueError:
+                    pass
 
         line.description = '\n'.join(e.text for e in chain(
                 invoice_line.iterfind('./{*}Item/{*}Name'),
@@ -640,26 +662,46 @@
         if (credited_quantity := credit_note_line.find('./{*}CreditedQuantity')
                 ) is not None:
             line.quantity = -float(credited_quantity.text)
+            digits = -Decimal(credited_quantity.text).as_tuple().exponent
             if (unit_code := credited_quantity.get('unitCode')) not in {
                     None, 'ZZ', 'XZZ'}:
                 try:
                     line.unit, = UoM.search([
                             ('unece_code', '=', unit_code),
+                            ('digits', '>=', digits),
                             ], limit=1)
                 except ValueError:
-                    raise InvoiceError(gettext(
+                    raise InvoiceError(ngettext(
                             'edocument_ubl.msg_unit_not_found',
-                            code=unit_code))
+                            digits,
+                            code=unit_code,
+                            digits=digits))
             else:
                 line.unit = None
         else:
             line.quantity = -1
             line.unit = None
+            digits = 0
 
         line.product = cls._parse_2_item(
             credit_note_line.find('./{*}Item'), supplier=supplier)
         if line.product:
             line.on_change_product()
+            if line.unit and line.unit.digits < digits:
+                # Search the unit with enough digits
+                try:
+                    line.unit, = UoM.search([
+                            ('category', '=', line.unit.category),
+                            ('digits', '>=', digits),
+                            ['OR',
+                                ('factor', '=', line.unit.factor),
+                                ('rate', '=', line.unit.rate),
+                                ],
+                            ],
+                        order=[('digits', 'ASC')],
+                        limit=1)
+                except ValueError:
+                    pass
 
         line.description = '\n'.join(e.text for e in chain(
                 credit_note_line.iterfind('./{*}Item/{*}Name'),
diff -r ba15a67c6c7d -r 07a0ab59f30e modules/edocument_ubl/message.xml
--- a/modules/edocument_ubl/message.xml Fri Jan 30 11:20:26 2026 +0100
+++ b/modules/edocument_ubl/message.xml Fri Jan 23 23:49:58 2026 +0100
@@ -17,7 +17,8 @@
             <field name="text">Could not find a currency with code 
"%(code)s".</field>
         </record>
         <record model="ir.message" id="msg_unit_not_found">
-            <field name="text">Could not find a unit with UNECE code 
"%(code)s".</field>
+            <field name="text">Could not find a unit with UNECE code 
"%(code)s" and at least %(digits)s digit.</field>
+            <field name="text_plural">Could not find a unit with UNECE code 
"%(code)s" and at least %(digits)s digits.</field>
         </record>
         <record model="ir.message" id="msg_tax_not_found">
             <field name="text">Could not find tax for:

Reply via email to