Odoo · Uganda · URA EFRIS

Fiscalise Odoo invoices to URA EFRIS, automatically

Confirm the invoice in Odoo as you already do. It is submitted to EFRIS, and the fiscal document number, verification code and QR code come back onto the invoice — and onto the PDF your customer receives. Nobody re-keys anything into the URA portal.

Where your Odoo is hosted changes what is possible

Worth settling first, because it decides one thing that matters daily: whether an invoice can be stopped before it is posted, or is fiscalised just after. Both work; they do not feel the same to the person doing the invoicing.

Odoo.sh or self-hosted

A module is installed inside Odoo, so fiscalisation happens as part of confirming the invoice. If URA rejects it, the posting stops and the person raising it is told why, then and there — no document goes out unfiscalised and nobody finds out days later.

The fiscal fields sit on the invoice itself and print on the standard PDF layout, so the document your customer receives is the one they expect.

Odoo Online

Odoo's own hosted edition does not accept custom modules, so the connection is made from outside over Odoo's external API. Invoices are picked up as they are confirmed and the fiscal details written back onto them.

The trade-off is that a rejection cannot block the posting — it is reported straight away and corrected with a credit note. If that is not acceptable for how you invoice, moving to Odoo.sh is the usual answer, and it is a hosting change rather than a migration.

What comes across

EFRIS needs more than the invoice itself. These are read from Odoo and kept in step.

Customer invoices

Fiscalised to EFRIS on confirmation, with the fiscal document number, verification code and QR code returned onto the invoice and its printed layout.

Credit notes

The reversal is raised against the original invoice, which URA requires — a credit note detached from its invoice is rejected.

Products and services

Registered with URA through the API as part of the integration, carrying their unit of measure and tax treatment. Nothing is keyed in by hand.

Stock movements

For goods: opening stock and subsequent movements, which URA requires before those goods can be invoiced. Services carry no stock.

Contacts

Buyer type and TIN where the sale is business-to-business, read from the customer record, which changes what EFRIS requires on the document.

Taxes

Standard-rated, zero-rated, exempt and deemed VAT, plus excise duty where it applies — resolved from your Odoo tax records, including price-included ones.

Where Odoo and EFRIS disagree

The connection is not the difficult part — Odoo is the most open system on this list. Reconciling how the two think about a sale is, and it is where in-house attempts usually stall.

Odoo has three product types. EFRIS has two

Odoo separates storable products, consumables and services; URA separates goods from services and expects stock reported for goods. A consumable is a good as far as URA is concerned but carries no stock in Odoo, so there is nothing there to report — and it is precisely those lines that come back rejected for having no stock against them. The split has to be decided against URA’s definition, not Odoo’s.

Tax-included prices have to be unwound

Odoo will happily hold prices with the tax already inside them, which is normal for retail. EFRIS wants the unit price and the tax stated separately, and it recomputes both. Unwinding an inclusive price introduces rounding at exactly the point where EFRIS is least forgiving, so it is done deliberately rather than left to whatever the report shows.

An item valid in Odoo can still be unknown to URA

A product has to exist in the EFRIS catalogue before an invoice can mention it, and its unit of measure must come from URA’s own list rather than from Odoo’s units, which are freely defined per category. Registration happens through the API — one of the first calls an integration makes — so this is handled, not typed in somewhere.

Discounts are a separate line, not a column

Odoo puts a discount percentage on the line it reduces. EFRIS will not accept that — the discount must follow as its own negative line, flagged as the discount and carrying no quantity at all. A quantity there is rejected outright.

One database, several companies, one TIN

Odoo’s multi-company support is a strength, and it numbers invoices by journal within each company. EFRIS enforces uniqueness of the seller reference per taxpayer, across every till and every integration on that TIN, so two companies invoicing under the same TIN will collide — and the rejection reads as a duplicate submission rather than as a sequence problem. Where the companies are genuinely separate taxpayers, each needs its own EFRIS credentials and device.

Each of these is a rejection code we have already worked through. The error-code reference documents them, free to read, whether or not you ever work with us.

Two ways to do this

Most businesses want the first. Odoo partners and teams with their own Python developers usually want the second.

We set it up

We set up the environment and everything your business needs to start issuing fiscalised invoices. You keep working in Odoo exactly as before.

Talk to us
Your developers build it

Send invoices to our API as ordinary JSON and it handles the EFRIS protocol, the signing and the tax reconciliation — your module stays a few hundred lines of Odoo, not a fiscalisation project. Or licence the whole platform as source and run it on your own infrastructure.

efrisgateway.com
Questions & Answers

Odoo and EFRIS — common questions

Can Odoo connect to URA EFRIS?
Yes. Odoo has no EFRIS support of its own, but it can be connected so that invoices confirmed in Odoo are fiscalised with URA automatically and the fiscal document number, verification code and QR code come back onto the invoice and its printed layout. This works on Odoo Online, Odoo.sh and self-hosted installations, by different routes.
Does it work on Odoo Online, or do I need to self-host?
It works on Odoo Online. The difference is that Odoo Online does not accept custom modules, so the connection is made from outside over Odoo’s external API and a rejection cannot stop the invoice from being posted — it is reported immediately and corrected with a credit note. On Odoo.sh or a self-hosted installation, a module fiscalises as part of confirming the invoice, so a rejection stops the posting instead.
Which Odoo versions are supported?
The mechanism has not changed across recent Odoo releases — invoices are still account moves, taxes are still tax records, and the external API still works the same way — so current and recent versions are all workable. The version you run is confirmed at the start so the module or the API mapping is built against it rather than against a guess.
Will the QR code appear on the invoice PDF?
Yes. The fiscal document number, verification code and QR code are written back onto the invoice record and placed on the printed layout, so the PDF your customer receives carries what URA requires without anyone assembling a second document.
What happens when URA rejects an invoice?
The rejection is reported with the URA code and the specific reason, rather than a generic failure, so it can be corrected and resubmitted. Both sides of every submission are stored, which means a rejected document can be examined afterwards instead of reconstructed from memory.
Do I need to be a URA-accredited integrator to use this?
No. The fiscalisation is done using your own taxpayer credentials and device, so the invoices are issued by your business exactly as URA expects.
Other systems

Running something else?

The EFRIS side is identical whatever you run. What changes is how the invoice leaves your system, so each has its own notes.

A custom ERP, an in-house system or a POS nobody has heard of is not a problem either — if it can produce an invoice, it can be connected. Tell us what you run.