- How multi-company works in Odoo:
allowed_company_idsand the company switcher - Adding
company_idto a custom model company_dependentfields for per-company configuration- Writing record rules that enforce company isolation
- Common multi-company pitfalls and how to avoid them
How Multi-Company Works
In a multi-company Odoo instance, each user has a primary company and a set of allowed companies. The current company context is in self.env.company and the allowed set is in self.env.companies:
self.env.company # the currently active company (res.company)
self.env.companies # all companies the user has switched to (recordset)
self.env.company.id # the active company ID
self.env.companies.ids # list of allowed company IDs
When a user has multiple companies active, ORM searches automatically include records from all active companies (if record rules are set up correctly).
Adding company_id to Your Model
Use the standard company_id field name with res.company as the comodel:
from odoo import models, fields
class LibraryBook(models.Model):
_name = 'library.book'
_description = 'Library Book'
name = fields.Char(string='Title', required=True)
company_id = fields.Many2one(
'res.company',
string='Company',
required=True,
default=lambda self: self.env.company,
index=True,
)
The default lambda self: self.env.company sets the company to the user's current active company when creating a new record.
Multi-Company Record Rules
A record rule enforces that users can only see records from their allowed companies:
<record id="rule_library_book_company" model="ir.rule">
<field name="name">Library Book: multi-company rule</field>
<field name="model_id" ref="model_library_book"/>
<field name="domain_force">
['|',
('company_id', '=', False),
('company_id', 'in', company_ids)]
</field>
</record>
The domain ('company_id', '=', False) allows shared records (no company) to be visible to everyone. company_ids is a special variable in record rule domains that resolves to the user's allowed company IDs at evaluation time.
company_dependent Fields
Some configuration fields should have different values per company without duplicating records. Use company_dependent=True:
class LibraryConfig(models.TransientModel):
_inherit = 'res.config.settings'
library_late_fee = fields.Float(
string='Late Fee per Day',
config_parameter='library.late_fee',
company_dependent=True, # each company has its own value
)
With company_dependent=True, each company stores its own value for the field. When you read the field, Odoo automatically returns the value for self.env.company. The field type must be one of: Char, Float, Integer, Boolean, Date, Datetime, Selection, or Many2one.
Common Multi-Company Pitfalls
| Mistake | Symptom | Fix |
|---|---|---|
Hardcoding company_id=1 | Data only works for main company | Always use self.env.company.id |
| No record rule on company_id | Users see other companies' data | Add ir.rule with company_ids domain |
Forgetting | company_id=False in rule | Shared configuration invisible | Always add the False alternative |
| sudo() bypasses record rules | Multi-company isolation broken | Add company filter explicitly in sudo() searches |
- Use
self.env.company(not hardcoded IDs) for the current company - Add a
company_idMany2one field with a default lambda and a matching record rule - Record rule domain:
['|', ('company_id', '=', False), ('company_id', 'in', company_ids)] - Use
company_dependent=Truefor configuration fields that differ per company
Frequently Asked Questions
What's the difference between company_id and the branches feature?
The company_id field is Odoo's core multi-company mechanism — each company is a fully independent accounting entity. Odoo 17+ introduced "branches" as a lighter-weight sub-company structure for organizations that share accounting but have different operational units. Branches use the same mechanism but with a different UX and less strict isolation.
How do I create records for a specific company from code?
Use self.with_company(target_company).env['my.model'].create({...}). This sets the environment's company to target_company for that context. Alternatively, explicitly set 'company_id': target_company.id in the create vals. The first approach is safer as it also affects defaults of related records created during that operation.
Can a record belong to multiple companies?
Standard records with company_id belong to one company (or none for shared records). Some models like res.partner and product.template use company_ids (Many2many) instead to allow a record to be accessible across multiple companies. Use Many2many for resources that need to be shared selectively across companies.