Les formules, telles que les écrit le Schematron
| Règle | Texte d'EN 16931 (Schematron validation-1.3.17) |
|---|---|
| BR-CO-15 | Invoice total amount with VAT (BT-112) = Invoice total amount without VAT (BT-109) + Invoice total VAT amount (BT-110). |
| BR-CO-14 | Invoice total VAT amount (BT-110) = Σ VAT category tax amount (BT-117). |
| BR-CO-16 | Amount due for payment (BT-115) = Invoice total amount with VAT (BT-112) -Paid amount (BT-113) +Rounding amount (BT-114). |
| BR-CO-17 | VAT category tax amount (BT-117) = VAT category taxable amount (BT-116) x (VAT category rate (BT-119) / 100), rounded to two decimals. |
| BR-S-08 | For each different value of VAT category rate (BT-119) where the VAT category code (BT-118) is "Standard rated", the VAT category taxable amount (BT-116) in a VAT breakdown (BG-23) shall equal the sum of Invoice line net amounts (BT-131) plus the sum of document level charge amounts (BT-99) minus the sum of document level allowance amounts (BT-92) where the VAT category code (BT-151, BT-102, BT-95) is "Standard rated" and the VAT rate (BT-152, BT-103, BT-96) equals the VAT category rate (BT-119). |
| BR-S-09 | The VAT category tax amount (BT-117) in a VAT breakdown (BG-23) where VAT category code (BT-118) is "Standard rated" shall equal the VAT category taxable amount (BT-116) multiplied by the VAT category rate (BT-119). |
Les six sont fatales. Lisez-les comme une chaîne : les lignes additionnent la base de chaque taux (BR-S-08), chaque base multipliée par son taux donne la TVA de ce taux (BR-CO-17 et BR-S-09), les TVA par taux additionnées donnent la TVA totale (BR-CO-14), base plus TVA donne le total (BR-CO-15), et le total moins ce qui a été payé plus l'arrondi donne le montant restant dû (BR-CO-16).

Quelles règles remarquent un centime
Toutes ne comptent pas les centimes. BR-CO-17 et BR-S-09 comparent la TVA de chaque taux (BT-117) à la base multipliée par le taux, mais le Schematron leur laisse 1 € de marge dans un sens comme dans l'autre. BR-S-08 compare des bases, donc l'arrondi de la TVA ne l'affecte pas. BR-CO-14, BR-CO-15 et BR-CO-16 comparent des sommes arrondies à deux décimales, sans marge : là, un centime est déjà une erreur.
Le centime apparaît lorsqu'il existe deux façons de calculer la TVA. Odoo la calcule selon la méthode d'arrondi des taxes définie pour la société (Tax Calculation Rounding Method) :
| Version d'Odoo | Options | Par défaut |
|---|---|---|
| Odoo 17 et 18 | Round per Line ou Round Globally | Par ligne |
| Odoo 19 et 20 | Round per Tax ou Round per Line | Par taxe |
Avec l'arrondi par ligne, Odoo arrondit la TVA de chaque ligne puis additionne. Sur une facture comportant beaucoup de petites lignes, cette somme peut se retrouver à un centime de la TVA calculée sur la base de chaque taux. Si tous les chiffres du fichier proviennent du même calcul, les sommes concordent. Si le fichier porte la TVA totale d'Odoo à côté d'un détail recalculé par taux, BR-CO-14 échoue ; s'il porte le total TTC d'Odoo à côté d'une TVA recalculée, c'est BR-CO-15 qui échoue. Pour que BR-CO-17 se déclenche, l'écart doit dépasser 1 €.
La méthode d'arrondi est un réglage de la société, et non de chaque facture : la modifier affecte les taxes calculées à partir de ce moment. Faites-le entre deux factures, jamais au milieu d'une opération.
Ce que fait le Factur-X d'Odoo
Dans le Factur-X (CII) d'Odoo 18, 19 et 20, le total TTC (BT-112) est écrit comme la somme de la base (BT-109) et de la TVA (BT-110) déjà présentes dans le fichier, précisément pour respecter BR-CO-15. Cela garantit la règle de la somme ; le reste de la chaîne dépend toujours de la concordance du détail.
Avec notre module XRechnung et ZUGFeRD
Le module génère le XML avec son propre code, et mélange aujourd'hui les deux calculs. Il calcule la TVA de chaque taux (BT-117) comme base multipliée par taux, et recopie le total TTC (en UBL) ou la TVA totale (en CII) depuis la facture Odoo. Avec l'arrondi par ligne, un centime d'écart suffit : le XML sort avec BR-CO-15 en XRechnung UBL et avec BR-CO-14 en XRechnung CII, le format que le module utilise par défaut. Nous l'avons vérifié avec le validateur de KoSIT. Dans Odoo 17 et 18, l'arrondi par ligne est la valeur par défaut.
Avec un paiement partiel, BR-CO-16 échoue : le montant à payer (BT-115) correspond à ce qui reste ouvert dans Odoo, mais le fichier ne porte pas le montant déjà réglé (BT-113).

Dans Odoo 18, 19 et 20, avec l'abonnement FlexigoTech Cloud, chaque XRechnung et chaque ZUGFeRD EN 16931 générés par le module passent par les règles de KoSIT, qui incluent ces six règles. Si l'une échoue, elle est inscrite sur la facture, dans Validation Errors, et la facture n'est pas marquée Ready to send. Dans Odoo 17, la version publiée ne valide pas : le fichier part avec l'erreur et sans avertissement.
Que faire
- Regardez dans le rapport quelle règle échoue en premier : BR-S-08 pointe vers les bases, BR-CO-14 vers le détail, BR-CO-15 vers le total et BR-CO-17 vers la TVA d'un taux.
- Vérifiez la méthode d'arrondi de la société et, si vous émettez des factures électroniques avec beaucoup de lignes, discutez avec votre expert-comptable de l'arrondi par taxe.
- BR-CO-16 apparaît avec les paiements partiels ou l'arrondi de caisse : le montant payé et le montant d'arrondi doivent figurer dans le fichier. Avec notre module, générez la XRechnung avant d'enregistrer le moindre paiement.
- Corrigez et générez de nouveau ; ne modifiez pas le XML à la main.
À lire aussi
Vérifié dans le code du module (version 17.0 et 20.0) et dans les sources citées
Sources
- github.com/ConnectingEurope/eInvoicing-EN16931/releases/tag/validation-1.3.17
- Code du module : flexigo_germany_xrechnung 20.0 (446f2f68): lib/generators/ubl_generator.py
- Code du module : flexigo_germany_xrechnung 20.0 (446f2f68): lib/generators/cii_generator.py
- Code du module : flexigo_germany_xrechnung 20.0 (446f2f68): models/res_company.py
- github.com/odoo/odoo/blob/6f00fdadb875342de2d14a179f25431f93cd63c9/addons/account/models/c
- github.com/odoo/odoo/blob/2982b5f44af03fae6a9888f20632c1cc19f623ba/addons/account/models/c
- github.com/odoo/odoo/blob/4d2d4b5895db70596863df941d9a1f0beeb117f8/addons/account/models/c
- github.com/odoo/odoo/blob/2fec20391a8a61ea3a3bf15e9f24d59dba910a8d/addons/account/models/c
- github.com/odoo/odoo/blob/2982b5f44af03fae6a9888f20632c1cc19f623ba/addons/account_edi_ubl_
- github.com/odoo/odoo/blob/4d2d4b5895db70596863df941d9a1f0beeb117f8/addons/account_edi_ubl_
- github.com/odoo/odoo/blob/2fec20391a8a61ea3a3bf15e9f24d59dba910a8d/addons/account_edi_ubl_
- Code du module : flexigo_germany_xrechnung 20.0 (446f2f68): models/xrechnung_cloud.py
- Code du module : flexigo_germany_xrechnung 20.0 (446f2f68): models/account_move.py
- Code du module : flexigo_germany_xrechnung 17.0 (ad05de8f): __manifest__.py
