← Back to Documentation
Odoo logo

Odoo Integration with UAE FTA E-Invoicing via TrustBill

Connect Odoo 15 / 16 / 17 / 18 (Community & Enterprise, Odoo Online / .sh / on-premise) to TrustBill for FTA PINT-AE e-invoicing · Version 1.0 · September 2026

Overview

This guide describes how Odoo — the open-source ERP suite — connects to TrustBill, a UAE FTA-accredited service provider (ASP), so Odoo users in the UAE can issue PINT-AE compliant e-invoices without switching ERPs.

Odoo covers the full ERP stack — Accounting, Sales, Purchase, Inventory, Manufacturing, CRM. UAE customers usually install the l10n_ae localization module for the local chart of accounts, VAT taxes and TRN handling. But Odoo does not ship native PINT-AE XML generation or a UAE-Peppol connector, so invoices posted in Odoo cannot reach the FTA network by themselves.

TrustBill fills that gap. Invoices are pulled from Odoo through the TrustBill Odoo Module (installed as a normal Odoo app), or via XML-RPC / JSON-RPC, or via Automated Actions webhooks. TrustBill translates the payload into PINT-AE XML, validates it in real time, delivers it over Peppol via a certified ASP (Tron-Stride or Storecove), and the FTA acknowledgement is written back onto the Odoo invoice as a custom field.

What this integration delivers

  • Certified TrustBill Odoo module — installed with pip-style copy into the Odoo addons/ path, then Apps → Install
  • Works with Odoo 15 LTS, 16, 17 and 18 — Community and Enterprise, self-hosted, Odoo.sh, or Odoo Online (with limitations)
  • Customer invoices (out_invoice), credit notes (out_refund) and vendor bills (in_invoice) mapped to PINT-AE document types
  • Odoo's UAE taxes (l10n_ae module) auto-mapped to FTA tax categories
  • Live FTA status written back onto account.move.x_trustbill_status and x_trustbill_ack_ref
  • Bulk migration path for historical Odoo invoices via CSV export
  • Multi-company Odoo databases handled — one company per TrustBill tenant
  • Compatible with Odoo Subscriptions (recurring), Sales Orders → Invoice, and Vendor Bills flows

Why Odoo Isn't PINT-AE Ready

Odoo Enterprise ships Peppol e-invoicing modules for several countries — Belgium, France, Poland, Spain, Netherlands, Singapore — but the UAE PINT-AE profile is not on that list as of Odoo 18. What that means concretely:

  • Odoo's built-in account_edi and account_peppol modules will not generate PINT-AE XML — they target UBL 2.1 with country-specific profiles that don't include AE
  • Odoo's Peppol Access Point service does not currently route to UAE ASPs (Tron-Stride / Storecove)
  • The UAE FTA requires accreditation of the transmitting ASP; Odoo Access Point is not accredited in the UAE as of this document's date

TrustBill provides the missing three pieces: PINT-AE XML generation, Schematron validation, and delivery over an accredited UAE ASP. Odoo continues to be the system of record for accounting and stays authoritative for the invoice number, VAT computation and customer master data.

Supported Odoo Versions & Editions

Odoo releases a new major version every October. TrustBill supports the current version and the three previous LTS releases.

Odoo versionRelease statusCommunityEnterpriseSupport level
Odoo 18Current stable (Oct 2024)YesYesFull — recommended
Odoo 17Previous stableYesYesFull
Odoo 16Older stableYesYesFull
Odoo 15LTS — Enterprise support until Oct 2027YesYesFull
Odoo 14 and olderEnd of supportYesEOLBest-effort — upgrade before UAE FTA go-live
OpenERP 7 / 8 / 9LegacyEOLEOLMigrate first — no direct connector
Odoo 18.1 / 18.2 point releasesBug-fix rollupsYesYesFull — same connector, no changes

Community vs Enterprise: both editions work — the TrustBill Odoo module has no Enterprise-only dependencies. Enterprise-only features that enhance the flow (Studio for custom fields, Subscriptions app, Documents app) are supported when present but not required.

Deployment Model Compatibility

Odoo runs in three deployment models. Not every connection method works in every model — Odoo Online is the most restrictive because it does not allow custom modules.

DeploymentCustom modules?XML-RPC / JSON-RPC?Automated Actions webhook?Recommended path
Self-hosted (Docker / Debian / source)YesYesYesTrustBill Odoo module (fastest, richest)
Odoo.sh (Odoo's own PaaS)Yes — via GitHub branchYesYesTrustBill Odoo module (push to your .sh repo)
Odoo Online (SaaS at odoo.com)No — modules blockedYes — with API keyYes — v16+ Automated Actions with HTTP requestAutomated Actions webhook + XML-RPC pull. TrustBill module cannot be installed.

Odoo Online users, please read: Odoo's SaaS platform explicitly forbids third-party module installation. The TrustBill Odoo module cannot be uploaded there. Your two options are: (1) migrate to Odoo.sh (Odoo will do the migration for free), which restores full custom-module support, or (2) use the API-only path documented below — XML-RPC pull + Automated Actions webhook + write-back via Odoo's own external API. Both deliver equivalent compliance; the API-only path just has a slightly higher setup cost.

Prerequisites

On the Odoo side

  • Odoo 15, 16, 17 or 18 — Community or Enterprise
  • The l10n_ae UAE localization module installed (Apps → search "United Arab Emirates - Accounting" → Install). This module ships the UAE chart of accounts and default VAT taxes.
  • Company set to United Arab Emirates with a valid 15-digit UAE TRN in res.company.vat (Settings → Companies → Update Info → Tax ID)
  • Customers (res.partner) populated with TRN in the vat field for B2B
  • Taxes (account.tax) configured for UAE — 5% Standard, 0% Zero-Rated, Exempt
  • Fiscal Positions for GCC and non-GCC customers (if applicable)
  • An Odoo user with API access — settings admin can enable API keys via Settings → Users → Enable API keys. Odoo 14+ supports personal API keys (recommended over shared passwords).

On the TrustBill side

  • A TrustBill SME account (one account = one TRN = one Odoo company)
  • Completed KYB (business verification) — trade licence + TRN certificate uploaded
  • Submission mode set to test or sandbox during onboarding, live for production
  • Per-tenant API credential (issued during the Sources → Add Odoo flow)

Integration Architecture

Two paths, depending on whether you can install a module in Odoo:

Push mode — TrustBill Odoo Module (self-hosted, Odoo.sh)

  1. An invoice is posted in Odoo (account.move.action_post).
  2. The TrustBill module's @api.model handler triggers on state = 'posted'.
  3. The handler serialises the invoice into TrustBill's InvoiceCreateInput and POSTs to https://api.trustbill.ae/v1/invoices with a per-tenant Bearer JWT.
  4. TrustBill stores the invoice as draft.
  5. If auto-submit is enabled, POST /v1/invoices/submit fires immediately; otherwise the user clicks Submit to FTA from the invoice's TrustBill tab.
  6. Pipeline runs: queued → transformed → validated → delivered. ASP forwards to FTA over Peppol.

Pull mode — XML-RPC + Automated Actions webhook (Odoo Online)

  1. Odoo user configures an Automated Action on account.move triggered by On Update when state becomes posted.
  2. The action's Server Action calls Odoo's HTTP Request feature (v16+) to POST to https://api.trustbill.ae/v1/webhooks/odoo/{tenant} with the invoice ID.
  3. TrustBill's webhook receiver acknowledges immediately, then fetches the full invoice + partner + lines via XML-RPC using the stored API key.
  4. The rest matches Push mode from step 4 onwards.

Inbound write-back (both modes)

  1. ASP posts an inbound webhook to TrustBill on FTA acknowledgement.
  2. TrustBill's Odoo worker calls the Odoo API to set account.move.x_trustbill_status = 'acknowledged' and x_trustbill_ack_ref = <FTA ref>.
  3. The invoice's TrustBill tab updates on the next Odoo page refresh.

Connection Methods

Six paths, ordered from easiest (module install) to most flexible (custom middleware).

1. TrustBill Odoo Module (recommended for self-hosted + Odoo.sh)

A regular Odoo module named trustbill_einvoice published under LGPL-3. Drop into your addons/ path (or add via your Odoo.sh GitHub branch), then Apps → Install.

  • Adds a TrustBill tab on account.move with Submit / Status / History
  • Adds custom fields: x_trustbill_status, x_trustbill_ack_ref, x_trustbill_reject_reason
  • Adds a Settings page for tenant credentials + tax mapping
  • Uses Odoo's built-in ir.cron for retry and status refresh — no external scheduler needed
  • Ships translation files for English + Arabic

2. XML-RPC / JSON-RPC (Odoo's canonical external API)

TrustBill pulls invoices from Odoo using the built-in xmlrpc/2/object or jsonrpc/object endpoints. Works on every Odoo deployment including Odoo Online.

  • No Odoo module required — read-only integration
  • Uses an Odoo API key (v14+) instead of a user password
  • Polls account.move filtered by state=posted and write_date >= last_seen
  • Write-back also uses XML-RPC (account.move.write) — custom fields must exist first (added via Studio on Enterprise, or via a small Odoo module on Community)

3. Automated Actions webhook (v16+, works on Odoo Online)

Configure an Automated Action in Odoo to POST an HTTP request to TrustBill on account.move state change. Odoo 16 added the HTTP Request Server Action out of the box.

  • Real-time push — no polling delay
  • Works on Odoo Online (which forbids custom modules but permits Automated Actions with Studio subscription)
  • TrustBill still needs XML-RPC credentials to fetch the full invoice body
  • Best combined with method 2 for the fetch-back leg

4. Odoo 18 REST API

Odoo 18 introduced a first-party REST API surface. TrustBill's connector uses it in preference to XML-RPC when Odoo 18+ is detected.

  • Odoo 15–17 users can install the community muk_rest module for a similar surface
  • Same auth model (API key) as XML-RPC
  • Faster and easier to debug than XML-RPC for large invoice batches

5. Zapier / Make / n8n (no-code)

TrustBill publishes Odoo connectors on Zapier, Make and self-hosted n8n. Build a "New Odoo invoice posted → Create TrustBill invoice → Submit" flow in minutes.

  • Best for orgs that already standardise on one of these platforms
  • Handles retry, error branching, and Slack/email notification on failure
  • Adds latency (Zapier polls Odoo every 1–15 minutes on paid plans)

6. CSV Export (one-off migrations)

Export invoices from Odoo (Accounting → Customers → Invoices → Actions → Export) and upload to TrustBill's bulk import endpoint.

Which method should I use?

On self-hosted / Odoo.sh, install the TrustBill Odoo Module. On Odoo Online, combine the Automated Actions webhook (push trigger) with XML-RPC or Odoo 18 REST (fetch body + write-back). Use Zapier only if your team already uses it.

Authentication (API Keys)

Odoo supports two credential models. TrustBill uses API keys (available since Odoo 14) — never user passwords.

Creating an Odoo API key

  1. Log into Odoo as the integration user (create a dedicated user TrustBill Integration for audit clarity)
  2. Click the user avatar → PreferencesAccount Security tab
  3. Click New API Key — copy the generated key immediately (Odoo shows it once)
  4. Paste it in the TrustBill Sources → Odoo wizard along with the Odoo URL, database name, and login

Connection parameters TrustBill stores

ParameterExample
Odoo URLhttps://mycompany.odoo.com or https://erp.example.ae
Database namemycompany-main-19281 (Odoo Online) or prod (self-hosted)
Login (email)trustbill@yourcompany.ae
API keyStored KMS-encrypted; never re-shown after save

Idempotency-Key derivation for Odoo

Every TrustBill write endpoint requires an Idempotency-Key. For Odoo, derive it from the account.move ID (integer, stable across renumbering):

Idempotency-Key: odoo::{database}::account.move::{id}

Automated Action retries or module ir.cron retries reuse the same key — TrustBill returns the cached response, so retries never create duplicate FTA submissions.

Odoo → PINT-AE Field Mapping

The TrustBill connector maps Odoo's account.move record (with move_type in ['out_invoice', 'out_refund', 'in_invoice']) into InvoiceCreateInput, which is then rendered into PINT-AE XML.

Header-level mapping (account.move)

Odoo fieldTrustBill fieldNotes
namenumberSequence-generated ("INV/2026/00042"); 1–64 chars
idIdempotency-Key + sellerInvoiceReferenceOdoo internal integer ID
invoice_dateissueDateISO YYYY-MM-DD
invoice_date_duedueDateOptional
currency_id.namecurrencyMust be AED; multi-currency invoices converted at currency_rate
move_typeinvoiceTypeout_invoicesale, in_invoicepurchase, out_refundcredit_note, in_refunddebit_note
company_id.vatseller.trn15-digit UAE TRN
company_id.nameseller.name
partner_id.namebuyer.nameCustomer master
partner_id.vatbuyer.trn15-digit TRN; leave empty for B2C
partner_id.country_id.codebuyer.countryISO 3166-1 alpha-2 (Odoo stores it natively)
partner_id.street, street2, city, state_id, zipbuyer.address.{line1,line2,city,state,postalCode}
reversed_entry_id.namereferencedInvoiceNumberOn out_refund — links credit note to the original invoice
narrationnotesVisible in FTA-facing XML
refbuyerReferenceCustomer PO / reference

Line-level mapping (account.move.line where display_type = 'product')

Odoo fieldTrustBill fieldNotes
namelines[].descriptionNon-empty; supports Arabic
quantitylines[].quantityMust be > 0 (positive on credit notes too — see convention below)
price_unitlines[].unitPriceVAT-exclusive by default; if partner_id.property_position is a tax-inclusive Fiscal Position, connector strips VAT
discountlines[].discountPercentOdoo stores discount as a percentage on the line
product_id.default_codelines[].sellerItemIdFalls back to product_id.id if no internal reference
product_uom_idlines[].unitOdoo UOM mapped to UN/ECE Rec 20 (Units → C62, Hours → HUR, kg → KGM, Litre → LTR)
tax_ids (many2many with account.tax)lines[].vatRate + lines[].taxCategoryMapped via the l10n_ae Tax Mapping table
analytic_distributionNot sent to FTAPreserved on the Odoo side; TrustBill ignores

Totals are computed server-side

Do not send amount_untaxed / amount_tax / amount_total. TrustBill re-computes totals from lines[] and returns them so the Odoo module can reconcile. If Odoo's amount_total differs from TrustBill's by more than ±0.01 AED (Odoo's rounding is per-line, TrustBill's is per-invoice), the connector logs a variance and blocks submission until you re-post the invoice in Odoo (Reset to Draft → Post) so its rounding refreshes.

Create Invoice (draft)

Endpoint: POST /v1/invoices

A real Odoo invoice mapped to the TrustBill payload. Odoo invoice: INV/2026/00042 — Sale of two IoT sensors to a Dubai buyer at 5% VAT.

Request body

{ "number": "INV/2026/00042", "issueDate": "2026-09-08", "dueDate": "2026-10-08", "currency": "AED", "invoiceType": "sale", "buyer": { "name": "Smart Buildings LLC", "trn": "100234512300007", "country": "AE", "email": "ap@smartbuildings.example", "address": { "line1": "Al Barsha Heights, Office 2210", "city": "Dubai", "country": "AE" } }, "seller": { "name": "Odoo Trader LLC", "trn": "100100100100013", "country": "AE" }, "lines": [ { "description": "IoT Environmental Sensor Kit", "sellerItemId": "IOT-ENV-KIT", "quantity": 2, "unit": "C62", "unitPrice": 1250.00, "vatRate": 0.05, "taxCategory": "standard_5" } ], "sellerInvoiceReference": "Odoo account.move id 4128", "notes": "Delivery: 5 working days" }

Response (201 Created) — abbreviated:

{ "id": "0d4f7e2a-....-uuid", "number": "INV/2026/00042", "status": "draft", "invoiceType": "sale", "totals": { "subtotal": 2500, "tax": 125, "total": 2625, "currency": "AED" }, "pipeline": [], "errors": [], "submissionMode": "sandbox", "source": "odoo-module" }

Credit Notes & Refunds

Odoo posts credit notes as account.move records with move_type = 'out_refund'. The reversed_entry_id field links back to the original invoice.

Odoo documentinvoiceTypeRequired extra fields
Credit Note reversing an invoice (reversed_entry_id set)credit_notereferencedInvoiceNumber = reversed_entry_id.name, reasonCode
Standalone Credit Note (no reversed_entry_id)credit_notereasonCode = OtherAdjustment
Vendor Refund (in_refund)debit_notereferencedInvoiceNumber = original vendor bill number

Sign convention: Odoo credit notes carry positive quantities and positive rates (unlike vendor-side journal entries). Send them the same way to TrustBill — the invoiceType tells the FTA that the document reverses the reference invoice; PINT-AE handles the sign in the XML.

Subscriptions & Recurring Invoices

Odoo Enterprise ships a Subscriptions module (sale_subscription) that generates recurring invoices on a schedule. The connector treats each generated child invoice as a fresh event:

  • Each recurring cycle creates a new account.move with its own id — so a fresh Idempotency-Key, a fresh TrustBill draft, a fresh FTA submission
  • The subscription order (sale.order) itself is not pushed to TrustBill — only the invoices it produces
  • Pro-rated first / last invoices flow through the same mapping; period start/end appear in the line name
  • Community edition users can install oca/contract for equivalent recurring functionality — same connector behaviour

Auto-submit tip: for subscription cycles, enable TrustBill's auto-submit on ingest. Otherwise every generated child sits as a draft in TrustBill waiting for a human to click Submit.

Submit Invoice(s) for Delivery

Creating a draft in TrustBill does not deliver the invoice to the FTA. Submission is an explicit second call.

Endpoint: POST /v1/invoices/submit

{ "ids": ["0d4f7e2a-....-uuid"] }

Odoo module UX: the TrustBill module adds a Submit to FTA button on the invoice's TrustBill tab. Auto-submit-on-ingest is the default for tenants with more than 50 invoices/month, so the button is rarely needed.

Status Tracking & Field Write-back

The TrustBill module (or the XML-RPC write path on Odoo Online) writes status changes back onto three custom fields on account.move:

TrustBill statusMeaningOdoo field written
draftCreated in TrustBill, not submittedx_trustbill_status = 'Draft'
queuedSubmitted, waiting for pipelinex_trustbill_status = 'Queued'
validatedPassed schema + Schematronx_trustbill_status = 'Validated'
deliveredSent to FTA via ASPx_trustbill_status = 'Delivered'
acknowledgedFTA confirmed receipt / validityx_trustbill_status = 'Acknowledged', x_trustbill_ack_ref = <FTA ref>
rejectedFTA or ASP rejected the invoicex_trustbill_status = 'Rejected', x_trustbill_reject_reason = <code>

Add the fields to the Invoices list view so you can filter and colour-code by TrustBill status directly in Odoo: Invoicing → Customers → Invoices → three-dot menu → Add custom filter. The TrustBill module also adds a Status History row on the invoice's TrustBill tab showing every pipeline transition with its timestamp.

Bulk Migration from Odoo

To migrate historical Odoo invoices (up to 12 months back) into TrustBill:

  1. In Odoo: Invoicing → Customers → Invoices → filter by date range → select all → Actions → Export
  2. Pick the fields matching TrustBill's CSV schema below (or use the pre-built TrustBill Migration Template shipped with the module)
  3. Run the TrustBill odoo-csv-normaliser script to remap any custom fields
  4. Upload in test mode via POST /v1/invoices/bulk
  5. Review the summary; repeat in sandbox and live

TrustBill CSV column headers

number,issueDate,dueDate,invoiceType,currency,sellerTrn,sellerName,buyerTrn,buyerName,buyerCountry,buyerAddressLine1,buyerCity,lineDescription,lineQty,lineUnit,lineUnitPrice,lineVatRate,lineTaxCategory,referencedInvoiceNumber,notes
LimitValue
Max rows per file1,000 invoices
Max file size5 MB
Partial successYes — bad rows reported without rolling back the batch

Test / Sandbox / Live Modes

Two orthogonal switches: Odoo databases (test vs prod) × TrustBill submission modes (test/sandbox/live).

Odoo side

  • Self-hosted: spin up a second PostgreSQL database (--db-filter=prod|staging) or use -d staging_2026_09
  • Odoo.sh: use the built-in Staging branch — every push creates a fresh staging database with a subdomain like staging-2b1a4.dev.odoo.com
  • Odoo Online: create a duplicate database from My Databases → Duplicate — free for 15 days

TrustBill side

TrustBill modePurposeReaches FTA?
testLocal integration testing — no ASP callNo
sandboxEnd-to-end dry run against the ASP sandboxFTA sandbox only
liveReal, legally binding e-invoicingYes — the FTA production network

Recommended pairing: Odoo Staging database + TrustBill sandbox mode during onboarding. Flip both to production together on go-live day.

l10n_ae Tax Mapping

The l10n_ae module installs a preset of UAE taxes. The connector maps them out of the box; custom-named taxes need to be mapped once in TrustBill Settings → Odoo → Tax Mapping.

account.tax recordamounttax_group_idTrustBill taxCategory
VAT 5% (Sales)5.0VAT 5%standard_5
Zero-Rated (Export)0.0Zero Ratedzero_rated_export
Zero-Rated (Healthcare)0.0Zero Ratedzero_rated_healthcare
Zero-Rated (Education)0.0Zero Ratedzero_rated_education
Exempt (Financial)0.0Exemptexempt_financial
Exempt (Residential Rent)0.0Exemptexempt_residential_rent
Reverse Charge (Designated Zone)0.0Reverse Chargereverse_charge_designated_zone
Out of Scope0.0Out of Scopeout_of_scope

The connector reads account.tax.name, amount and tax_group_id.name to guess the mapping. If your l10n has been customised (e.g. localised into Arabic), map each custom tax name in the TrustBill Settings once — the mapping survives Odoo upgrades.

UAE-Specific Scenarios

Free Zone / Designated Zone sellers

Add a boolean field x_designated_zone to res.company (the TrustBill module ships it). When true, the connector sets seller.designatedZone: true and PINT-AE emits the free-zone declaration.

Fiscal Positions for non-GCC and designated-zone customers

Odoo's Fiscal Position mechanism maps default taxes to region-specific replacements. The connector reads the resolved account.move.line.tax_ids (post-Fiscal-Position mapping), so if you configure Fiscal Positions correctly, the correct taxCategory flows through without further work.

Margin scheme (used cars, antiques, second-hand goods)

The TrustBill module adds a per-product.template boolean x_margin_scheme. When true, the connector sends lines[].marginScheme: true and uses the item's standard_price (cost) as the margin base.

Reverse charge on imports

Vendor bills where the vendor's country_id is outside GCC and the line uses the Reverse Charge tax are mapped to invoiceType: purchase + taxCategory: reverse_charge_designated_zone. PINT-AE emits the ReverseCharge markers.

B2C simplified tax invoices

Customers with no vat field populated are treated as B2C. Invoices under 10,000 AED become simplified tax invoices under FTA rules; TrustBill emits the correct PINT-AE profile automatically.

Multi-Company Odoo

Odoo natively supports multiple companies in one database, each with its own TRN, chart of accounts and invoice sequences. Each Odoo company = one TrustBill tenant.

  • Configure one TrustBill Sources entry per Odoo company, keyed on company_id
  • The module reads the invoice's company_id and routes to the matching TrustBill tenant credential
  • Accountants managing many client Odoo instances install the TrustBill module once per instance
  • The TrustBill Accounting Firm account type surfaces every client tenant on a single dashboard

Invoice numbering across companies: Odoo's default sequence is per-company, so INV/2026/00042 in Company A and Company B are two different invoices with the same number. TrustBill's uniqueness constraint is per-tenant + submission mode, so they don't collide as long as you use one tenant per company.

Error Handling

TrustBill errors follow the shape { code, humanMessage, meta? }. The Odoo module surfaces the humanMessage on the invoice's TrustBill tab and writes the code to x_trustbill_reject_reason.

HTTPTypical codeOdoo root causeAction
400invalid_bodyPartner missing vat, or line has no tax_idsFix the partner / line in Odoo; module auto-resubmits on next state change
400totals_mismatchOdoo per-line rounding vs TrustBill per-invoice; typical with tax-inclusive Fiscal PositionsReset invoice to Draft → Post again in Odoo — this refreshes rounding
400unknown_taxCustom-named account.tax not in the connector's mapMap the tax in TrustBill Settings → Odoo → Tax Mapping
400l10n_ae_missingl10n_ae module not installed on the Odoo companyApps → search "United Arab Emirates - Accounting" → Install
401odoo_api_key_invalidAPI key revoked, or user disabledRegenerate the API key in Odoo Preferences → Account Security, update in TrustBill Sources
401odoo_db_not_foundDatabase name changed (common after Odoo.sh production promotion)Update the database name in TrustBill Sources
403kyb_requiredTrustBill tenant not yet approved by complianceContact TrustBill support
409invoice_mode_mismatchOdoo Production instance connected but TrustBill still in sandboxSwitch mode in TrustBill dashboard
409invoice_number_duplicateTwo Odoo companies pushed the same nameUse per-company sequences (Odoo default) + one TrustBill tenant per company
422idempotency_requiredCustom middleware missed the headerAlways use odoo::db::account.move::id

Setup Checklist

  1. Confirm Odoo version (15 / 16 / 17 / 18) and deployment (self-hosted / .sh / Online)
  2. Install l10n_ae and set the company country to United Arab Emirates
  3. Fill the TRN on res.company.vat
  4. Populate customer TRNs in res.partner.vat for B2B
  5. Create a dedicated integration user (TrustBill Integration) with Accounting → User access rights, then generate an API key from Preferences → Account Security
  6. Create your TrustBill SME account and complete KYB (trade licence + TRN certificate)
  7. Install the TrustBill Odoo module (self-hosted / .sh) OR configure the Automated Actions webhook (Odoo Online)
  8. Paste the Odoo URL, DB, login, API key into TrustBill Sources → Add Odoo
  9. Confirm the tax mapping — the connector auto-maps l10n_ae taxes; custom names need one-time mapping
  10. Enable sandbox mode in TrustBill
  11. Post a test invoice in Odoo — confirm x_trustbill_status = 'Acknowledged' within 3 minutes
  12. Reconcile Odoo's Reporting → Tax Report against TrustBill's Delivered Invoices report
  13. Switch to live mode when FTA registration is complete

Suggested test cases before go-live

  • B2B customer invoice, single line, 5% VAT — expect acknowledged
  • B2C invoice under 10,000 AED (partner with empty vat) — expect PINT-AE "simplified" profile
  • Credit note reversing an earlier invoice (out_refund with reversed_entry_id) — expect matching pair
  • Export sale using Fiscal Position "Export outside GCC" — expect zero_rated_export
  • Designated-zone customer — expect reverse-charge markers
  • Multi-line invoice with mixed VAT (5% + 0%) — expect two taxCategory values
  • Subscription cycle child invoice — expect fresh FTA submission per cycle
  • Multi-currency invoice (USD → AED via Odoo currency_rate) — expect conversion, matching totals
  • Multi-company database (Company A + Company B) — expect routing to the correct TrustBill tenant per company
  • Bulk upload of 500 historical invoices via CSV — expect partial success reported per row

Troubleshooting

SymptomLikely causeFix
Module install fails on Odoo.shModule not in the enterprise or community branch's addons pathPush the module to <repo>/addons/trustbill_einvoice then rebuild the .sh branch
Invoice posted in Odoo, nothing in TrustBillir.cron disabled, or Automated Action condition not matchingEnable the TrustBill: sync pending invoices cron; verify Automated Action trigger is On Update with state == 'posted'
Custom fields don't appear on the invoiceModule installed but the user's view rights don't include themReload the browser (Odoo caches views per user); verify the user has read access to x_trustbill_*
Invoice stuck in queuedInsufficient TrustBill credit or KYB pendingCheck the TrustBill dashboard banner; top up credit
Invoice reaches rejected with "buyer_trn_invalid"res.partner.vat is 14 digits or has spacesFix on the partner form; module auto-retries on next state change or via the Retry button
Arabic descriptions render as ?PostgreSQL database not on UTF-8 (rare on modern Odoo)Check SHOW SERVER_ENCODING; — should be UTF8; if not, dump/restore the DB with --encoding=UTF8
totals_mismatch on tax-inclusive invoicesOdoo per-line rounding vs TrustBill per-invoiceIn Odoo: Settings → Accounting → Taxes → Rounding Method = Round Globally; recompute the invoice
Automated Action webhook fails with 401TrustBill webhook URL includes tenant token that was regeneratedRegenerate the webhook URL in TrustBill Sources; paste the new URL into the Automated Action
Duplicate FTA submissions after Odoo cron restartModule's local queue lost the "already-sent" markerConfirm Idempotency-Key formula uses account.move.id — duplicates are safely deduped on TrustBill side
Status write-back not appearing on Odoo invoiceAPI key user lacks write access to account.moveGrant the integration user "Show Full Accounting Features" access group

Support & Contact

Technical Support

Escalation

For Odoo go-live escalations, please include: your TrustBill tenant ID, the Odoo version + edition (from Settings → About Odoo), the database name, the account.move.id of the failing invoice, and the timestamp of the failed pipeline run. TrustBill support responds to Odoo-tagged tickets within one business hour during UAE working days.