多公司指南¶
警告
本教程要求您对 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)
注解
不得使用 check_company=True 定义字段 company_id。
警告
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 的安全规则来实现,其中包含用户的当前公司(用户在多公司小组件中勾选的公司)。
id,name,model_id,group_id/id,operation,domain
record_shared_company_rule,Shared Record: multi-company,record.shared,,crud,"[
'|', ('company_id', '=', False), ('company_id', 'in', company_ids),
]"
id,name,model_id,group_id/id,operation,domain
record_restricted_company_rule,Restricted Record: multi-company,record.restricted,,crud,"[
('company_id', 'in', company_ids),
]"