diff --git a/fusion_plating/fusion_plating_jobs/__manifest__.py b/fusion_plating/fusion_plating_jobs/__manifest__.py index c3240cc4..ce61ab41 100644 --- a/fusion_plating/fusion_plating_jobs/__manifest__.py +++ b/fusion_plating/fusion_plating_jobs/__manifest__.py @@ -3,7 +3,7 @@ # License OPL-1 (Odoo Proprietary License v1.0) { 'name': 'Fusion Plating — Native Jobs', - 'version': '19.0.12.1.7', + 'version': '19.0.12.4.0', 'category': 'Manufacturing/Plating', 'summary': 'Native plating job model — replaces mrp.production / mrp.workorder bridge.', 'author': 'Nexa Systems Inc.', diff --git a/fusion_plating/fusion_plating_jobs/models/fp_job_step.py b/fusion_plating/fusion_plating_jobs/models/fp_job_step.py index 04f26e57..89425bfc 100644 --- a/fusion_plating/fusion_plating_jobs/models/fp_job_step.py +++ b/fusion_plating/fusion_plating_jobs/models/fp_job_step.py @@ -486,6 +486,45 @@ class FpJobStep(models.Model): step.state = 'cancelled' return True + def button_reset(self): + """Reset a step back to 'ready' so it can be redone — operator + self-serve for a mistake, an accidental skip, or a customer redo + request. Clears the finish + sign-off stamps and closes any open + timelog so the redo re-captures them; KEEPS the first-start audit + (date_started / started_by) and the move / CoC history intact. + Posts a chatter audit on the parent job. No-op on a step already + ready/pending (nothing to undo). + """ + now = fields.Datetime.now() + for step in self: + if step.state in ('ready', 'pending'): + continue + prev_label = dict( + step._fields['state'].selection + ).get(step.state, step.state) + open_logs = step.time_log_ids.filtered( + lambda l: not l.date_finished) + if open_logs: + open_logs.write({'date_finished': now, 'state': 'stopped'}) + vals = {'state': 'ready'} + if 'date_finished' in step._fields: + vals['date_finished'] = False + if 'finished_by_user_id' in step._fields: + vals['finished_by_user_id'] = False + if 'signoff_user_id' in step._fields: + vals['signoff_user_id'] = False + step.write(vals) + if step.job_id: + step.job_id.message_post(body=_( + 'Step "%(s)s" reset to Ready (was %(p)s) by %(u)s ' + 'for redo.' + ) % { + 's': step.name or '?', + 'p': prev_label, + 'u': self.env.user.display_name, + }) + return True + def write(self, vals): """Post a chatter trail on the parent JOB whenever an active step gets reassigned. The step itself already tracks @@ -846,6 +885,16 @@ class FpJobStep(models.Model): Prompt = self.env['fusion.plating.process.node.input'] if not node: return Prompt + # Master switch (Sub 12d): when the recipe node opts OUT of + # measurement collection, the Record-Inputs wizard returns ZERO + # rows (fp_job_step_input_wizard.default_get). The finish gate MUST + # agree — otherwise required prompts are demanded with no way to + # enter them and the step is permanently stuck (bake nodes with + # collect_measurements=False but required prompts — WO-30098 + 63 + # others on entech). Honour the switch here so gate <=> wizard. + if ('collect_measurements' in node._fields + and not node.collect_measurements): + return Prompt prompts = node.input_ids if 'kind' in prompts._fields: prompts = prompts.filtered(lambda i: i.kind == 'step_input') diff --git a/fusion_plating/fusion_plating_jobs/views/fp_job_form_inherit.xml b/fusion_plating/fusion_plating_jobs/views/fp_job_form_inherit.xml index 9a1b88f9..9aaab80f 100644 --- a/fusion_plating/fusion_plating_jobs/views/fp_job_form_inherit.xml +++ b/fusion_plating/fusion_plating_jobs/views/fp_job_form_inherit.xml @@ -100,8 +100,8 @@ - - + + @@ -118,7 +118,7 @@ - + +