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')