Hướng dẫn về đa công ty¶
Cảnh báo
Bài hướng dẫn này yêu cầu bạn có kiến thức tốt về Odoo. Vui lòng tham khảo bài hướng dẫn Server framework 101 trước nếu cần.
Kể từ phiên bản 13.0, một người dùng có thể đăng nhập vào nhiều công ty cùng lúc. Điều này cho phép người dùng truy cập thông tin từ nhiều công ty, đồng thời cũng có thể tạo/chỉnh sửa bản ghi trong môi trường đa công ty.
Nếu không được quản lý đúng cách, đây có thể là nguồn gốc của nhiều hành vi không nhất quán trong môi trường đa công ty. Ví dụ, một người dùng đăng nhập vào cả công ty A và B có thể tạo một đơn bán hàng trong công ty A rồi thêm vào đó các sản phẩm thuộc công ty B. Chỉ khi người dùng đăng xuất khỏi công ty B thì lỗi truy cập mới xảy ra đối với đơn bán hàng đó.
Để quản lý đúng các hành vi đa công ty, ORM của Odoo cung cấp nhiều tính năng:
Field phụ thuộc công ty¶
Khi một bản ghi có thể truy cập từ nhiều công ty, chúng ta phải lường trước rằng các giá trị khác nhau sẽ được gán cho một field nhất định tùy theo công ty mà giá trị đó được thiết lập.
Để field của cùng một bản ghi hỗ trợ nhiều giá trị, nó phải được định nghĩa với thuộc tính company_dependent được đặt là 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
Ghi chú
Phương thức _compute_display_info được gắn decorator depends_context('company') (xem depends_context) để đảm bảo rằng computed field được tính toán lại tùy theo công ty hiện tại (self.env.company).
Khi một field phụ thuộc công ty được đọc, công ty hiện tại sẽ được dùng để lấy giá trị của field đó. Nói cách khác, nếu một người dùng đăng nhập vào cả công ty A và B với A là công ty chính và tạo một bản ghi cho công ty B, giá trị của các field phụ thuộc công ty sẽ là giá trị của công ty A.
Để đọc giá trị của các field phụ thuộc công ty được thiết lập bởi một công ty khác với công ty hiện tại, chúng ta cần đảm bảo công ty đang sử dụng là công ty đúng. Điều này có thể thực hiện bằng with_company(), phương thức này sẽ cập nhật công ty hiện tại.
# 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
Cảnh báo
Bất cứ khi nào bạn đang tính toán/tạo mới/... những thứ có thể có hành vi khác nhau giữa các công ty, bạn nên đảm bảo rằng những gì bạn đang làm được thực hiện đúng công ty. Việc luôn sử dụng with_company để tránh các vấn đề về sau không tốn kém gì nhiều.
@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)
...
Tính nhất quán đa công ty¶
Khi một bản ghi được thiết lập để chia sẻ giữa nhiều công ty thông qua field company_id, chúng ta phải đảm bảo rằng nó không thể được liên kết với bản ghi của một công ty khác thông qua một relational field. Ví dụ, chúng ta không muốn một đơn bán hàng và hóa đơn của nó thuộc về các công ty khác nhau.
Để đảm bảo tính nhất quán đa công ty này, bạn phải:
Đặt thuộc tính class
_check_company_autothànhTrue.Định nghĩa các relational field với thuộc tính
check_companyđược đặt làTruenếu model của chúng có fieldcompany_id.
Ở mỗi lần gọi create() và write(), các kiểm tra tự động sẽ được kích hoạt để đảm bảo tính nhất quán đa công ty của bản ghi.
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)
Ghi chú
Field company_id không được định nghĩa với check_company=True.
Cảnh báo
Tính năng check_company thực hiện kiểm tra một cách nghiêm ngặt! Điều này có nghĩa là nếu một bản ghi không có company_id (tức field này không bắt buộc), nó không thể được liên kết với một bản ghi mà company_id đã được thiết lập.
Ghi chú
Khi không có domain nào được định nghĩa trên field và check_company được đặt là True, một domain mặc định sẽ được thêm vào: ['|', '('company_id', '=', False), ('company_id', '=', company_id)]
Công ty mặc định¶
Khi field company_id được đặt là bắt buộc trên một model, một thực hành tốt là thiết lập một công ty mặc định. Điều này giúp đơn giản hóa quy trình thiết lập cho người dùng hoặc thậm chí đảm bảo tính hợp lệ khi công ty bị ẩn khỏi giao diện. Thực tế, công ty thường bị ẩn nếu người dùng không có quyền truy cập nhiều công ty (tức khi người dùng không thuộc group 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)
View¶
Như đã nêu ở phần trên, công ty thường bị ẩn khỏi giao diện nếu người dùng không có quyền truy cập nhiều công ty. Điều này được đánh giá thông qua group 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>
Quy tắc bảo mật¶
Khi làm việc với các bản ghi được chia sẻ giữa các công ty hoặc bị giới hạn trong một công ty duy nhất, chúng ta phải đảm bảo rằng người dùng không có quyền truy cập vào các bản ghi thuộc về công ty khác.
Điều này đạt được thông qua các quy tắc bảo mật dựa trên company_ids, chứa các công ty hiện tại của người dùng (các công ty mà người dùng đã chọn trong widget đa công ty).
<!-- 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>