
Recurring accounting errors when configuring Odoo in Egypt
Why do an Egyptian company's books drift even on Odoo? Recurring errors trace to settings in taxes, journals, inventory valuation and lock dates, as Odoo 19.0 documents them.
Executive summary
- Many accounting errors that recur in practice with Odoo are configuration errors, not usage errors, each traceable to a specific field or settings path, as Odoo 19.0 documents it (odoo.com/documentation/19.0).
- Withholding tax in Odoo is a negative tax with its own account, Tax Grids and tax group, not a manual deduction kept outside the tax setup.
- The Suspense Account on a bank journal holds every transaction not yet reconciled; a growing balance is late reconciliation, not a real balance.
- The inventory valuation method (Perpetual or Periodic) is independent of the accounting standard (Anglo-Saxon or Continental), and it decides when cost reaches the income statement.
- One lock date is not enough: Lock Everything admits exceptions, Lock Tax Return separately covers tax entries, and Hard Lock is irreversible.
- An analytic distribution model with no account prefixes can put balance-sheet lines on a cost centre; restricting the prefixes to income and expense prevents it.
Taxes: designing the tax before it posts
Every tax has a Tax Type (Sales, Purchase or None), and it is the type, not the tax's name, that decides where the tax can be selected. Applying a tax automatically adds Tax Grids to the invoice line and the tax journal item, and the Tax Return report is built from these tags, so a tax without the right grid shows on the invoice but not on its line of the return.
Withholding tax is defined in Odoo as a negative tax, not a manual deduction: under Accounting ‣ Configuration ‣ Taxes the amount is entered as negative, and a retention Tax Group is created on the Advanced Options tab. In 19.0 the Egyptian localisation package (l10n_eg) creates these taxes ready-made; Odoo 20 (September 2026) reorganises Egyptian withholding into two flows, Deducted Withholding and Gross Withholding, so the setup must be reviewed on upgrade. Withholding itself is covered in the withholding tax and Form 41 guide.
Journals, the suspense account and bank reconciliation
A sales journal has a Default Income Account and a purchase journal a Default Expense Account: the account an invoice posts to unless one is set on the product category or the product. A wrong default can run through many invoices unnoticed, because nothing about it looks wrong.
Of the Suspense Account on bank, cash and credit card journals, the documentation says: "transactions on this journal are posted on this account until they are reconciled, at which point this account is replaced with the account the transaction was reconciled against". Its balance at any moment is therefore what remains unreconciled. Payments create no journal entry unless Outstanding Receipts/Payments accounts are set; if the journal's main bank account is used as the outstanding account, the invoice or bill is marked Paid as soon as the payment is registered, before the bank movement arrives. Automated reconciliation models apply in sequence: where a transaction matches several, only the first applies, so a model out of order posts the amount to the wrong account automatically.
Inventory valuation: the costing method and the accounting standard
Under Accounting ‣ Configuration ‣ Settings, the Inventory Valuation section sets at company level, overridable on the product category, the Inventory Valuation (Perpetual (at invoicing) or Periodic (at closing)) and the Inventory Cost Method (FIFO, AVCO or Standard Price). The method is independent of the Anglo-Saxon or Continental standard: Periodic usually goes with Continental and Perpetual with Anglo-Saxon, but Odoo allows otherwise, and it is the method that decides when cost reaches the income statement: on posting the vendor bill under Periodic, the customer invoice under Perpetual.
Here the two versions differ. In 18.0 the choice sat on the product category (Manual, or Automated once Automatic Accounting was enabled), and automated Anglo-Saxon accounting relied on two separate interim accounts, Stock Input and Stock Output. In 19.0 they are no longer used; their place is taken by a new kind of interim account, Variation, whose account type depends on the combination, beside the Valuation account for the asset. A database upgraded without moving their remaining balance into Valuation by journal entry, before or after the upgrade, does not keep accurate inventory values on the balance sheet.
Lock dates and documents that stay in draft
Among the lock dates under Accounting ‣ Accounting ‣ Lock Dates, three have quite different effects:
| Date | What it blocks | Exception |
|---|---|---|
| Lock Everything | Changes to posted entries dated up to it, and posting new entries on those dates (their date moves to the day after) | Created by a user with Administrator rights, for that user or for everyone, for a set duration; logged in the company record's chatter |
| Lock Tax Return | Tax entries only; a new transaction dated before it has its tax values moved to the next open period | The same mechanism, required before a tax return entry can be reset to draft |
| Hard Lock | Entries up to its date | None; it is irreversible |
Draft is a deliberate control, not an oversight. Odoo's checks, depending on the localisation, include No draft entries at the annual closing and Draft entries at the tax return review; both ask for every draft in the period to be reviewed and posted, or redated, before sign-off. Undoing a posting is harder: Secure Posted Entries with Hash restricts alteration of a journal's entries and cannot be removed once the journal holds a posted entry; once a tax return is posted, its period is closed to new tax entries and corrections to invoices go into the following period, and the documented ways to correct the return entry itself are Reverse Entry, or Reset to Draft after the Lock Tax Return date is removed through an exception. Early posting trades a simple correction for a formal, logged reversal.
Analytic distribution and currency differences
An Analytic Plan can be made Mandatory, and an Analytic Distribution Model applies automatically; both are scoped by account prefix: Financial Accounts Prefixes on the plan's Applicability tab, Accounts Prefixes on the model. Leave the field blank, or wide enough to take in receivables, the bank or stock, and balance-sheet lines in manual entries can pick up a meaningless cost centre, mixing financing movements into the analytic reports.
Realised exchange differences post automatically on reconciliation, to the journal and the gain and loss accounts set under Accounting ‣ Configuration ‣ Settings, Default Accounts section. Unrealised differences on unsettled balances, such as a foreign-currency bank account or receivable at a historic rate, appear in the Unrealized Currencies report (Accounting ‣ Review) and reach the balance sheet only when its Adjustment Entry is posted, which reverses automatically on the Reversal Date. Relying on realised differences alone, without this report at each close, leaves foreign-currency balances at a stale rate.
Egyptian e-invoicing: configuring the connection
The integration rests on two modules: l10n_eg (the localisation package) and l10n_eg_edi_eta (the connection to the ETA's system). The connection is set under Accounting ‣ Configuration ‣ Settings ‣ ETA E-Invoicing Settings by entering the ETA Client ID and ETA Secret, the credentials the ETA issues when the system is registered on its portal; the preproduction (test) environment is separate from production, with different credentials to be updated in Odoo when moving. Each branch needs a Company-type contact with its address and Tax ID filled in, and its own sales journal with its ETA Activity Code and ETA Branch ID. Every product has an ETA Item code field under the Accounting tab, used when the barcode does not match the product's ETA item code; the mandate and its phases are covered in the e-invoicing guide.
What this requires
- Check the Tax Type and Tax Grids on every tax, set withholding up as a negative tax rather than a manual deduction, and review it on upgrading to version 20.
- Keep the Suspense Account close to zero through monthly reconciliation, and check the order of the automated reconciliation models.
- Choose the inventory valuation method deliberately, not by default, and move the Stock Input and Stock Output balances into Valuation when upgrading to 19.0.
- Set Lock Everything and Lock Tax Return every period in the system, not by verbal instruction, and restrict the exception right to those who need it.
- Scope the account prefixes on analytic plans and distribution models to income and expense accounts, and run the Unrealized Currencies report at every close.
- Register each branch with its own journal and ETA data, and update the credentials when moving from the test environment to production.
The firm's ERP Consulting Department reviews the tax, journal, inventory valuation and analytic distribution setup in Odoo, and checks the e-invoicing connection before go-live.
Mahmoud Nassef — Chartered Accountant (Egyptian Register), Founder Partner
Member, Egyptian Society of Accountants & Auditors
Member, Egyptian Tax Society
Member, Egyptian Society for Public Finance and Taxation
Partner profile · Book a consultation
Disclaimer: This bulletin is prepared for general information on the legislation in force at the date of its publication. It does not constitute a professional opinion or tax or legal advice on any particular matter, and it should not be relied upon in place of advice based on an examination of the circumstances of each case. Nassef & Partners International accepts no responsibility for any action taken, or refrained from, in reliance on its contents. The positions stated remain subject to subsequent legislation and decisions.
