From dcd4955bb7d50dffa651bf03f1029543668ce46f Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Thu, 4 Jun 2026 16:55:34 -0400 Subject: [PATCH] feat(jobs+receiving): confirm->receive flow, lock recipe, reset step, lock steps, fix bake gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Confirm->Receive (A): after a single interactive SO confirm, receiving's action_confirm returns action_view_receiving() so the user lands straight on the Receive Parts screen (opt-out via fp_no_receiving_redirect context). - Lock recipe (1): recipe_id readonly on the WO form — stick to the order-entry recipe. - Hide spec (2): customer_spec_id invisible on the WO form. - Reset step (3): new fp.job.step.button_reset (operator-usable, audited) + an undo button next to Start. Resets to Ready, clears finish + sign-off, closes open timelogs, keeps start audit + move/CoC history. - Lock steps (4): steps list create=false delete=false (no Add a line / no trash) — steps come from the recipe, only skippable, never deleted. - Bake gate fix (5): _fp_missing_required_step_inputs now honours the node's collect_measurements master switch, matching the Record-Inputs wizard. collect_measurements=False + required prompts no longer blocks finish (wizard shows 0 rows, so the gate must too). Unblocks WO-30098 + 63 other affected nodes (bake steps). Deployed + verified on entech (-u jobs; bake finishes, reset done->ready, recipe readonly, spec hidden, steps locked, receiving redirect target OK). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../fusion_plating_jobs/__manifest__.py | 2 +- .../fusion_plating_jobs/models/fp_job_step.py | 49 +++++++++++++++++++ .../views/fp_job_form_inherit.xml | 14 ++++-- .../models/sale_order.py | 9 ++++ 4 files changed, 70 insertions(+), 4 deletions(-) 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 @@ - + +