diff --git a/fusion_plating/fusion_plating_bridge_mrp/models/hr_employee.py b/fusion_plating/fusion_plating_bridge_mrp/models/hr_employee.py index f766cbe6..bea5d80a 100644 --- a/fusion_plating/fusion_plating_bridge_mrp/models/hr_employee.py +++ b/fusion_plating/fusion_plating_bridge_mrp/models/hr_employee.py @@ -87,20 +87,62 @@ class HrEmployee(models.Model): for emp in self: emp.x_fc_is_clocked_in = emp.id in open_emp_ids - def _search_x_fc_is_clocked_in(self, operator, value): - """Lets `[('x_fc_is_clocked_in', '=', True)]` work as a domain.""" + def _search_x_fc_is_clocked_in(self, *args): + """Lets `[('x_fc_is_clocked_in', '=', True)]` work as a domain. + + Two compounding gotchas surfaced after fusion_clock auto-closed + the demo open attendances: + + 1. Odoo 19 normalises ``('=', True)`` into + ``('in', OrderedSet([True]))`` before invoking the search + method. The previous code only handled ``=`` / ``!=`` and + fell through to ``return []`` for ``in`` / ``not in`` — + which Odoo treats as "no constraint" and matches every + row. + + 2. ``('id', 'in', [])`` is also treated as no-constraint in + some Odoo versions; replaced with a ``[0]`` sentinel so + the empty-open-list case correctly matches nothing. + + Strategy: reduce caller intent to a *match_set* of booleans + (which values of ``x_fc_is_clocked_in`` should match), flip on + negative operators, then translate into ``id IN`` / ``NOT IN`` + on the cached open-attendance employee ids. Variable signature + future-proofs against Odoo's compute-field API shifting again. + """ + # Variable signature — Odoo 19 may pass (records, op, val). + if len(args) == 3: + _records, operator, value = args + elif len(args) == 2: + operator, value = args + else: + return [('id', '=', False)] + Att = self.env.get('hr.attendance') if Att is None: return [('id', '=', False)] - open_ids = Att.sudo().search([ - ('check_out', '=', False), - ]).mapped('employee_id').ids + if operator in ('=', '!='): - wanted = bool(value) - if operator == '!=': - wanted = not wanted - return [('id', 'in' if wanted else 'not in', open_ids)] - return [] + match_set = {bool(value)} + elif operator in ('in', 'not in'): + match_set = set(map(bool, value)) + else: + return [('id', '=', False)] + + # Negated operators flip the match set. + if operator in ('!=', 'not in'): + match_set = {True, False} - match_set + + if not match_set: + return [('id', '=', False)] + if match_set == {True, False}: + return [] # every row matches + + open_emp_ids = Att.sudo().search( + [('check_out', '=', False)] + ).employee_id.ids + ids_term = open_emp_ids or [0] + return [('id', 'in' if True in match_set else 'not in', ids_term)] @api.model def _fp_clocked_in_user_ids(self):