feat(jobs+shopfloor): live-step priority chain + done-job filter
Fix the Shop Floor plant kanban so cards land in the right column: - fp.job._compute_active_step_id walks priority chain (in_progress > paused > ready > pending), not just in_progress - fp.job._compute_card_state edge case respects job.state='done' (no more bogus 'contract_review' label on done jobs) - fp.job.step._compute_area_kind reads kind.area_kind directly; legacy _STEP_KIND_TO_AREA dict removed (50+ lines deleted) - /fp/landing/plant_kanban filters out done/cancelled jobs from the live board Migration 19.0.10.25.0 backfills template metadata (codes, descriptions, icons, kind_id) on 30 unfinished library templates and repoints recipe nodes for 6 unambiguous name patterns (Blasting -> blast, Ready For X -> gating, De-Masking -> demask, Scheduling -> gating, Nickel Strip -> wet_process, Pre-Meas/Check Sulfamate -> inspect). Battle test bt_s24_between_steps.py covers between-step routing, paused step lifecycle, and done-job board filter. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -257,13 +257,21 @@ class FpJob(models.Model):
|
||||
def _compute_card_state(self):
|
||||
"""Dispatch matching spec §6.2 / §9.3 explicit precedence list."""
|
||||
for job in self:
|
||||
# Edge: no active step (all pending or all done)
|
||||
# Edge: no live step (all steps done OR no steps at all).
|
||||
# - job.state='done' → 'done' (defensive — done jobs are
|
||||
# filtered off the Shop Floor board upstream, but the
|
||||
# field still needs a value).
|
||||
# - confirmed + parts not yet received → 'no_parts'.
|
||||
# - else → 'ready' (job awaiting work, no steps yet OR
|
||||
# recipe not assigned).
|
||||
if not job.active_step_id:
|
||||
if (job.state == 'confirmed'
|
||||
if job.state == 'done':
|
||||
job.card_state = 'done'
|
||||
elif (job.state == 'confirmed'
|
||||
and job._fp_inbound_not_received()):
|
||||
job.card_state = 'no_parts'
|
||||
else:
|
||||
job.card_state = 'contract_review'
|
||||
job.card_state = 'ready'
|
||||
continue
|
||||
|
||||
step = job.active_step_id
|
||||
@@ -384,11 +392,32 @@ class FpJob(models.Model):
|
||||
|
||||
@api.depends('step_ids.state', 'step_ids.sequence')
|
||||
def _compute_active_step_id(self):
|
||||
"""Pick the "live" step — first match by priority then sequence.
|
||||
|
||||
Priority order:
|
||||
in_progress > paused > ready > first pending
|
||||
|
||||
in_progress is the most informative (someone is actively
|
||||
working on it). paused means someone was working and stopped —
|
||||
the card belongs at that station so the next operator can
|
||||
pick it up. ready is the next-up step waiting for an operator.
|
||||
The first pending after a done step is the "next gate" —
|
||||
where the card visually waits between steps.
|
||||
|
||||
Returns False only when every step is `done` (job finished)
|
||||
or when there are no steps at all (recipe not assigned).
|
||||
|
||||
See spec 2026-05-24-shopfloor-live-step-fix-design.md Change 1.
|
||||
"""
|
||||
PRIORITY_STATES = ('in_progress', 'paused', 'ready', 'pending')
|
||||
for job in self:
|
||||
active = job.step_ids.filtered(
|
||||
lambda s: s.state == 'in_progress'
|
||||
).sorted('sequence')
|
||||
job.active_step_id = active[:1].id if active else False
|
||||
ordered = job.step_ids.sorted('sequence')
|
||||
live = job.env['fp.job.step']
|
||||
for state in PRIORITY_STATES:
|
||||
live = ordered.filtered(lambda s: s.state == state)
|
||||
if live:
|
||||
break
|
||||
job.active_step_id = live[:1].id if live else False
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Sub 14 — Configurable workflow state (status bar milestone)
|
||||
|
||||
Reference in New Issue
Block a user