Skip to main content

GCC E-Invoicing | Core Engine

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 (Community or Enterprise) Price: $499 (one-time) License: Up to 3 domain activations 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, or 19.0 (Community or Enterprise)
Odoo dependenciesaccount, account_edi, account_edi_ubl_cii, certificate, mail, web, ecosire_license_client
Python packagescryptography, lxml, qrcode
Environment variableECOSIRE_EINVOICE_MASTER_KEY (required — see below)
LicenseAn active ECOSIRE license for this module
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.
  7. Enter your ECOSIRE license key when prompted.

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

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 v1.1 roadmap and is not included in this 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