多公司指南?
警告
本教程需要對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 main company (self.env.company)
val = record.company_dependent_field
# Accessed as 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]
(或者self
如果是res_company
)。
對于
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
的安全規則實現的,其中包含用戶的當前公司(用戶在多公司小部件中選中的公司)。
<!-- 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>