fix(jobs): workflow bar stuck at Draft for confirmed/done jobs

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) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-05-12 00:05:05 -04:00
parent 1c68fd0555
commit 89dd77aff2
3 changed files with 33 additions and 4 deletions

View File

@@ -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.',

View File

@@ -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
# ------------------------------------------------------------------

View File

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