From d6bda9740fec3f4d0748ac69872915647c54ea3e Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Tue, 12 May 2026 00:34:19 -0400 Subject: [PATCH] fix(jobs): Received workflow milestone keys off pre-recipe receiving MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Received milestone was tied to recipe steps tagged default_kind='receiving'. But receiving in this system is a pre- recipe inbound logistics flow (fp.receiving model in fusion_plating_receiving). When parts physically arrive, the flow sets sale_order.x_fc_receiving_status to partial or received. Changes: - New trigger_on_parts_received Boolean on fp.job.workflow.state. - _fp_is_passed_for_job branch: passes when sale_order's x_fc_receiving_status is in (partial, received). - _compute_workflow_state_id depends extended with sale_order_id.x_fc_receiving_status so the bar recomputes automatically when the receiving flow updates the SO. - DB seed update: Received state drops trigger_default_kinds= 'receiving' and gains trigger_on_parts_received=True. Verified end-to-end on entech: bar moves Confirmed → Received on status change, regresses on rollback, accepts both 'partial' and 'received' as satisfying the milestone. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../fusion_plating_jobs/__manifest__.py | 2 +- .../fusion_plating_jobs/models/fp_job.py | 2 ++ .../models/fp_job_workflow_state.py | 23 +++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/fusion_plating/fusion_plating_jobs/__manifest__.py b/fusion_plating/fusion_plating_jobs/__manifest__.py index 1c0fb325..fed43300 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.4', + 'version': '19.0.8.20.5', '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 6647cdd9..de74b609 100644 --- a/fusion_plating/fusion_plating_jobs/models/fp_job.py +++ b/fusion_plating/fusion_plating_jobs/models/fp_job.py @@ -108,6 +108,8 @@ class FpJob(models.Model): 'quality_hold_count', 'delivery_id', 'delivery_id.state', + 'sale_order_id', + 'sale_order_id.x_fc_receiving_status', ) def _compute_workflow_state_id(self): WS = self.env['fp.job.workflow.state'] 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 e9e73133..424cb6bb 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 @@ -157,6 +157,20 @@ class FpJobWorkflowState(models.Model): 'safety-net for Done ("done").', ) + trigger_on_parts_received = fields.Boolean( + string='Trigger on Parts Received', + default=False, + help='Special trigger — passes once the job\'s sale order has ' + 'a receiving status of "partial" or "received" (set by ' + 'the fp.receiving inbound logistics flow). Used by the ' + 'Received milestone in lieu of recipe-side ' + 'default_kind="receiving" tagging. Receiving is a pre-' + 'recipe activity (parts physically arrive on the dock) — ' + 'keeping the trigger off recipe steps means shops without ' + 'a "Receiving" step in their recipe still see the bar ' + 'advance correctly.', + ) + block_when_quality_hold = fields.Boolean( string='Blocked by Quality Hold', default=False, @@ -214,6 +228,15 @@ class FpJobWorkflowState(models.Model): job.delivery_id and job.delivery_id.state == 'delivered' ) + # Special trigger: parts physically received (pre-recipe). + if self.trigger_on_parts_received: + so = job.sale_order_id + if (so and 'x_fc_receiving_status' in so._fields + and so.x_fc_receiving_status + in ('partial', 'received')): + return True + return False + # Special trigger: job.state reached ("at least" semantics). if self.trigger_on_job_state: order = {