From 89dd77aff2a91e2146068806d4466007d9ffbd8e Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Tue, 12 May 2026 00:05:05 -0400 Subject: [PATCH] fix(jobs): workflow bar stuck at Draft for confirmed/done jobs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause: two compounding bugs in fp.job.workflow_state_id. - The "Confirmed" state seed has no trigger fields set, so it never passes _fp_is_passed_for_job. - The _compute_workflow_state_id loop breaks on the first non-passed state — so when Confirmed fails, every later state stays unevaluated and the bar is stuck at Draft. Fixes: - Add trigger_on_job_state Selection field on fp.job.workflow.state with values confirmed/in_progress/done. Passes when fp.job.state >= the chosen value ("at least" semantics with explicit ordering that treats on_hold==in_progress and cancelled outside the progression). Lets workflow states key off the job's own state when recipe default_kind tagging isn't present. - Extend _fp_is_passed_for_job with the new branch. - Change _compute_workflow_state_id from first-non-pass-breaks to highest-passed-wins. Untagged/not-applicable states no longer block the cascade — the bar shows the furthest milestone the job has actually reached. - Seed update (DB-side, since data is noupdate=1): Confirmed now has trigger_on_job_state='confirmed'. Result: Work Order # 00011 (state=confirmed, all 11 steps done/ skipped) now correctly shows the bar at "Done" instead of "Draft" (via the existing trigger_all_steps_done on Done). Mid-flight confirmed jobs without recipe tagging will show at least "Confirmed" now. Recipe authoring note (out of scope here): for accurate Received / In Progress / Inspected intermediate states, recipe nodes still need default_kind tagging (receiving / wet|bake|mask|rack / final_inspect). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../fusion_plating_jobs/__manifest__.py | 2 +- .../fusion_plating_jobs/models/fp_job.py | 6 ++-- .../models/fp_job_workflow_state.py | 29 +++++++++++++++++++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/fusion_plating/fusion_plating_jobs/__manifest__.py b/fusion_plating/fusion_plating_jobs/__manifest__.py index 0cbbd692..1600cdd0 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.8.20.2', + 'version': '19.0.8.20.3', '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.py b/fusion_plating/fusion_plating_jobs/models/fp_job.py index baf7005d..6647cdd9 100644 --- a/fusion_plating/fusion_plating_jobs/models/fp_job.py +++ b/fusion_plating/fusion_plating_jobs/models/fp_job.py @@ -115,11 +115,11 @@ class FpJob(models.Model): for job in self: passed = WS.browse() for ws in all_states: + # Highest-passed semantics: untagged / not-applicable + # states don't block the cascade. The bar reflects + # the furthest milestone the job has actually reached. if ws._fp_is_passed_for_job(job): passed = ws - else: - # First non-passed state stops the bar's progress - break job.workflow_state_id = passed # ------------------------------------------------------------------ diff --git a/fusion_plating/fusion_plating_jobs/models/fp_job_workflow_state.py b/fusion_plating/fusion_plating_jobs/models/fp_job_workflow_state.py index 9da46fc5..e00d0a86 100644 --- a/fusion_plating/fusion_plating_jobs/models/fp_job_workflow_state.py +++ b/fusion_plating/fusion_plating_jobs/models/fp_job_workflow_state.py @@ -141,6 +141,22 @@ class FpJobWorkflowState(models.Model): 'customer pickup) independently from plating steps.', ) + trigger_on_job_state = fields.Selection( + [ + ('confirmed', 'Job Confirmed'), + ('in_progress', 'Job In Progress'), + ('done', 'Job Done'), + ], + string='Trigger on Job State', + help='State-driven trigger: this milestone passes when ' + 'fp.job.state reaches AT LEAST the chosen value. Fallback ' + 'for jobs whose recipes do not tag steps with default_kind ' + '(so default_kind-driven triggers cannot fire). Order: ' + 'draft < confirmed < in_progress/on_hold < done. ' + 'Use for Confirmed ("confirmed") and optionally as a ' + 'safety-net for Done ("done").', + ) + block_when_quality_hold = fields.Boolean( string='Blocked by Quality Hold', default=False, @@ -198,6 +214,19 @@ class FpJobWorkflowState(models.Model): job.delivery_id and job.delivery_id.state == 'delivered' ) + # Special trigger: job.state reached ("at least" semantics). + if self.trigger_on_job_state: + order = { + 'draft': 0, + 'confirmed': 1, + 'in_progress': 2, + 'on_hold': 2, + 'done': 3, + } + required = order.get(self.trigger_on_job_state, -1) + actual = order.get(job.state, -1) + return required >= 0 and actual >= required + # Special trigger: first wet step started if self.trigger_first_step_started: wet_kinds = ('wet', 'bake', 'mask', 'rack')