Este artículo actualmente está disponible solo en inglés. La traducción estará disponible próximamente.
By the end of this recipe you will have two (or more) legal entities trading with each other inside a single Odoo 19 Enterprise database, with sales orders in Company A automatically generating purchase orders in Company B, and inter-company journal entries posted in both ledgers without a single manual touch. Skill required: comfortable Odoo administrator who has already configured one company's chart of accounts. Time required: 2 to 3 hours for the first pair, roughly 45 minutes for each additional company once the pattern is established. Everything below has been deployed for paying ECOSIRE customers running the same configuration in production since the Odoo 17 era and updated for the v19 changes.
The trap most teams fall into is assuming "multi-company" means "multi-currency" or "multi-warehouse". It is neither. Multi-company means each company has its own chart of accounts, its own VAT registration, its own fiscal year, and its own statutory filings. The plumbing that makes invoicing flow between them is the inter-company transactions module plus a fiscal position pair, and getting that pair wrong is the single biggest source of "why are my consolidations off" tickets we receive.
What you will need
Before you start, gather these items. Skipping any of them turns a 2-hour task into a 2-day task.
- Odoo version: 19.0 Enterprise or Community with the official
account_intercompany_invoiceandinter_company_rulesmodules installed. Community editions older than 17 are missing the auto-validate flag and will require a small custom module. - User access: Settings administrator AND accounting administrator on every company involved. Multi-company permissions are not inherited.
- Pre-configured data: At least one fully working company with chart of accounts loaded, fiscal localization installed, and a working sales-to-invoice flow already proven.
- Currency setup: Each company's
res.company.currency_idmust be active inSettings > Currencies. If Company A is GBP and Company B is EUR, both currencies need exchange rate updates scheduled (we recommend the ECB feed viacurrency_rate_live). - Time: 2 to 3 hours of uninterrupted work, ideally on a staging copy of production. Multi-company is one of the few setups in Odoo where rollback by
git revertdoes not save you: it touches data, not code.
You should also have a clear answer to one business question before you touch a single setting: who pays VAT on inter-company invoices? In the EU, the answer is usually "no one, it is a reverse-charge transaction". In the US between two LLCs, it is "every state where nexus exists". Configure the fiscal positions to match.
Step-by-step
1. Create the second company
Go to Settings > Users & Companies > Companies > Create. Fill in the legal name exactly as it appears on the registration certificate, the VAT number with country prefix, and the parent company if you are running a holding structure. Click Save. Now log out and log back in: the new company appears in the company switcher in the top-right corner. Verification: switch to the new company and confirm the dashboard shows zero invoices, zero orders, and zero contacts. If you see data, you are still in the old company.
2. Install the localization on the new company
This is the step that 80 percent of self-implementation projects skip. Each company needs its own fiscal localization. Switch to the new company, go to Settings > Companies > Update Info > Configuration, and select the country. Odoo will prompt you to install the localization package (chart of accounts, taxes, fiscal positions). Click "Install Package" and wait 90 seconds. Verification: navigate to Accounting > Configuration > Chart of Accounts and confirm the account codes match the local standard (for the UK that is 4-digit codes starting with 1xxx for assets; for India it is the Tally-compatible 3-digit hierarchy).
3. Enable inter-company transactions
Go to Settings > General Settings > Companies > Inter-Company Transactions. Tick the box. A new section appears with three options:
- Generate Bills/Invoices — when Company A sells to Company B, automatically create a vendor bill in Company B.
- Generate Sales/Purchase Orders — same as above but for SO/PO documents instead of invoices.
- Validation — automatically validate the counter-document, or leave it as draft for manual review.
We recommend "Generate Sales/Purchase Orders" with manual validation in the first 30 days. After that, switch to auto-validate once you trust the data flow. Save settings.
4. Configure inter-company partners
Each company must exist as a contact in every other company's database. Switch to Company A, go to Contacts, and create a contact for Company B with the exact same VAT number you registered in step 1. Now do the same in reverse. Critical: tick "Is a Company" and "Vendor" and "Customer" on both contact records. Without those flags the inter-company rules silently skip the document.
5. Set up the fiscal position pair
This is where rebates, reverse-charges, and exempt transactions are configured. Go to Accounting > Configuration > Fiscal Positions > Create. Name it "Inter-Company UK to DE" or similar, set the country, and add tax mappings: each domestic VAT code on the source side maps to the EU reverse-charge code on the destination. Repeat in the second company with the inverse mapping. Now open the contact for Company B inside Company A and set its fiscal position to the one you just created. Verification: create a draft sales order from A to B, add a product with 20% domestic VAT, and confirm the SO line shows 0% reverse-charge VAT instead.
6. Create the inter-company chart of accounts links
In each company, designate one receivable account and one payable account specifically for inter-company transactions. Convention is account codes 1099 (receivable) and 2099 (payable) — they sit at the bottom of each section so they are easy to spot in trial balances. Set these as the default accounts on the inter-company contact records. Verification: when you post an inter-company invoice, the journal entry should debit/credit only the 1099/2099 accounts, never the standard 1100/2100 accounts.
7. Test with a synthetic transaction
Create a sales order from Company A to Company B for a single low-cost product. Confirm the SO. Switch to Company B. The corresponding purchase order should already be there in draft. Approve it. Switch back to A, create the customer invoice from the SO, and validate. Switch to B again, validate the vendor bill. Open Accounting > Journals and confirm both companies have the right journal entries. Verification: month-end consolidation reports should show zero net inter-company position. If they do not, you have a fiscal position mismatch.
8. Configure the inter-company journal in each company
Add a dedicated INTERCO journal in both companies (Accounting > Configuration > Journals > Create, type "Miscellaneous"). Use this journal for the elimination entries at consolidation time. Without it, your auditor will spend hours hunting through the regular sales journal.
# Custom hook to auto-tag inter-company moves on creation
from odoo import models, api
class AccountMove(models.Model):
_inherit = 'account.move'
@api.model_create_multi
def create(self, vals_list):
moves = super().create(vals_list)
for move in moves:
partner = move.partner_id
if partner and partner.is_company and partner.vat:
# Detect if partner matches another internal company
internal = self.env['res.company'].search([
('vat', '=', partner.vat),
('id', '!=', move.company_id.id),
], limit=1)
if internal:
interco_journal = self.env['account.journal'].search([
('code', '=', 'INTERCO'),
('company_id', '=', move.company_id.id),
], limit=1)
if interco_journal:
move.journal_id = interco_journal
return moves
Drop this snippet in a small custom module (account_intercompany_autotag) and the journal switch happens automatically every time you create an invoice with an internal-company partner. Verification: create an inter-company invoice and confirm it lands in the INTERCO journal, not the regular Customer Invoices journal.
9. Schedule the FX revaluation cron
If your companies use different currencies, you need monthly FX revaluation. Go to Accounting > Reporting > Foreign Currency Adjustments and configure the revaluation accounts (typically 4990 unrealized FX gain, 7990 unrealized FX loss). Schedule the cron for the first business day of each month. Verification: at month-end, the trial balance should show the unrealized FX line and balance to zero across both companies' P&L.
Common mistakes
- Forgetting to set the contact as both Customer and Vendor. The inter-company rules silently skip the document. We have lost half-day debugging sessions to this.
- Using the same VAT number on the contact and the company record without country prefix. Odoo's matching is exact string. UK GB123456789 will not match GB 123456789 (with space).
- Running
db neutralizein production thinking it only clears email queues. It also clears inter-company rules. Always neutralize on staging only. - Mapping the inter-company receivable to the regular trade receivable account. Aging reports will show your sister company as a 60-day overdue customer, which is technically true but operationally wrong.
- Skipping the fiscal position on the contact. Without it, domestic VAT gets charged on inter-company invoices and the consolidation breaks.
Going further
Once the basic flow works, layer on these advanced configurations.
Three-company chains for holding structures: parent invoices the operating subsidiary, which invoices the customer-facing brand. Set up a chain of fiscal positions, one per leg, with explicit tax remapping at each step.
Multi-currency consolidation: install account_consolidation (Enterprise only) and configure each company as a consolidation entity with its own translation rate and historical rate accounts. The CTA (cumulative translation adjustment) account will magically balance.
Profit elimination: when Company A sells inventory to Company B with a markup, the unrealized profit needs eliminating at consolidation. Odoo 19's Enterprise consolidation module handles this if you flag products as "intercompany markup" — but for complex transfer pricing, consider Odoo customization services to build a dedicated elimination wizard.
Centralized purchasing: configure all companies to buy from a single supplier in one company, with the cost re-invoiced to the consuming company. This needs a small custom module to automate the journal split, but it cuts your supplier touchpoints by 80 percent for procurement-heavy operations.
Frequently Asked Questions
Can I run multi-company invoicing on Odoo Community Edition?
Yes, but with caveats. The account_intercompany_invoice module is Community-friendly, but the auto-validate flag and the consolidation reports are Enterprise-only. Most ECOSIRE customers running Community get to about 80 percent of the Enterprise functionality with the OCA modules (account_invoice_inter_company from the multi-company repo). For full statutory consolidation, Enterprise is the cleaner path.
Do I need separate Odoo databases per company?
No. Multi-company in Odoo means multiple companies in one database, sharing user accounts but with strict data isolation by company_id. Separate databases are required only when you have separate legal jurisdictions with conflicting Odoo versions or when one entity is on a different upgrade cadence from the rest.
How do I handle inter-company stock transfers?
Use the same inter-company rules but for stock pickings instead of invoices. Configure each warehouse with its company_id and create a transit location shared between both companies. The picking from A's warehouse to transit and the picking from transit to B's warehouse are linked, and the cost flows through automatically if you have AVCO valuation enabled.
What is the impact on user permissions?
Each user has an "allowed companies" set on their res.users record. They can switch between allowed companies but cannot see data from disallowed ones. The trap is that Odoo's record rules are written assuming company_id in allowed_company_ids, so if you have custom modules that hardcode company_id = X, those rules will leak data across companies. Audit every custom rule before going live with a multi-company setup. Our Odoo support team does this audit as part of every multi-company implementation.
If you would rather have the configuration done correctly the first time, our Odoo implementation team has set this up for 200+ clients across UK, EU, US, and APAC jurisdictions. Or read our companion piece on how to migrate from Odoo Community to Enterprise once your multi-company structure is in place.
Escrito por
ECOSIRE TeamTechnical Writing
The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.
ECOSIRE
Transforme su negocio con Odoo ERP
Implementación, personalización y soporte experto de Odoo para optimizar sus operaciones.
Artículos relacionados
Cómo agregar un botón personalizado a una vista de formulario de Odoo (2026)
Agregue botones de acción personalizados a las vistas de formulario de Odoo 19: método de acción de Python, herencia de vistas, visibilidad condicional, cuadros de diálogo de confirmación. Probado en producción.
Cómo agregar un campo personalizado en Odoo sin Studio (2026)
Agregue campos personalizados a través de un módulo personalizado en Odoo 19: herencia de modelo, extensión de vista, campos calculados, decisiones de tienda/no tienda. Código primero, controlado por versiones.
Cómo agregar un informe personalizado en Odoo usando un diseño externo
Cree un informe PDF con su marca en Odoo 19 usando web.external_layout: plantilla QWeb, formato de papel, enlace de acción. Con logotipo impreso + anulaciones de pie de página.