Skip to content

E-Invoicing Integrations

The einvoice module turns a posted customer invoice into a standardised EN 16931 / Peppol BIS Billing 3.0 UBL document, validates it against the core business rules, and transmits it through a certified Access Point provider. This page documents the data contract a consultant configures and the provider seam a third party implements to add a new Access Point — the same shape as the payment and shipping provider hooks.

For a FinancialDocument of type Customer Invoice, Credit Note, or Debit Note, the module generates UBL 2.1 XML carrying the Peppol BIS Billing 3.0 customization. The tax breakdown is derived from the invoice lines — one cac:TaxSubtotal per VAT category + rate — even though the document stores only a single rolled-up tax total.

The invoice form gains an E-Invoicing tab (status, the generated XML, any validation errors) and three actions:

ActionMethodEffect
Validate E-Invoiceaction_validate_einvoiceCheck the invoice against the core EN 16931 rules; surfaces what to fix.
Generate E-Invoiceaction_generate_einvoiceValidate, then build and store the Peppol BIS UBL.
Send E-Invoiceaction_send_einvoiceGenerate if needed, then transmit through the configured Access Point.

The document is only transmittable when both parties are addressable on the network and the tax data is complete:

FieldOnMeaning
peppol_endpoint_id + peppol_schemeCompany, ContactThe party’s electronic address on the Peppol network (usually the VAT number or a GLN) and its EAS scheme code (e.g. 9930 = DE VAT, 0088 = GLN).
tax_numberCompany, ContactThe VAT identifier (already part of core). Required on the seller when VAT is charged.
vat_categoryTaxThe EN 16931 VAT category (Standard Rate / Zero Rated / Exempt / Reverse Charge / …). Maps to the UNCL5305 code (S/Z/edit/AE/…) on the wire.
exemption_reasonTaxRequired by EN 16931 for any non-standard category.

action_validate_einvoice reports missing or inconsistent values in these terms (e.g. “Buyer Peppol endpoint ID / scheme is missing”) so they can be fixed before sending.

Peppol uses a 4-corner model: your install is Corner 1 (the sender); the Access Point provider is Corner 2 and handles the AS4 transport plus SML/SMP discovery to reach the recipient’s Access Point (Corner 3) and the recipient (Corner 4). You never operate an Access Point yourself — you connect to a certified provider’s API.

A provider is a module that extends Integration (with integration_category: E-Invoicing in its manifest so it appears in the Integrations catalog) and overrides the transmission hooks, guarding on its own provider check and delegating to super() for the rest — exactly the payment/shipping pattern:

class MyApProvider(Model):
__inherit__ = "Integration"
async def send_einvoice(self, document, xml_bytes, doc_format):
if not await self._is_my_ap():
return await super().send_einvoice(document, xml_bytes, doc_format)
# ... POST xml_bytes to the Access Point's API ...
return {"message_id": "<ap-ref>", "state": "Sent"} # or "Delivered" / "Rejected"

The hooks a provider may implement (the base raises NotImplementedError, so an invoice can always be generated and validated locally even with no provider connected):

HookPurposeReturns
send_einvoice(document, xml_bytes, doc_format)Transmit a generated e-invoice (Corner 2).{"message_id", "state"}
fetch_einvoice_status(message_id)Poll delivery status of a sent document.{"state", "detail"}
validate_einvoice(xml_bytes, doc_format)Optional provider-side Schematron validation (the full ~200-rule EN 16931 set).{"valid", "errors": [...]}
receive_einvoices()Inbound (Corner 4): fetch e-invoices delivered to you.[{"message_id", "xml", "sender", "received_at"}]

The enabled provider is resolved with Integration.resolve_einvoice_provider() (the first enabled E-Invoicing integration).

Reference provider — einvoice_storecove. Storecove is a certified Peppol Access Point with a REST API. The module submits the generated EN 16931 UBL/CII (base64, rawDocumentData) to POST /document_submissions; Storecove parses it, converts to the recipient country’s national format where required, and transmits — so one integration reaches the whole Peppol network plus the national systems it covers. Configure it in the Integrations catalog with a Storecove API key and the sending company’s LegalEntity id (both from the Storecove dashboard; use a sandbox key to test). Inbound uses Storecove’s pull-mode webhook queue.

Reference provider — einvoice_sovos. Sovos is a global compliance network for the clearance-model countries (Brazil, Saudi Arabia, Mexico, India) as well as Peppol. Unlike an Access Point, Sovos does the country conversion, cryptographic signing, and government clearance itself — you submit a Sovos Canonical Invoice (SCI), which is UBL 2.1 wrapped in a Standard Business Document (SBD) envelope. The module therefore reuses the same generated UBL and wraps it (the SBDH carries the country + business process Sovos routes on), submitting base64 to POST /v1/documents with OAuth (API key + secret). Configure it with the Sovos key/secret and a sandbox toggle. Note: the plain EN 16931 UBL clears in UBL-accepting jurisdictions; the hardest clearance countries (e.g. Brazil) require country-specific SCI content beyond the core UBL, which is added per country on top of this provider.

A profile selects the standard/national flavour of the generated document. The base ships Peppol BIS Billing 3.0; a country module adds its own profile by extending FinancialDocument — contributing the profile name to the einvoice_profile selection and overriding two hooks (guarding on the profile value, delegating to super() for the rest):

class XRechnungDocument(Model):
__inherit__ = "FinancialDocument"
_selection_add = {"einvoice_profile": ["XRechnung 3.0"]}
async def _einvoice_customization_id(self):
if self.einvoice_profile == "XRechnung 3.0":
return "urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_3.0"
return await super()._einvoice_customization_id()
async def _einvoice_profile_validation(self):
errors = await super()._einvoice_profile_validation()
if self.einvoice_profile == "XRechnung 3.0":
# ... append the national (BR-DE) rule violations ...
return errors
  • _einvoice_customization_id() sets the UBL cbc:CustomizationID for the profile.
  • _einvoice_profile_validation() returns the profile’s national rule violations, which are checked alongside the core EN 16931 set on validate/generate.

The user picks the profile per invoice (E-Invoicing tab). Two reference profiles ship:

  • l10n_de_einvoice — XRechnung 3.0. Same Peppol BIS UBL syntax, a German customization, and the BR-DE rules (Leitweg-ID buyer reference, seller contact point, IBAN payment means). It only overrides _einvoice_customization_id + _einvoice_profile_validation.

  • l10n_fr_einvoice — Factur-X. France’s format is CII (UN/CEFACT Cross Industry Invoice), not UBL, and is delivered as a hybrid PDF (the CII embedded in the invoice PDF). It overrides _einvoice_render to build CII instead of UBL, and adds a Download Factur-X PDF action that renders the invoice report and embeds the CII. Note: the module produces the CII + embeds it with the Factur-X XMP, but strict PDF/A-3 conformance must be validated with an external validator (veraPDF / FNFE-MPE) before production — it is not asserted in-process.

  • l10n_sa_einvoice — ZATCA (Saudi Arabia). ZATCA is UBL 2.1, so it reuses build_ubl and only injects the KSA content (Commercial Registration Number as a supplier PartyIdentification, and the invoice-type-code subtype in the name attribute) via a third hook, _einvoice_augment_ubl(root) — which lets a profile mutate the built UBL in place (applies whether the UBL is stored, sent via an Access Point, or handed to Sovos). BR-SA rules check the 15-digit 3…3 VAT and the CRN. The cryptographic stamp, ICV/PIH hash chain, QR, and ZATCA clearance are all performed by the transmission provider (Sovos) — the profile never touches the cryptography.

The base emits seller contact and SEPA payment means (from the company’s iban/bic) whenever present, so those national requirements are satisfiable without profile-specific builder code.

  • l10n_in_einvoice — India GST (IRP). Adds the India-mandatory content via _einvoice_augment_ubl — GSTIN as the GST tax scheme, per-line HSN/SAC codes (from a new hsn_code on the product), and place of supply (the buyer’s state as CountrySubentityCode). BR-IN validation checks the 15-char GSTIN, HSN on every line, and the place of supply. The provider registers the invoice with the IRP and returns the IRN + signed QR, and derives the CGST/SGST/IGST split from the rate and place of supply.

  • l10n_mx_einvoice — Mexico CFDI 4.0. UBL-based (like Saudi): injects per-line SAT product (ClaveProdServ) and unit (ClaveUnidad) codes and the payment FormaPago (as PaymentMeansCode); RFC travels via the SBDH. The provider maps to CFDI, applies the PAC stamp (timbrado), obtains the SAT UUID and clears. So a national profile has three hooks to override: _einvoice_customization_id (the CustomizationID), _einvoice_render (a different syntax, e.g. CII), and _einvoice_augment_ubl (inject/adjust UBL elements). Clearance-model countries add their content on the same base and transmit through a provider that performs the signing and government clearance.