The bug. _prepare_bt8() (account_invoice_en16931/models/account_move.py)
first reads out_vat_on_payment, provided by l10n_fr_account_vat_return —
16.0 only, with no 17/18 migration anywhere. On 18.0 the fallback is therefore
permanent: the VAT tax of the first invoice line (the code's own comment:
"not a good solution... but how could we do better with the broken native
datamodel ?"). BT-8 thus depends on line ordering.
Reproduction (sandbox, 2026-08-16): two-line invoice, L1 service tax 20%
on_payment, L2 goods tax 20% on_invoice → XML says BT-8 = 72 while the PDF
prints "Option to pay tax on debits" (l10n_fr_account/report_invoice.xml,
any(on_invoice)). The same Factur-X contradicts itself between its human and
machine layers; swapping the two lines flips BT-8.
Why the legal fact cannot be deduced. The "option pour les débits" is a
deliberate administrative act (art. 269-2-c CGI). An opting service provider's
taxes and an ordinary goods seller's taxes are both on_invoice: "an option
was exercised" exists nowhere in the data model.
Proposal — two layers.
1. Company-level rule × option. The criterion is the company's own sale
taxes, never the invoice's lines:
| All the company's sale taxes |
no option |
option exercised |
scope = service |
72 (customary) |
5 |
scope = consu |
omit (useful? — not a case we run) |
5 |
| mixed |
omit |
5 |
- Company level, not invoice level: exigibility follows the nature of the
activity, and G1.43 reasons the same way for the option ("générale et
l'emporte sur l'ensemble des factures émises"). A per-invoice rule breaks on
works whose supplies are incorporated, on detailed product packs and on
native combo lines — all read as "mixed" while the operation stays a single
service. And "mixed" is precisely where the rule goes silent, i.e. where the
buyer most needs the information.
- Scope, not
tax_exigibility: the latter is the consequence, and is
routinely misconfigured. In a real service company every active sale tax
carries scope=service — reverse-charge and out-of-scope ones included —
while tax_exigibility is split on_invoice/on_payment.
- Nothing for goods without an option:
5 means "chargeable on invoice
date", whereas goods are chargeable on delivery (art. 269-2-a). Emitting
it without having opted asserts what the option asserts. 29 would be exact
but adds nothing to the ordinary rule. Silence keeps 5 unambiguous on
issuance: only an opting company emits it — which is what G1.43 makes
mandatory ("Le BT-8 ne sera obligatoire que si l'entreprise a opté pour la
TVA sur les débits et le spécifie au moyen du code 5 (CII) ou 3 (UBL)").
- On reception, never read
5 as "an option": the 18 CII samples of
XP Z12-012 (Annexe B) all carry BT-8 and track the nature (B1/B9 → 5,
S1/S8 → 72), none with an opting company — so goods sellers do emit 5
under the ordinary regime. Consequence: the fact "an option was exercised" is
carried nowhere in the XML, only by the PDF mention. A rendering layer
must therefore never infer that mention from a technical value, as
l10n_fr_account does today with any(on_invoice) — which prints an
"option" for any goods seller, and for a mere out-of-scope tax left at its
on_invoice default.
(l10n_fr_pdp in Odoo 18 community already computes over all lines and omits
when ambiguous, but can never emit 72 — the rule above is that one completed.)
2. Declarative company field for the option — a fact with its date: option
exercised on date (+ reference of the letter to the tax office); the rule
compares the invoice date to it, an end date covers revocation. The same field
drives the PDF mention (mandatory for 5-by-option, customary for 72, nothing
otherwise), guaranteeing PDF ↔ XML consistency by construction. Mirrored on the
invoice as a stored readonly=False compute frozen at posting.
The bug.
_prepare_bt8()(account_invoice_en16931/models/account_move.py)first reads
out_vat_on_payment, provided byl10n_fr_account_vat_return—16.0 only, with no 17/18 migration anywhere. On 18.0 the fallback is therefore
permanent: the VAT tax of the first invoice line (the code's own comment:
"not a good solution... but how could we do better with the broken native
datamodel ?"). BT-8 thus depends on line ordering.
Reproduction (sandbox, 2026-08-16): two-line invoice, L1 service tax 20%
on_payment, L2 goods tax 20%on_invoice→ XML says BT-8 = 72 while the PDFprints "Option to pay tax on debits" (
l10n_fr_account/report_invoice.xml,any(on_invoice)). The same Factur-X contradicts itself between its human andmachine layers; swapping the two lines flips BT-8.
Why the legal fact cannot be deduced. The "option pour les débits" is a
deliberate administrative act (art. 269-2-c CGI). An opting service provider's
taxes and an ordinary goods seller's taxes are both
on_invoice: "an optionwas exercised" exists nowhere in the data model.
Proposal — two layers.
1. Company-level rule × option. The criterion is the company's own sale
taxes, never the invoice's lines:
scope = servicescope = consuactivity, and G1.43 reasons the same way for the option ("générale et
l'emporte sur l'ensemble des factures émises"). A per-invoice rule breaks on
works whose supplies are incorporated, on detailed product packs and on
native
combolines — all read as "mixed" while the operation stays a singleservice. And "mixed" is precisely where the rule goes silent, i.e. where the
buyer most needs the information.
tax_exigibility: the latter is the consequence, and isroutinely misconfigured. In a real service company every active sale tax
carries
scope=service— reverse-charge and out-of-scope ones included —while
tax_exigibilityis spliton_invoice/on_payment.5means "chargeable on invoicedate", whereas goods are chargeable on delivery (art. 269-2-a). Emitting
it without having opted asserts what the option asserts.
29would be exactbut adds nothing to the ordinary rule. Silence keeps
5unambiguous onissuance: only an opting company emits it — which is what G1.43 makes
mandatory ("Le BT-8 ne sera obligatoire que si l'entreprise a opté pour la
TVA sur les débits et le spécifie au moyen du code 5 (CII) ou 3 (UBL)").
5as "an option": the 18 CII samples ofXP Z12-012 (Annexe B) all carry BT-8 and track the nature (
B1/B9→5,S1/S8→72), none with an opting company — so goods sellers do emit5under the ordinary regime. Consequence: the fact "an option was exercised" is
carried nowhere in the XML, only by the PDF mention. A rendering layer
must therefore never infer that mention from a technical value, as
l10n_fr_accountdoes today withany(on_invoice)— which prints an"option" for any goods seller, and for a mere out-of-scope tax left at its
on_invoicedefault.(
l10n_fr_pdpin Odoo 18 community already computes over all lines and omitswhen ambiguous, but can never emit 72 — the rule above is that one completed.)
2. Declarative company field for the option — a fact with its date: option
exercised on date (+ reference of the letter to the tax office); the rule
compares the invoice date to it, an end date covers revocation. The same field
drives the PDF mention (mandatory for 5-by-option, customary for 72, nothing
otherwise), guaranteeing PDF ↔ XML consistency by construction. Mirrored on the
invoice as a stored
readonly=Falsecompute frozen at posting.