多公司指南¶
警告
本教程需要对Odoo有很好的了解。如有需要,请先参考 服务器框架入门 教程。
从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和公司B(A为主公司),并为公司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
。
在每次调用 create()
和 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
。
- Model._check_company(fnames=None)[源代码]¶
检查给定字段名称值的公司。
- 参数
fnames (list) – 要检查的关系字段的名称
- 引发
UserError – 如果任何字段的值的
company_id
不在[False, self.company_id]
中(或者如果是res_company
,则是self
)。
对于
res_users
的关系字段,验证记录的公司是否在company_ids
字段中。主公司为 A 的用户,拥有访问公司 A 和 B 的权限,可以被分配或链接到公司 B 的记录。
警告
check_company
功能执行严格的检查!这意味着如果一条记录没有 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)
视图¶
如上文 所述 ,如果用户无法访问多个公司,公司字段通常会从视图中隐藏。这是通过组 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
的安全规则实现的, 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>