feat: implement bi-directional synchronization of company access between employees and res.users

This commit is contained in:
Suherdy Yacob 2026-05-28 15:20:20 +07:00
parent e5998791e9
commit dfe249a1f0
5 changed files with 108 additions and 0 deletions

View File

@ -1,2 +1,3 @@
from . import hr_employee
from . import pos_config
from . import res_users

View File

@ -27,6 +27,29 @@ class HrEmployee(models.Model):
domain="[('share', '=', False), ('company_ids', 'in', company_ids)]"
)
@api.model_create_multi
def create(self, vals_list):
employees = super().create(vals_list)
employees.sudo()._sync_user_company_ids()
return employees
def write(self, vals):
res = super().write(vals)
if any(f in vals for f in ['company_ids', 'company_id', 'user_id']):
self.sudo()._sync_user_company_ids()
return res
def _sync_user_company_ids(self):
for employee in self:
if employee.user_id:
user = employee.user_id
employee_companies = employee.company_ids | employee.company_id
companies_to_add = employee_companies - user.company_ids
if companies_to_add:
user.write({
'company_ids': [(4, cid.id) for cid in companies_to_add]
})
@api.depends('company_ids')
def _compute_company_id(self):
for employee in self:
@ -110,6 +133,13 @@ class HrEmployee(models.Model):
if rule_product.domain_force != new_domain_product:
rule_product.sudo().write({'domain_force': new_domain_product})
# Proactively synchronize company_ids for all existing employees to cure current mismatches
try:
mismatched_employees = self.sudo().search([('user_id', '!=', False)])
mismatched_employees._sync_user_company_ids()
except Exception:
pass
class HrEmployeePublic(models.Model):

77
models/res_users.py Normal file
View File

@ -0,0 +1,77 @@
from odoo import models, api
class ResUsers(models.Model):
_inherit = 'res.users'
@api.model_create_multi
def create(self, vals_list):
# Pre-process vals to ensure primary company_id is always in allowed company_ids
# This prevents Odoo's _check_user_company ValidationError when template users have different company_ids
for vals in vals_list:
company_id = vals.get('company_id')
if company_id:
if 'company_ids' in vals:
company_ids_commands = vals['company_ids']
existing_ids = set()
has_six = False
six_index = -1
for idx, cmd in enumerate(company_ids_commands):
if isinstance(cmd, (list, tuple)) and len(cmd) >= 3:
if cmd[0] == 6:
existing_ids.update(cmd[2])
has_six = True
six_index = idx
elif cmd[0] == 4:
existing_ids.add(cmd[1])
if company_id not in existing_ids:
if has_six and six_index != -1:
cmd = list(company_ids_commands[six_index])
cmd[2] = list(cmd[2]) + [company_id]
company_ids_commands[six_index] = tuple(cmd)
else:
company_ids_commands.append((4, company_id))
else:
vals['company_ids'] = [(6, 0, [company_id])]
users = super().create(vals_list)
users.sudo()._sync_employee_company_ids()
return users
def write(self, vals):
# Pre-process vals to ensure primary company_id is always in allowed company_ids
company_id = vals.get('company_id')
if company_id:
has_company_ids = 'company_ids' in vals
existing_ids = set()
if has_company_ids:
for cmd in vals['company_ids']:
if isinstance(cmd, (list, tuple)) and len(cmd) >= 3:
if cmd[0] == 6:
existing_ids.update(cmd[2])
elif cmd[0] == 4:
existing_ids.add(cmd[1])
for user in self:
user_allowed = set(user.company_ids.ids)
if has_company_ids:
if company_id not in existing_ids:
vals['company_ids'] = list(vals['company_ids']) + [(4, company_id)]
else:
if company_id not in user_allowed:
vals['company_ids'] = [(4, company_id)]
res = super().write(vals)
if any(f in vals for f in ['company_id', 'company_ids']):
self.sudo()._sync_employee_company_ids()
return res
def _sync_employee_company_ids(self):
for user in self:
employee_companies = user.employee_ids.mapped('company_ids')
employee_primary_companies = user.employee_ids.mapped('company_id')
companies_to_add = (employee_companies | employee_primary_companies | user.company_id) - user.company_ids
if companies_to_add:
user.write({
'company_ids': [(4, cid.id) for cid in companies_to_add]
})