Format float and computed Decimal using fixed-point notation

This avoid the usage of scientific notation.
For float we must convert them to Decimal to get a precision large enough to
show all coefficient digits.
For computed Decimal (which are thus not rounded) we must normalized them first
to avoid trailing 0.

Closes #14522
This commit is contained in:
Cédric Krier
2026-01-20 09:19:12 +01:00
parent 8ae58308ce
commit 680344c92b
5 changed files with 18 additions and 12 deletions
+4 -1
View File
@@ -86,7 +86,10 @@ class Invoice(Model):
tmpl = self._get_template(template)
if not tmpl:
raise NotImplementedError
return (tmpl.generate(this=self, specification=specification)
return (tmpl.generate(
this=self,
specification=specification,
Decimal=Decimal)
.filter(remove_comment)
.render()
.encode('utf-8'))
@@ -70,10 +70,10 @@ this repository contains the full copyright notices and license terms. -->
<cac:TaxSubtotal py:for="line in lines">
<cbc:TaxableAmount py:attrs="{'currencyID': this.invoice.currency.code}">${-line.base}</cbc:TaxableAmount>
<cbc:TaxAmount py:attrs="{'currencyID': this.invoice.currency.code}">${-line.amount}</cbc:TaxAmount>
<cbc:Percent py:if="line.tax.type == 'percentage' and not (specification or '').startswith('peppol')">${line.tax.rate * 100}</cbc:Percent>
<cbc:Percent py:if="line.tax.type == 'percentage' and not (specification or '').startswith('peppol')">${format((line.tax.rate * 100).normalize(), 'f')}</cbc:Percent>
<cac:TaxCategory>
<cbc:ID py:if="line.tax.unece_category_code">${line.tax.unece_category_code}</cbc:ID>
<cbc:Percent py:if="line.tax.type == 'percentage'">${line.tax.rate * 100}</cbc:Percent>
<cbc:Percent py:if="line.tax.type == 'percentage'">${format((line.tax.rate * 100).normalize(), 'f')}</cbc:Percent>
<cac:TaxScheme py:if="line.tax.unece_code">
<cbc:ID>${line.tax.unece_code}</cbc:ID>
</cac:TaxScheme>
@@ -91,7 +91,7 @@ this repository contains the full copyright notices and license terms. -->
</cac:LegalMonetaryTotal>
<cac:CreditNoteLine py:for="line_id, line in enumerate(this.lines, 1)">
<cbc:ID>${line_id}</cbc:ID>
<cbc:CreditedQuantity py:attrs="{'unitCode': line.unit.unece_code if line.unit and line.unit.unece_code else 'ZZ'}">${-line.quantity}</cbc:CreditedQuantity>
<cbc:CreditedQuantity py:attrs="{'unitCode': line.unit.unece_code if line.unit and line.unit.unece_code else 'ZZ'}">${format(-Decimal(str(line.quantity)), 'f')}</cbc:CreditedQuantity>
<cbc:LineExtensionAmount py:attrs="{'currencyID': this.invoice.currency.code}">${-line.amount}</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Description py:if="line.description">${line.description}</cbc:Description>
@@ -111,7 +111,7 @@ this repository contains the full copyright notices and license terms. -->
</py:if>
<cac:ClassifiedTaxCategory py:for="tax in line.taxes">
<cbc:ID py:if="tax.unece_category_code">${tax.unece_category_code}</cbc:ID>
<cbc:Percent py:if="tax.type == 'percentage'">${tax.rate * 100}</cbc:Percent>
<cbc:Percent py:if="tax.type == 'percentage'">${format((tax.rate * 100).normalize(), 'f')}</cbc:Percent>
<cac:TaxScheme py:if="tax.unece_code">
<cbc:ID>${tax.unece_code}</cbc:ID>
</cac:TaxScheme>
+4 -4
View File
@@ -70,10 +70,10 @@ this repository contains the full copyright notices and license terms. -->
<cac:TaxSubtotal py:for="line in lines">
<cbc:TaxableAmount py:attrs="{'currencyID': this.invoice.currency.code}">${line.base}</cbc:TaxableAmount>
<cbc:TaxAmount py:attrs="{'currencyID': this.invoice.currency.code}">${line.amount}</cbc:TaxAmount>
<cbc:Percent py:if="line.tax.type == 'percentage' and not (specification or '').startswith('peppol')">${line.tax.rate * 100}</cbc:Percent>
<cbc:Percent py:if="line.tax.type == 'percentage' and not (specification or '').startswith('peppol')">${format((line.tax.rate * 100).normalize(), 'f')}</cbc:Percent>
<cac:TaxCategory>
<cbc:ID py:if="line.tax.unece_category_code">${line.tax.unece_category_code}</cbc:ID>
<cbc:Percent py:if="line.tax.type == 'percentage'">${line.tax.rate * 100}</cbc:Percent>
<cbc:Percent py:if="line.tax.type == 'percentage'">${format((line.tax.rate * 100).normalize(), 'f')}</cbc:Percent>
<cac:TaxScheme py:if="line.tax.unece_code">
<cbc:ID>${line.tax.unece_code}</cbc:ID>
</cac:TaxScheme>
@@ -91,7 +91,7 @@ this repository contains the full copyright notices and license terms. -->
</cac:LegalMonetaryTotal>
<cac:InvoiceLine py:for="line_id, line in enumerate(this.lines, 1)">
<cbc:ID>${line_id}</cbc:ID>
<cbc:InvoicedQuantity py:attrs="{'unitCode': line.unit.unece_code if line.unit and line.unit.unece_code else 'ZZ'}">${line.quantity}</cbc:InvoicedQuantity>
<cbc:InvoicedQuantity py:attrs="{'unitCode': line.unit.unece_code if line.unit and line.unit.unece_code else 'ZZ'}">${format(Decimal(str(line.quantity)), 'f')}</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount py:attrs="{'currencyID': this.invoice.currency.code}">${line.amount}</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Description py:if="line.description">${line.description}</cbc:Description>
@@ -111,7 +111,7 @@ this repository contains the full copyright notices and license terms. -->
</py:if>
<cac:ClassifiedTaxCategory py:for="tax in line.taxes">
<cbc:ID py:if="tax.unece_category_code">${tax.unece_category_code}</cbc:ID>
<cbc:Percent py:if="tax.type == 'percentage'">${tax.rate * 100}</cbc:Percent>
<cbc:Percent py:if="tax.type == 'percentage'">${format((tax.rate * 100).normalize(), 'f')}</cbc:Percent>
<cac:TaxScheme py:if="tax.unece_code">
<cbc:ID>${tax.unece_code}</cbc:ID>
</cac:TaxScheme>
+4 -1
View File
@@ -2,6 +2,7 @@
# this repository contains the full copyright notices and license terms.
import functools
import os
from decimal import Decimal
import genshi
import genshi.template
@@ -78,7 +79,9 @@ class Invoice(Model):
tmpl = self._get_template(template)
if not tmpl:
raise NotImplementedError
return (tmpl.generate(this=self)
return (tmpl.generate(
this=self,
Decimal=Decimal)
.filter(remove_comment)
.render()
.encode('utf-8'))
@@ -48,7 +48,7 @@ this repository contains the full copyright notices and license terms. -->
<ram:ExemptionReason py:if="tax.legal_notice">${tax.legal_notice}</ram:ExemptionReason>
<ram:BasisAmount py:if="base">${base * this.type_sign}</ram:BasisAmount>
<ram:CategoryCode py:if="tax.unece_category_code">${tax.unece_category_code}</ram:CategoryCode>
<ram:RateApplicablePercent py:if="tax.type == 'percentage'">${tax.rate * 100}</ram:RateApplicablePercent>
<ram:RateApplicablePercent py:if="tax.type == 'percentage'">${format((tax.rate * 100).normalize(), 'f')}</ram:RateApplicablePercent>
</ram:ApplicableTradeTax>
</py:def>
<rsm:ExchangedDocumentContext>
@@ -76,7 +76,7 @@ this repository contains the full copyright notices and license terms. -->
</ram:NetPriceProductTradePrice>
</ram:SpecifiedLineTradeAgreement>
<ram:SpecifiedLineTradeDelivery>
<ram:BilledQuantity py:attrs="{'unitCode': line.unit.unece_code} if line.unit and line.unit.unece_code else {}">${line.quantity * this.type_sign}</ram:BilledQuantity>
<ram:BilledQuantity py:attrs="{'unitCode': line.unit.unece_code} if line.unit and line.unit.unece_code else {}">${format(Decimal(str(line.quantity * this.type_sign)), 'f')}</ram:BilledQuantity>
</ram:SpecifiedLineTradeDelivery>
<ram:SpecifiedLineTradeSettlement>
<py:for each="tax in line.invoice_taxes">