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:
@@ -3,7 +3,7 @@
|
|||||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||||
{
|
{
|
||||||
'name': 'Fusion Plating — Native Jobs',
|
'name': 'Fusion Plating — Native Jobs',
|
||||||
'version': '19.0.8.20.2',
|
'version': '19.0.8.20.3',
|
||||||
'category': 'Manufacturing/Plating',
|
'category': 'Manufacturing/Plating',
|
||||||
'summary': 'Native plating job model — replaces mrp.production / mrp.workorder bridge.',
|
'summary': 'Native plating job model — replaces mrp.production / mrp.workorder bridge.',
|
||||||
'author': 'Nexa Systems Inc.',
|
'author': 'Nexa Systems Inc.',
|
||||||
|
|||||||
@@ -115,11 +115,11 @@ class FpJob(models.Model):
|
|||||||
for job in self:
|
for job in self:
|
||||||
passed = WS.browse()
|
passed = WS.browse()
|
||||||
for ws in all_states:
|
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):
|
if ws._fp_is_passed_for_job(job):
|
||||||
passed = ws
|
passed = ws
|
||||||
else:
|
|
||||||
# First non-passed state stops the bar's progress
|
|
||||||
break
|
|
||||||
job.workflow_state_id = passed
|
job.workflow_state_id = passed
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|||||||
@@ -141,6 +141,22 @@ class FpJobWorkflowState(models.Model):
|
|||||||
'customer pickup) independently from plating steps.',
|
'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(
|
block_when_quality_hold = fields.Boolean(
|
||||||
string='Blocked by Quality Hold',
|
string='Blocked by Quality Hold',
|
||||||
default=False,
|
default=False,
|
||||||
@@ -198,6 +214,19 @@ class FpJobWorkflowState(models.Model):
|
|||||||
job.delivery_id and job.delivery_id.state == 'delivered'
|
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
|
# Special trigger: first wet step started
|
||||||
if self.trigger_first_step_started:
|
if self.trigger_first_step_started:
|
||||||
wet_kinds = ('wet', 'bake', 'mask', 'rack')
|
wet_kinds = ('wet', 'bake', 'mask', 'rack')
|
||||||
|
|||||||
Reference in New Issue
Block a user