#288960·odoo

[19.0] account_edi_ubl_cii: CII import rounds unit price to 2 decimals, causing a rounding line and wrong tax total

Author: RZT-NicoCreated Sep 17, 2026Updated Sep 17, 2026

Odoo Version

  • 17.0
  • 18.0
  • 19.0
  • Other (specify)

Steps to Reproduce

Odoo Version

19.0

Modules involved:

  • account
  • account_edi_ubl_cii
  • l10n_fr_pdp

Steps to Reproduce

Import a Factur-X/CII vendor bill containing one invoice line with these values:

<ram:IncludedSupplyChainTradeLineItem>
    <ram:AssociatedDocumentLineDocument>
        <ram:LineID>1</ram:LineID>
    </ram:AssociatedDocumentLineDocument>

    <ram:SpecifiedTradeProduct>
        <ram:Name>Test product</ram:Name>
    </ram:SpecifiedTradeProduct>

    <ram:SpecifiedLineTradeAgreement>
        <ram:GrossPriceProductTradePrice>
            <ram:ChargeAmount>5.3280</ram:ChargeAmount>
            <ram:BasisQuantity unitCode="C62">1.0000</ram:BasisQuantity>
            <ram:AppliedTradeAllowanceCharge>
                <ram:ChargeIndicator>
                    <udt:Indicator>false</udt:Indicator>
                </ram:ChargeIndicator>
                <ram:ActualAmount>2.3070</ram:ActualAmount>
            </ram:AppliedTradeAllowanceCharge>
        </ram:GrossPriceProductTradePrice>

        <ram:NetPriceProductTradePrice>
            <ram:ChargeAmount>3.0210</ram:ChargeAmount>
            <ram:BasisQuantity unitCode="C62">1.0000</ram:BasisQuantity>
        </ram:NetPriceProductTradePrice>
    </ram:SpecifiedLineTradeAgreement>

    <ram:SpecifiedLineTradeDelivery>
        <ram:BilledQuantity unitCode="C62">10368.0000</ram:BilledQuantity>
    </ram:SpecifiedLineTradeDelivery>

    <ram:SpecifiedLineTradeSettlement>
        <ram:ApplicableTradeTax>
            <ram:TypeCode>VAT</ram:TypeCode>
            <ram:CategoryCode>S</ram:CategoryCode>
            <ram:RateApplicablePercent>5.50</ram:RateApplicablePercent>
        </ram:ApplicableTradeTax>

        <ram:SpecifiedTradeSettlementLineMonetarySummation>
            <ram:LineTotalAmount>31321.73</ram:LineTotalAmount>
        </ram:SpecifiedTradeSettlementLineMonetarySummation>
    </ram:SpecifiedLineTradeSettlement>
</ram:IncludedSupplyChainTradeLineItem>

Use these document-level totals:

<ram:SpecifiedTradeSettlementHeaderMonetarySummation>
    <ram:LineTotalAmount>31321.73</ram:LineTotalAmount>
    <ram:ChargeTotalAmount>0.00</ram:ChargeTotalAmount>
    <ram:AllowanceTotalAmount>0.00</ram:AllowanceTotalAmount>
    <ram:TaxBasisTotalAmount>31321.73</ram:TaxBasisTotalAmount>
    <ram:TaxTotalAmount currencyID="EUR">1722.70</ram:TaxTotalAmount>
    <ram:GrandTotalAmount>33044.43</ram:GrandTotalAmount>
    <ram:TotalPrepaidAmount>0.00</ram:TotalPrepaidAmount>
    <ram:DuePayableAmount>33044.43</ram:DuePayableAmount>
</ram:SpecifiedTradeSettlementHeaderMonetarySummation>

The calculation expressed by the CII document is:

Gross unit price:       5.3280
Unit price discount:   -2.3070
Net unit price:         3.0210

3.0210 × 10,368 = 31,321.728
Rounded line total   = 31,321.73
VAT 5.5%            =  1,722.70
Grand total          = 33,044.43

This case can also be added to the existing CII import tests, near:

addons/account_edi_ubl_cii/tests/test_files/import/facturx/invoice/fr/
test_partial_import_invoice_line_line_extension_amount_full_price_node_and_invoiced_quantity.xml

Current Behavior

Odoo reconstructs the gross unit price as:

round(3.0210 + 2.3070, 2) = 5.33

The imported product line consequently has approximately:

Quantity:       10,368
Unit price:     5.33
Line subtotal: 31,342.46
VAT 5.5%:       1,723.84

Odoo then creates an additional untaxed line named Rounding to bring the untaxed total back to EUR 31,321.73.

The untaxed total is compensated, but the VAT and grand total remain incorrect because the rounding line has no tax.

The exact difference may vary slightly when additional CII line-level allowances or charges are present, but the root cause remains the rounding of the unit price from 5.3280 to 5.33.

Expected Behavior

Odoo should preserve sufficient precision when importing the unit price.

The imported bill should have:

Untaxed amount: 31,321.73
VAT:             1,722.70
Grand total:    33,044.43

No artificial Rounding invoice line should be necessary.

At minimum:

invoice.amount_untaxed == 31321.73
invoice.amount_tax == 1722.70
invoice.amount_total == 33044.43

Technical Analysis

The problem appears to be caused by:

addons/account_edi_ubl_cii/models/account_edi_cii.py
_import_cii_invoice_line_add_price_unit_quantity_discount()

Current code:

price_unit = round(
    (price_subtotal + price_discount_amount) / price_quantity,
    2,
)

For this example:

price_subtotal = 3.0210
price_discount_amount = 2.3070
price_quantity = 1

price_unit = round(5.3280, 2)
price_unit = 5.33

The lost precision is multiplied by the billed quantity:

(5.33 - 5.328) × 10,368 = 20.736

Unit prices in EN 16931 are not restricted to the two-decimal precision of monetary totals. Rounding the reconstructed unit price to the currency precision before multiplying it by the quantity can therefore create a material difference.

The additional Rounding line is created later by:

_import_cii_invoice_fix_untaxed_amount()

It compensates the untaxed total but has no tax. The VAT is consequently calculated from the incorrect product-line subtotal.

The XML tax correction is not applied when the tax difference exceeds the hard-coded tolerance in:

account.edi.common._import_invoice_fix_taxes_amounts()

As a result, the imported bill may have a grand total different from the legally received electronic invoice.

Suspected Regression

The dedicated CII import pipeline was introduced by:

Before this refactoring, the generic importer preserved the gross price precision:

price_unit = gross_price_unit / basis_qty

It then inferred the discount from the CII LineTotalAmount.

The previous calculation therefore retained 5.3280 instead of replacing it with 5.33.

Proposed Direction

Do not round the reconstructed CII unit price to two decimals at this stage:

price_unit = (
    price_subtotal + price_discount_amount
) / price_quantity

Alternatively, use GrossPriceProductTradePrice/ChargeAmount directly when it is provided and consistent with the net price and price discount.

A regression test should verify that:

invoice.amount_untaxed == 31321.73
invoice.amount_tax == 1722.70
invoice.amount_total == 33044.43

and that no invoice line named Rounding is created.

Log Output

No traceback is produced. The import succeeds but creates incorrect accounting amounts.

Support Ticket

Not available.

Additional Information

The issue was initially detected on a real incoming French Factur-X invoice. The original document cannot be shared for confidentiality reasons, so all business-identifying information has been removed from this report.

AI disclosure: This issue was prepared by OpenAI Codex on behalf of the reporter, based on an analysis of the Factur-X data and the Odoo 19.0 source code. The technical findings and reproduction steps were generated by Codex.

Log Output

Support Ticket

No response