diff --git a/fusion_plating/fusion_plating_jobs/__manifest__.py b/fusion_plating/fusion_plating_jobs/__manifest__.py index 1600cdd0..1c0fb325 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.3', + 'version': '19.0.8.20.4', '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 5eee6827..e5b212f3 100644 --- a/fusion_plating/fusion_plating_jobs/models/fp_job_step.py +++ b/fusion_plating/fusion_plating_jobs/models/fp_job_step.py @@ -174,6 +174,18 @@ class FpJobStep(models.Model): '(state=%s) by %s.' )) % (step.name, old_name, new_name, step.state, self.env.user.name)) + + # Auto-promote parent job: confirmed → in_progress on first + # step start. Without this, fp.job.state never reaches + # 'in_progress' anywhere in the codebase, so the In Progress + # workflow milestone (trigger_on_job_state='in_progress') + # never fires. + if vals.get('state') == 'in_progress': + jobs_to_promote = self.mapped('job_id').filtered( + lambda j: j.state == 'confirmed' + ) + if jobs_to_promote: + jobs_to_promote.write({'state': 'in_progress'}) return result @api.model 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 e00d0a86..e9e73133 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 @@ -227,18 +227,28 @@ class FpJobWorkflowState(models.Model): actual = order.get(job.state, -1) return required >= 0 and actual >= required - # Special trigger: first wet step started + # Special trigger: first wet step started. + # For tagged recipes (any step has kind in wet/bake/mask/rack), + # use strict kind-based check. For untagged recipes (all steps + # are kind='other'), fall back to "any step started" so the + # milestone fires regardless of recipe authoring quality. if self.trigger_first_step_started: wet_kinds = ('wet', 'bake', 'mask', 'rack') - production_started = any( - s.state in ('in_progress', 'paused', 'done') - and (s.kind in wet_kinds) - for s in steps - ) - if not production_started: - return False - # Production milestone — not blocked by quality hold here - return True + has_kind_tagging = any(s.kind in wet_kinds for s in steps) + if has_kind_tagging: + production_started = any( + s.state in ('in_progress', 'paused', 'done') + and (s.kind in wet_kinds) + for s in steps + ) + else: + # Untagged recipe — any started step counts as + # "production has started". + production_started = any( + s.state in ('in_progress', 'paused', 'done') + for s in steps + ) + return production_started # Standard trigger: ALL recipe steps matching the trigger # (default_kind in our list OR per-node override pointing at