import hashlib from odoo import models, fields, api, _ class HrEmployee(models.Model): _inherit = 'hr.employee' company_ids = fields.Many2many( 'res.company', string='Branches', domain="[('parent_id', '!=', False)]", help="Branch companies this employee is associated with." ) # Overriding company_id to be computed from company_ids # This maintains compatibility with standard Odoo logic that expects a single company_id company_id = fields.Many2one( 'res.company', string='Company', compute='_compute_company_id', store=True, readonly=False, required=True, help="The primary company of the employee. Automatically set to the first branch in Branches." ) attendance_manager_id = fields.Many2one( 'res.users', 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: if employee.company_ids: employee.company_id = employee.company_ids[0] elif employee.company_id: # 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_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) @api.model def _load_pos_data_read(self, records, config): """Override to read employee data (including work_contact_id / res.partner) with sudo() so cashier users whose user.company_ids doesn't include the employee partner's company_id don't get a read access error on session open. The employee records are already filtered by _load_pos_data_domain before arriving here, so sudo() is safe — we're only relaxing the partner rule. """ fields = self._load_pos_data_fields(config) # Read with sudo to bypass res.partner multi-company rule for work_contact_id read_records = records.sudo().read(fields, load=False) manager_ids = records.filtered( lambda emp: config.group_pos_manager_id.id in emp.user_id.all_group_ids.ids ).ids employees_barcode_pin = records.get_barcodes_and_pin_hashed() bp_per_employee_id = {bp_e['id']: bp_e for bp_e in employees_barcode_pin} for employee in read_records: if employee['id'] in manager_ids: role = 'manager' employee['_user_role'] = 'admin' elif employee['id'] in config.advanced_employee_ids.ids: role = 'manager' elif employee['id'] in config.minimal_employee_ids.ids: role = 'minimal' else: role = 'cashier' employee['_role'] = role employee['_barcode'] = bp_per_employee_id[employee['id']]['barcode'] employee['_pin'] = bp_per_employee_id[employee['id']]['pin'] return read_records class HrEmployeePublic(models.Model): _inherit = 'hr.employee.public' company_ids = fields.Many2many( 'res.company', related='employee_id.company_ids', readonly=True, string='Branches' )