10000 chapter 6 - multi-company · odoo/documentation@df80bdf · GitHub
[go: up one dir, main page]

Skip to content

Commit df80bdf

Browse files
committed
chapter 6 - multi-company
1 parent 7c97cb0 commit df80bdf

File tree

2 files changed

+118
-1
lines changed

2 files changed

+118
-1
lines changed

content/developer/tutorials/server_framework_101/06_security.rst

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,10 +348,126 @@ access to property records.
348348
Separate company data
349349
=====================
350350

351-
tmp
351+
In enterprise environments, organizations often need to manage multiple distinct entities within the
352+
same system. This approach allows different branches, subsidiaries, franchises, or even completely
353+
different companies to operate independently while sharing common resources and functionalities.
354+
355+
In Odoo, the **multi-company** feature enables managing multiple companies within a single database.
356+
Each company can have its own configuration and data, while still allowing users to access data from
357+
multiple companies. This is implemented through several key mechanisms:
358+
359+
- **Company field**: Adding a `company_id` many-to-one field to a model allows linking its records
360+
to a specific company (represented by the generic `res.company` model). Records can be:
361+
- **Company-specific**: When `company_id` has a value, making the record belong to one company.
362+
- **Company-shared**: When `company_id` is empty, making the record accessible across all
363+
companies.
364+
- **Company-dependent fields**: The `company_dependent=True` attribute set on a field creates a
365+
separate value for each company. The values are stored in a JSON object in the database and the
366+
right value is automatically selected based on the current company of the user.
367+
- **Company context**: The `with_company(company)` model method temporarily changes the company
368+
context when accessing company-dependent fields, allowing to retrieve values from a specific
369+
company's perspective.
370+
- **Context-aware dependencies**: The `@api.depends_context('company')` decorator ensures that
371+
computed fields are computed depending on the current company (`self.env.company`).
372+
- **Company consistency checks**: The `_check_company=True` attribute on a relational field ensures
373+
that the linked records either belong to the same company, or are shared records. The check can be
374+
made automatic by setting the `_check_company_auto=True` class attribute. Otherwise, the check
375+
must be implemented manually by calling the `_check_company` model method.
376+
- **Company rules**: Record rules can be defined to restrict access to records based on the company
377+
they belong to. When their domain is evaluated, the `company_ids` variable contains the companies
378+
selected by the current user in the company switcher.
379+
380+
.. example::
381+
In the example below, we extend the product and product category models to support multi-company,
382+
and define record rules to ensure proper data isolation between companies.
383+
384+
.. code-block:: python
385+
386+
class Product(models.Model):
387+
_name = 'product'
388+
_check_company_auto = True
389+
390+
company_id = fields.Many2one(string="Company", comodel_name='res.company')
391+
price = fields.Float(string="Sales Price", required=True, default=100)
392+
cost = fields.Float(string="Manufacturing Cost", company_dependent=True)
393+
margin = fields.Float(
394+
string="Profit Margin", compute='_compute_margin', inverse='_inverse_margin', store=True
395+
)
396+
category_id = fields.Many2one(
397+
string="Category",
398+
comodel_name='product.category',
399+
ondelete='restrict',
400+
required=True,
401+
default=lambda self: self.env.ref('product_tutorial.category_apparel'),
402+
check_company=True,
403+
)
404+
405+
@api.depends('price', 'cost')
406+
@api.depends_context('company')
407+
def _compute_margin(self):
408+
for product in self:
409+
product.margin = product.price - product.with_company(product.company_id).cost
410+
411+
class ProductCategory(models.Model):
412+
_name = 'product.category'
413+
_check_company_auto = True
414+
415+
company_id = fields.Many2one(
416+
string="Company",
417+
comodel_name='res.company',
418+
required=True,
419+
default=lambda self: self.env.company.id,
420+
)
421+
product_ids = fields.One2many(
422+
string="Products", comodel_name='product', inverse_name='category_id', check_company=True
423+
)
424+
425+
.. note::
426+
- A `company_id` field is added to the `product` and `product.category` models, allowing them
427+
to be company-specific.
428+
- The `company_id` field is optional on the `product` model, allowing products to be shared
429+
between companies. It is however required for the `product.category` model, making
430+
categories company-specific.
431+
- It's a good practice to provide a default value for the `company_id` field, as it eases the
432+
creation of new records, especially since the company can be hidden from view when the user
433+
doesn't have access to multiple companies.
434+
- The `cost` field is company-dependent, giving each company its own cost value for the same
435+
product.
436+
- The `_compute_margin` method is decorated with `@api.depends_context('company')` to trigger
437+
recomputation when switching companies. Although not strictly necessary in this case, it
438+
also uses `with_company` to ensure retrieving cost values from te correct company.
439+
- The `_check_company_auto=True` attribute is set on both models to ensure that relational
440+
fields with the `check_company=True` attribute are properly checked. This prevents linking a
441+
product to a category belonging to a different company.
442+
443+
.. seealso::
444+
- For more details, see the :ref:`Multi-company guidelines <reference/howtos/company>`.
352445

353446
Let's adapt our real estate app to support multiple agencies while keeping their data separate.
354447

448+
.. exercise::
449+
#. Create a second company and assign it to the admin user.
450+
#. In the company switcher, tick the checkbox of the new company to have access to both companies
451+
at once. Then, switch from one company to another by clicking on the company name.
452+
#. todo
453+
#. Make properties and offers records company-specific, but allow property types and tags to be
454+
shared between companies. Ensure cross-company consistency.
455+
#. Add record rules to ensure proper data isolation between companies.
456+
457+
.. todo: require company_dependent field
458+
.. todo: require with_company
459+
.. todo: require context_depends
460+
461+
.. tip::
462+
- Reminder: The sources for generic models can be found in the
463+
`base <{GITHUB_PATH}/odoo/addons/base/>`_ module.
464+
- For some models, you might prefer linking the company to the parent model's company, through
465+
a related field, for example
466+
467+
.. spoiler:: Solution
468+
469+
tmp
470+
355471
.. _tutorials/server_framework_101/bypass_security:
3564 97E3 72

357473
Bypass security checks

content/developer/tutorials/server_framework_101/07_advanced_views.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ tmp
1919
.. todo: pills for the offer state in list view
2020
.. todo: adapt existing or add new stat button relying on a model method that returns a dictionary acting as an action descriptor
2121
.. todo: prevent agents from accessing the configuration menu item
22+
.. todo: <field name="company_id" groups="base.group_multi_company"/>
2223
2324
----
2425

0 commit comments

Comments
 (0)
0