多公司指南¶
警告
本教程需要对 Odoo 有很好的了解。如果需要,请先参阅 服务器框架101 教程。
从版本 13.0 开始,一个用户可以同时登录多个公司。这允许用户访问来自多个公司的信息,而且还可以在多公司环境中创建/编辑记录。
如果管理不当,可能会成为许多不一致的多公司行为的根源。例如,同时登录公司 A 和 B 的用户可以在公司 A 中创建销售订单,并将属于公司 B 的产品添加到其中。只有当用户从B公司注销时,销售订单才会出现访问错误。
为了正确管理多公司行为,Odoo 的 ORM 提供了多种功能:
公司相关领域¶
当一条记录可从多个公司获得时,我们必须预期,根据设置值的公司,给定字段将分配不同的值。
为了使同一记录的字段支持多个值,必须将其属性 company_dependent 设置为 True 进行定义。
from odoo import api, fields, models
class Record(models.Model):
_name = 'record.public'
info = fields.Text()
company_info = fields.Text(company_dependent=True)
display_info = fields.Text(string='Infos', compute='_compute_display_info')
@api.depends_context('company')
def _compute_display_info(self):
for record in self:
record.display_info = record.info + record.company_info
注解
_compute_display_info 方法用 depends_context('company') 修饰(请参阅 depends_context),以确保根据当前公司 (self.env.company) 重新计算计算字段。
当读取与公司相关的字段时,将使用当前公司来检索其值。也就是说,如果用户以A为主公司分别登录A公司和B公司,并为B公司创建一条记录,则公司相关字段的值将是A公司的值。
要读取由另一家公司而不是当前公司设置的公司相关字段的值,我们需要确保我们正在使用的公司是正确的。 这可以通过 with_company() 来完成,它会更新当前公司。
# Accessed as the main company (self.env.company)
val = record.company_dependent_field
# Accessed as the desired company (company_B)
val = record.with_company(company_B).company_dependent_field
# record.with_company(company_B).env.company == company_B
警告
每当您计算/创建/…在不同公司中可能表现不同的事物时,您应该确保您所做的任何事情都是在正确的公司中完成的。始终使用 with_company 来避免以后出现问题并不需要花费太多。
@api.onchange('field_name')
def _onchange_field_name(self):
self = self.with_company(self.company_id)
...
@api.depends('field_2')
def _compute_field_3(self):
for record in self:
record = record.with_company(record.company_id)
...
多公司一致性¶
当一条记录通过 company_id 字段在多家公司之间共享时,我们必须注意它不能通过关系字段链接到另一家公司的记录。例如,我们不希望销售订单及其发票属于不同的公司。
为了确保多公司一致性,您必须:
将类属性
_check_company_auto设置为True。如果关系字段的模型具有
company_id字段,则定义关系字段,并将属性check_company设置为True。
在每个:meth:`~odoo.models.Model.create`和:meth:`~odoo.models.Model.write`上,都会触发自动检查,以确保记录的多公司一致性。
from odoo import fields, models
class Record(models.Model):
_name = 'record.shareable'
_check_company_auto = True
company_id = fields.Many2one('res.company')
other_record_id = fields.Many2one('other.record', check_company=True)
注解
字段 company_id 不得与 check_company=True 一起定义。
警告
The check_company feature performs a strict check!这意味着如果记录没有 company_id (即该字段不是必需的),则它不能链接到设置了 company_id 的记录。
注解
当字段上未定义域且 check_company 设置为 True 时,将添加默认域:['|', '('company_id', '=', False), ('company_id', '=', company_id)]
默认公司¶
当模型上需要字段 company_id 时,一个好的做法是设置默认公司。它简化了用户的设置流程,甚至在公司隐藏时保证其有效性。事实上,如果用户无权访问多个公司(即,当用户没有组 base.group_multi_company 时),该公司通常是隐藏的。
from odoo import api, fields, models
class Record(models.Model):
_name = 'record.restricted'
_check_company_auto = True
company_id = fields.Many2one(
'res.company', required=True, default=lambda self: self.env.company
)
other_record_id = fields.Many2one('other.record', check_company=True)
意见¶
如 above 中所述,如果用户无权访问多个公司,该公司通常会隐藏在视图中。这是由 base.group_multi_company 小组进行评估。
<record model="ir.ui.view" id="record_form_view">
<field name="name">record.restricted.form</field>
<field name="model">record.restricted</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<group>
<field name="company_id" groups="base.group_multi_company"/>
<field name="other_record_id"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
安全规则¶
当处理跨公司共享或仅限于单个公司的记录时,我们必须注意用户无权访问属于其他公司的记录。
这是通过基于 company_ids 的安全规则来实现的,其中包含用户当前的公司(用户在多公司小部件中检查的公司)。
<!-- Shareable Records -->
<record model="ir.rule" id="record_shared_company_rule">
<field name="name">Shared Record: multi-company</field>
<field name="model_id" ref="model_record_shared"/>
<field name="global" eval="True"/>
<field name="domain_force">
['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]
</field>
</record>
<!-- Company-restricted Records -->
<record model="ir.rule" id="record_restricted_company_rule">
<field name="name">Restricted Record: multi-company</field>
<field name="model_id" ref="model_record_restricted"/>
<field name="global" eval="True"/>
<field name="domain_force">
[('company_id', 'in', company_ids)]
</field>
</record>