feat: implement multi-branch employee visibility through updated security rules, search overrides, and company sync logic
This commit is contained in:
parent
6a13fef24d
commit
b45364a528
22
README.md
Normal file
22
README.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# Multi-Company Employee Assignment (Odoo 19)
|
||||||
|
|
||||||
|
This module extends the standard Odoo HR functionality to allow a single employee record to be associated with multiple branch companies.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- **Multi-Branch Assignment**: Add a "Branches" (Many2many) field to the employee form.
|
||||||
|
- **Cross-Branch Visibility**: Employees appear in the "Employees" menu and search panel for all assigned branches.
|
||||||
|
- **POS Integration**: Cashiers and employees appear in the POS login interface for all branches they are assigned to.
|
||||||
|
- **Security Logic**: Access control rules are updated to allow visibility across all selected branches.
|
||||||
|
- **Fallback Mode**: Existing employees with no branches assigned remain visible based on their original primary company.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
1. Open an **Employee** form.
|
||||||
|
2. In the header/top section, find the **Branches** field.
|
||||||
|
3. Select all the branch companies this employee should belong to.
|
||||||
|
4. The first branch in the list will automatically be set as the "Primary Company" for standard Odoo processes.
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
- Overrides `hr.employee`'s `company_id` to be a computed field based on `company_ids`.
|
||||||
|
- Overrides `_search` and `ir.rule` to support Many2many company filtering.
|
||||||
|
- Overrides `pos.config`'s `_employee_domain` for POS login visibility.
|
||||||
|
- Includes transition logic for existing data.
|
||||||
BIN
__pycache__/__init__.cpython-312.pyc
Normal file
BIN
__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
@ -32,6 +32,37 @@ class HrEmployee(models.Model):
|
|||||||
for employee in self:
|
for employee in self:
|
||||||
if employee.company_ids:
|
if employee.company_ids:
|
||||||
employee.company_id = employee.company_ids[0]
|
employee.company_id = employee.company_ids[0]
|
||||||
elif not employee.company_id:
|
elif employee.company_id:
|
||||||
# Fallback to current company if none specified
|
# Sync company_ids from company_id for existing records
|
||||||
|
employee.company_ids = [(6, 0, [employee.company_id.id])]
|
||||||
|
else:
|
||||||
employee.company_id = self.env.company
|
employee.company_id = self.env.company
|
||||||
|
employee.company_ids = [(6, 0, [self.env.company.id])]
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _search(self, domain, offset=0, limit=None, order=None, **kwargs):
|
||||||
|
""" Override search to include company_ids and fallback to company_id if empty """
|
||||||
|
def replace_company_leaf(dom):
|
||||||
|
if not dom:
|
||||||
|
return dom
|
||||||
|
new_dom = []
|
||||||
|
for leaf in dom:
|
||||||
|
if isinstance(leaf, (list, tuple)):
|
||||||
|
if leaf[0] == 'company_id':
|
||||||
|
# Match if Branch is in search OR (Branch is empty AND Company is in search)
|
||||||
|
new_dom.append('|')
|
||||||
|
new_dom.append(('company_ids', leaf[1], leaf[2]))
|
||||||
|
new_dom.append('&')
|
||||||
|
new_dom.append(('company_ids', '=', False))
|
||||||
|
new_dom.append(('company_id', leaf[1], leaf[2]))
|
||||||
|
else:
|
||||||
|
new_dom.append(leaf)
|
||||||
|
elif isinstance(leaf, list):
|
||||||
|
new_dom.append(replace_company_leaf(leaf))
|
||||||
|
else:
|
||||||
|
new_dom.append(leaf)
|
||||||
|
return new_dom
|
||||||
|
|
||||||
|
if domain:
|
||||||
|
domain = replace_company_leaf(domain)
|
||||||
|
return super()._search(domain, offset=offset, limit=limit, order=order, **kwargs)
|
||||||
|
|||||||
@ -5,14 +5,14 @@
|
|||||||
<field name="name">Attendance multi branch rule</field>
|
<field name="name">Attendance multi branch rule</field>
|
||||||
<field name="model_id" ref="hr_attendance.model_hr_attendance"/>
|
<field name="model_id" ref="hr_attendance.model_hr_attendance"/>
|
||||||
<field name="global" eval="True"/>
|
<field name="global" eval="True"/>
|
||||||
<field name="domain_force">['|', ('employee_id.company_ids', '=', False), ('employee_id.company_ids', 'in', company_ids)]</field>
|
<field name="domain_force">['|', ('employee_id.company_ids', 'in', company_ids), '&', ('employee_id.company_ids', '=', False), ('employee_id.company_id', 'in', company_ids)]</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="hr_attendance.hr_attendance_overtime_line_rule_employee_company" model="ir.rule">
|
<record id="hr_attendance.hr_attendance_overtime_line_rule_employee_company" model="ir.rule">
|
||||||
<field name="name">Overtime Line multi branch rule</field>
|
<field name="name">Overtime Line multi branch rule</field>
|
||||||
<field name="model_id" ref="hr_attendance.model_hr_attendance_overtime_line"/>
|
<field name="model_id" ref="hr_attendance.model_hr_attendance_overtime_line"/>
|
||||||
<field name="global" eval="True"/>
|
<field name="global" eval="True"/>
|
||||||
<field name="domain_force">['|', ('employee_id.company_ids', '=', False), ('employee_id.company_ids', 'in', company_ids)]</field>
|
<field name="domain_force">['|', ('employee_id.company_ids', 'in', company_ids), '&', ('employee_id.company_ids', '=', False), ('employee_id.company_id', 'in', company_ids)]</field>
|
||||||
</record>
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
<field name="name">Employee multi branch rule</field>
|
<field name="name">Employee multi branch rule</field>
|
||||||
<field name="model_id" ref="hr.model_hr_employee"/>
|
<field name="model_id" ref="hr.model_hr_employee"/>
|
||||||
<field name="global" eval="True"/>
|
<field name="global" eval="True"/>
|
||||||
<field name="domain_force">['|', ('company_ids', '=', False), ('company_ids', 'in', company_ids)]</field>
|
<field name="domain_force">['|', ('company_ids', 'in', company_ids), '&', ('company_ids', '=', False), ('company_id', 'in', company_ids)]</field>
|
||||||
</record>
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<odoo>
|
<odoo>
|
||||||
|
<!-- Form View Extension -->
|
||||||
<record id="view_employee_form_inherit_multi_company" model="ir.ui.view">
|
<record id="view_employee_form_inherit_multi_company" model="ir.ui.view">
|
||||||
<field name="name">hr.employee.form.inherit.multi.company</field>
|
<field name="name">hr.employee.form.inherit.multi.company</field>
|
||||||
<field name="model">hr.employee</field>
|
<field name="model">hr.employee</field>
|
||||||
<field name="inherit_id" ref="hr.view_employee_form"/>
|
<field name="inherit_id" ref="hr.view_employee_form"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<!-- Replace company_id with company_ids -->
|
|
||||||
<xpath expr="//field[@name='company_id']" position="after">
|
<xpath expr="//field[@name='company_id']" position="after">
|
||||||
<field name="company_ids" widget="many2many_tags" options="{'no_create': True}"/>
|
<field name="company_ids" widget="many2many_tags" options="{'no_create': True}"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
@ -14,4 +14,25 @@
|
|||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<!-- Search View Extension -->
|
||||||
|
<record id="view_employee_filter_inherit_multi_company" model="ir.ui.view">
|
||||||
|
<field name="name">hr.employee.search.inherit.multi.company</field>
|
||||||
|
<field name="model">hr.employee</field>
|
||||||
|
<field name="inherit_id" ref="hr.view_employee_filter"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//filter[@name='group_job']" position="after">
|
||||||
|
<filter name="group_branches" string="Branches" domain="[]" context="{'group_by': 'company_ids'}"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Action Overrides to support Multi-Branch visibility in views -->
|
||||||
|
<record id="hr.open_view_employee_list_my" model="ir.actions.act_window">
|
||||||
|
<field name="domain">[('company_id', 'in', allowed_company_ids)]</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="hr.open_view_employee_list" model="ir.actions.act_window">
|
||||||
|
<field name="domain">[('company_id', 'in', allowed_company_ids)]</field>
|
||||||
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user