Skip to main content

GCC E-Invoicing | Core Engine

Technical nameecosire_einvoice_core
Odoo versions20.0, 19.0, 18.0, 17.0 (Community or Enterprise)
Current shipped version20.0 → 20.0.1.4.2, 19.0 → 19.0.1.4.2, 18.0 → 18.0.1.3.3, 17.0 → 17.0.1.3.3
Price$499 USD — one-time, per Odoo version
Odoo module licenceOPL-1
CategoryAccounting/Localizations/EDI

The ECOSIRE GCC E-Invoicing Core Engine is a country-agnostic e-invoicing platform for Odoo. It does the hard, country-neutral work — building compliant UBL 2.1 invoice XML, signing it with XAdES-BES, generating TLV QR codes, orchestrating submission to a tax authority with retries, and keeping a tamper-evident audit trail — while a country pack (such as GCC E-Invoicing | ZATCA Phase 2 — Saudi Arabia) plugs in the rules, endpoints, and validators for a specific country.

You install the core engine plus at least one country pack. The engine on its own generates the cryptographic groundwork (CSR, encrypted keys) and the document lifecycle, but it cannot clear or report invoices to an authority until a country pack is installed.

Compatibility: Odoo 17 / 18 / 19 / 20 (Community or Enterprise) Price: $499 USD (one-time) License: OPL-1 on every series. No licence key, no activation step and no companion licensing module: the engine runs as soon as it is installed. Category: Accounting / Localizations / EDI

Live demo

Try the suite (engine + KSA pack) at zatca.demo.ecosire.com — log in with admin / admin.

What it does​

  • UBL 2.1 invoice builder — produces standards-compliant Universal Business Language 2.1 XML, with plug-in extension points for country packs.
  • XAdES-BES digital signer — applies an enveloped XAdES-BES signature using an ECDSA secp256r1 key, byte-verified against the ZATCA SDK reference output.
  • TLV QR generator — builds tag-length-value QR codes parametrically per country.
  • Submission orchestrator — a scheduled job submits pending documents, retries transient failures with an exponential-backoff ladder, is idempotent (never double-submits a cleared document), and moves exhausted documents to a dead-letter state for manual review.
  • Hash-chained immutable audit log — every lifecycle event is recorded in an append-only, hash-chained table protected by PostgreSQL triggers that reject any UPDATE or DELETE at the database level.
  • CSID / certificate onboarding wizard — a 7-step wizard that generates the CSR, captures the authority OTP, and stores the resulting certificates.
  • OWL dashboard — KPI cards, a 30-day clearance trend (Chart.js), and a one-click retry list for failed documents.
  • Fernet-encrypted credential storage — private keys are encrypted at rest with a master key supplied via an environment variable (never stored in the database or in git).

Requirements​

RequirementDetail
Odoo17.0, 18.0, 19.0 or 20.0 (Community or Enterprise)
Odoo dependenciesaccount, account_edi, account_edi_ubl_cii, mail, web, plus certificate on 18.0, 19.0 and 20.0 (Odoo 17 has no such module and the engine does not need it)
Python packagescryptography, lxml, qrcode
Environment variableECOSIRE_EINVOICE_MASTER_KEY (required — see below)
LicenceOPL-1. No licence key or activation is required
Country packAt least one pack (e.g. ecosire_einvoice_ksa) to clear/report invoices

Install the Python packages into the same environment that runs Odoo:

pip install cryptography lxml qrcode

Required: the master encryption key​

The engine refuses to handle private keys unless the ECOSIRE_EINVOICE_MASTER_KEY environment variable is set. This is a deliberate fail-fast design — there is no silent fallback key and no plaintext passthrough. CSID private keys are encrypted with Fernet (AES-128-CBC + HMAC) using a key derived from this variable, and the variable is never stored in the database or committed to git.

Generate a strong key once and keep it safe. A valid 44-character Fernet key is accepted as-is; any other string is treated as a passphrase and a key is derived from it deterministically (SHA-256). The simplest approach is to generate a real Fernet key:

python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
# example output: 8Jx7s0Qe1m4mB2pT-vWqz3rYy5n9KkLp6cD8fGhJ0M=

Then set it for the Odoo process and restart Odoo. On a systemd-managed install:

# /etc/systemd/system/odoo.service (under [Service])
Environment="ECOSIRE_EINVOICE_MASTER_KEY=8Jx7s0Qe1m4mB2pT-vWqz3rYy5n9KkLp6cD8fGhJ0M="
sudo systemctl daemon-reload
sudo systemctl restart odoo

Or, when launching Odoo directly:

export ECOSIRE_EINVOICE_MASTER_KEY="8Jx7s0Qe1m4mB2pT-vWqz3rYy5n9KkLp6cD8fGhJ0M="
./odoo-bin -c /etc/odoo/odoo.conf
Back up the master key

If you lose ECOSIRE_EINVOICE_MASTER_KEY, previously encrypted CSID private keys can no longer be decrypted and you will have to re-onboard your certificates. Store it in your secrets manager, not in the database or repository. If you change it, existing encrypted keys become unreadable.

Installation​

  1. Download the module ZIP from your ECOSIRE Dashboard.
  2. Extract it (and your country pack) to your Odoo addons directory:
    unzip ecosire-einvoice-core-*.zip -d /opt/odoo/addons/
  3. Install the Python dependencies (pip install cryptography lxml qrcode).
  4. Set ECOSIRE_EINVOICE_MASTER_KEY (see above) and restart Odoo:
    sudo systemctl restart odoo
  5. In Odoo go to Apps, click Update Apps List.
  6. Search for ECOSIRE GCC E-Invoicing | Core Engine and click Install, then install your country pack. There is no licence key to enter.

After installation a new GCC E-Invoicing menu appears under Accounting (or Invoicing).

The engine adds a GCC E-Invoicing root menu under Accounting → Accounting with these entries:

MenuPathPurpose
DocumentsAccounting → GCC E-Invoicing → DocumentsThe e-invoice document list (one per signed invoice)
CSIDsAccounting → GCC E-Invoicing → CSIDsCertificate (CSID) records and their status
EGS DevicesAccounting → GCC E-Invoicing → EGS DevicesE-invoice generation units (per branch / cashier)
Audit LogAccounting → GCC E-Invoicing → Audit LogThe immutable, hash-chained event trail

Country packs add their own submenu under the same root (for example Saudi Arabia for the KSA pack).

Certificate (CSID) onboarding​

Certificates are onboarded through a 7-step wizard. The country-neutral steps (1–2) run with the engine alone; the authority-facing steps (4–6) require an installed country pack. If no pack is installed, the wizard still generates the CSR and the encrypted private key and shows a clear "install the country pack" message rather than failing.

  1. EGS Device — organisation name, VAT, commercial registration (CRN), branch, and device serial. You can pick an existing EGS device or have one created from these fields.
  2. Generate CSR — the engine generates an ECDSA secp256r1 key pair and an X.509 certificate signing request (CSR). The country pack enriches the CSR with authority-specific extensions when present.
  3. Enter OTP — paste the one-time password from the tax-authority portal.
  4. Compliance CSID — the country pack requests the compliance certificate.
  5. Sandbox Self-Test — runs the pack's compliance self-test.
  6. Promote to Production — requests the production certificate.
  7. Done — confirmation; you can now sign live invoices.

Throughout, the private key is held transiently only between steps 2 and 4, then immediately moved into a Fernet-encrypted attachment and cleared from the wizard.

A daily CSID Renewal Check scheduled job flags any active production certificate expiring within 30 days so you can renew before it lapses.

Daily usage​

The document lifecycle​

Each invoice that needs e-invoicing produces an einvoice.document record. Documents move through these states:

StateMeaning
DraftCreated, not yet signed
SignedXML built and XAdES-BES signature applied
SubmittedSent to the tax authority, awaiting outcome
ClearedAuthority cleared a Standard (B2B) invoice
ReportedAuthority accepted a Simplified (B2C) report
Pending RetryTransient failure — will be retried automatically
Signing Failed (retry)Signing failed — retryable
RejectedAuthority rejected the document
Dead LetterRetries exhausted — needs manual attention

How invoices get cleared or reported​

  • The Submit Pending Documents scheduled job runs every 5 minutes, submitting signed documents and advancing retries automatically.
  • You can also drive a single document by hand from Accounting → GCC E-Invoicing → Documents, opening the record and using the toolbar buttons:
    • Sign — build and sign the XML.
    • Submit Now — submit immediately instead of waiting for the cron.
    • Retry Now — re-attempt a failed or pending document on demand.

The document form's tabs expose the Errors (error code, message, next retry time), XML / QR (XML hash, QR payload, QR image), and Submission Attempts history for full traceability.

The dashboard​

The OWL dashboard surfaces, for your active companies (simulation documents excluded):

  • Clearance rate today — share of today's submitted documents that reached a success state.
  • Pending retry and dead-letter counts.
  • Minimum CSID expiry days — how soon your earliest-expiring active certificate lapses.
  • 30-day trend — a dual-line Chart.js graph of daily successes vs failures.
  • Failed documents — a one-click actionable list of the most recent rejected, dead-letter, pending-retry, and signing-failed documents.

Handling failed documents​

  1. Open the dashboard and review the Failed documents list, or filter Documents by state (Rejected / Dead Letter / Pending Retry).
  2. Open a document and read the Errors tab — the error code and message come straight from the authority response.
  3. Fix the underlying cause (often a missing field on the invoice or partner, or an expired certificate).
  4. Click Retry Now. Transient failures (network / 5xx / rate limiting) are retried automatically; permanent rejections need the data corrected first.

The audit log​

Every lifecycle event is written to an append-only, hash-chained audit table at Accounting → GCC E-Invoicing → Audit Log:

  • Each row stores a hash of the (sanitised) payload, the previous row's hash, and a row hash chaining them together — so any tampering breaks the chain.
  • Two PostgreSQL triggers reject any UPDATE or DELETE against the table, making it genuinely immutable at the database level, not just by convention.
  • A daily Audit Chain Integrity Check job walks the chain and alerts on the first mismatch — a second line of defence that catches even a superuser who bypassed the triggers.
  • Payloads are PII-sanitised before they are written: card numbers, long ID numbers, private keys, OTPs, tokens, and the master key are redacted.

Retention defaults to the authority-mandated period (6 years for ZATCA) and is configurable per company.

Environment modes​

The engine works against three environments, selectable during onboarding:

ModeUse
SandboxDeveloper-portal testing
SimulationPre-production rehearsal that mirrors production behaviour (excluded from dashboard KPIs)
ProductionLive clearance and reporting

What's new​

19.0.1.4.2, 20.0.1.4.2, 18.0.1.3.3 and 17.0.1.3.3​

  • The compliance dashboard renders on every series. On 19.0 and 20.0 the first-paint loading block used an expression the OWL template compiler cannot compile, so the dashboard showed Odoo's client-error dialog in every mode and every screen size. On 17.0 the component asked for the realtime bus in a way that fails on that series, so the action never mounted. The dashboard now opens with its four KPI cards, the 30-day submission trend and the failed-document list on 17.0, 18.0, 19.0 and 20.0.
  • Document references read as invoices. The audit log's Document column — and any other reference to an e-invoice document — now shows the linked invoice number (for example INV/2026/0001) instead of the raw technical key einvoice.document,<id>.
  • The E-Invoicing menu requires the E-Invoicing User group. Every model behind the app is group-scoped, so an unrestricted menu produced a working-looking application whose screens answered with an access error; the dashboard now also explains the condition in plain language rather than showing "Odoo Server Error".
  • Dashboard secondary text meets WCAG 2.2 AA contrast (4.31:1 → 5.52:1 on the 19.0/20.0 cards, 4.29:1 → 4.96:1 on the 18.0/17.0 page background).
  • The 30-day trend chart always draws its figures. The chart could show an empty plot under its legend when the figures arrived before the chart had finished starting; it now keeps the latest figures, stays in place while the first figures load, and paints immediately.
  • The dashboard fits a phone screen. The trend card no longer runs off the right edge on narrow screens, and long error messages in the failed-documents list end in an ellipsis with the full text on hover instead of being cut mid-word.
  • Audit log column label. The hash column reads Row Hash (chain).

19.0.1.4.1 and 20.0.1.4.1​

  • Loading state on the compliance dashboard. Until the first figures arrive, the dashboard shows a status panel instead of a grid of zeros, so a slow first load no longer looks like "nothing works".
  • Currency fallback is now the company currency. When an invoice carries no currency, the UBL payload uses the company's currency instead of Saudi riyal, so a UAE, Singapore or Malaysia document is never stamped SAR.
  • Audit log and submission-attempt dates use Odoo's standard creation-date field again; only the field label changes on an existing database and no migration is needed.

19.0.1.4.0​

  • Signing and submission require the E-Invoicing User group, and the CSID onboarding wizard steps require Manager, on every action that drives the workflow.
  • The licence-client integration was removed entirely: no licence key, no activation step.

18.0.1.3.2 and 17.0.1.3.2​

  • The 18.0 and 17.0 lines now carry the complete engine: signing, the submission orchestrator, the CSID onboarding wizard, the append-only audit log and the OWL dashboard.
  • No dependency on the ECOSIRE licence client, OPL-1 licensing. Signing and submission behaviour is unchanged.

FAQ​

Do I need a country pack? Yes — the engine is country-agnostic. Install it alongside at least one pack (for example the KSA ZATCA Phase 2 pack) to actually clear or report invoices to a tax authority.

What happens if ECOSIRE_EINVOICE_MASTER_KEY is not set? The engine fails fast and refuses to handle private keys. Set the variable and restart Odoo before onboarding certificates.

Is Point of Sale (POS) supported? POS e-invoicing is on the roadmap and is not included in the current release. The current release covers account.move invoices and credit/debit notes.

Where are private keys stored? Encrypted with Fernet (using the master key from the environment variable) as an attachment linked to the CSID record — never in plaintext and never in git.

Can I run it alongside Odoo's own localisation EDI? Yes. Country packs that replace a stock localisation EDI (such as the KSA pack vs l10n_sa_edi) provide a migration wizard with a coexistence option.

Support​