All posts
Integrator9 min read

Odoo multi-company setup: shared records, access rules, and inter-company transactions

Odoo supports running multiple legal entities from one instance. This guide explains which records are shared versus per-company, how the company_id access rules work, how to configure inter-company rules for automatic transactions, and the common mistakes that expose one company's data to another.

What Multi-Company Means in Odoo#

Odoo's multi-company feature allows a single Odoo database to host multiple legal entities - each with its own chart of accounts, fiscal positions, bank accounts, and documents. Users can be members of multiple companies and switch between them in a single session.

The database is shared: all companies share the same PostgreSQL tables. Separation is enforced by company_id fields and record rules, not by physical isolation.


Which Records Are Shared, Which Are Per-Company#

Shared by default (no company_id)#

  • Contacts (res.partner): shared across all companies unless you enable the "Company-Specific" option per partner
  • Products (product.template, product.product): shared by default; pricelists and costs are per-company
  • Product categories, units of measure, currencies

Per-company by default (have company_id)#

  • Chart of accounts: each company has its own accounts, journals, and tax configuration
  • Sale orders, purchase orders, invoices: always belong to a specific company
  • Pricelists (when set as company-specific)
  • Warehouses: a warehouse belongs to one company

Configurable#

Some records can be toggled between shared and per-company in Settings → Technical. The most common is Pricelists: you can set a pricelist as company-specific by giving it a company_id.


How Record Rules Enforce Company Separation#

Odoo uses ir.rule domain filters to restrict what a user sees. The default multi-company rule for sale orders looks roughly like:

python
[('company_id', 'child_of', user.company_ids.ids)]

This means a user who is a member of companies A and B can see records from both companies simultaneously if both are active in their session. If you need strict separation (no cross-company visibility), remove users from the companies they should not access.

Important: Record rules apply at the ORM level, not at the SQL level. If you use raw SQL queries (via env.cr.execute), record rules are bypassed. Always use the ORM for company-sensitive queries.


Configuring Users for Multi-Company#

  1. Go to Settings → Users & Companies → Users → select a user
  2. In the Companies tab, add all companies the user should have access to
  3. The user's Current Company is the one active documents are created in
  4. Users can switch companies with the company switcher in the top menu

When a user activates multiple companies simultaneously (holding Ctrl when clicking), Odoo creates a multi-company context. Documents created in this context inherit the primary company.


Inter-Company Rules: Automatic Transactions#

Inter-company rules automate the creation of corresponding documents when a transaction occurs in one company that involves another.

Setting Up Inter-Company Rules#

Go to Settings → General Settings → Companies → enable Synchronize Operations.

For each company pair, configure:

  • Sale Order → Purchase Order: when Company A creates a sale order to Company B, Odoo auto-creates a purchase order in Company B
  • Purchase Order → Sale Order: the reverse
  • Customer Invoice → Vendor Bill: when a posted invoice is sent to a related company, a draft vendor bill is created in the receiving company

Requirements#

  • Both companies must be in the same Odoo database
  • The partner record for Company B must have company_id set to Company B's res.company record (i.e., the contact IS the company)
  • The user triggering the rule must have access to both companies

Common Mistakes#

Mistake 1: Shared users see each other's drafts#

If a user is a member of both Company A and Company B and activates both companies in the switcher, they see all records from both. This is by design - but it surprises teams who expect complete separation.

Fix: If you need strict isolation, keep users in only one company. Use separate login accounts for staff who genuinely work in both entities.

Mistake 2: Products have wrong costs per company#

Product cost (standard_price) is stored per-company using the property system. Setting it on the product form while logged into Company A does not affect Company B's cost.

Fix: Switch to Company B and update the standard price there explicitly.

Mistake 3: Journal entries on the wrong company's account#

When posting an inter-company invoice, Odoo uses the company on the invoice to determine which chart of accounts to use. If the invoice company_id was set incorrectly at creation, the journal entries will post to the wrong company's accounts.

Fix: Always verify company_id on draft documents before posting. Add a server action check if this is recurring.

Mistake 4: Fiscal position not found for inter-company invoice#

Inter-company invoices use the fiscal position of the receiving partner. If no fiscal position maps the inter-company partner, taxes may not be applied or removed as expected.

Fix: Define a dedicated fiscal position for inter-company transactions and assign it to the company partner record.


Multi-Company and Custom Modules#

If you are building a custom module that stores company-specific data:

  1. Add company_id = fields.Many2one('res.company', required=True, default=lambda self: self.env.company)
  2. Add an ir.rule that filters by company_id in user.company_ids.ids
  3. Add a _sql_constraints unique constraint that includes company_id if the record should be unique per company but not globally

ERPeek can inspect whether your custom models have company_id fields and whether the corresponding record rules are correctly defined. See the contact page for details.

Try ERPeek on your own Odoo module - ask questions, scaffold tests, and explore your codebase in plain language.

Get started free