Skip to content

BT-8 derived from the first invoice line — proposal: company-level rule + declarative, dated option field #67

Description

@risto42

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/B95,
    S1/S872), 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions