راهنمای چندشرکتی

هشدار

این آموزش نیاز به دانش خوبی از Odoo دارد. در صورت نیاز، لطفاً ابتدا به آموزش Server framework 101 مراجعه کنید.

از نسخهٔ 13.0 به بعد، یک کاربر می‌تواند هم‌زمان به چند شرکت وارد شود. این به کاربر اجازه می‌دهد به اطلاعات چند شرکت دسترسی پیدا کند و همچنین رکوردها را در محیط چندشرکتی ایجاد/ویرایش کند.

اگر به‌درستی مدیریت نشود، می‌تواند منبع بسیاری از رفتارهای ناسازگار چندشرکتی باشد. برای مثال، کاربری که به شرکت‌های A و B وارد شده است می‌تواند یک سفارش فروش در شرکت A ایجاد کند و محصولات متعلق به شرکت B را به آن اضافه کند. تنها زمانی که کاربر از شرکت B خارج شود، خطاهای دسترسی برای سفارش فروش رخ خواهد داد.

برای مدیریت صحیح رفتارهای چندشرکتی، ORM در Odoo چند ویژگی ارائه می‌دهد:

فیلدهای وابسته به شرکت

وقتی رکوردی از چند شرکت در دسترس باشد، باید انتظار داشته باشیم که مقادیر متفاوتی بر اساس شرکتی که مقدار از آن تنظیم می‌شود، به فیلد مشخصی اختصاص داده شود.

برای اینکه فیلد یک رکورد بتواند چند مقدار را پشتیبانی کند، باید با ویژگی 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 تعریف شود.

هشدار

ویژگی 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>