fix(fusion_plating_shopfloor): partial advance blocked by from-step predecessor
The Move dialog's predecessor check flagged every unfinished step before the destination — including the from_step itself, which is in-progress by definition when advancing partial parts out of it. So any "Send → next" to a not-yet-started step showed a hard "Predecessor not done: <from_step>" blocker and greyed out SEND (reproduced on WO-30061: Racking → Ready for processing). This broke partial advance for ALL quantities, not just 1-part orders. Fix: _blockers_for_move only blocks unfinished steps STRICTLY BETWEEN from_step and to_step (you'd be skipping an incomplete intermediate stage). Immediate-next advance is allowed; skip-ahead still blocked; backward (rework) moves unblocked. Verified on entech: blocker no longer fires for Racking → Ready for processing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1849,7 +1849,7 @@ A 50-part job can have parts at several stages at once (10 Masking, 20 Plating,
|
||||
|
||||
4. **Partial-flow "light up" lives in `move_controller._do_move_parts_commit` / `_do_move_rack_commit`:** a forward (`transfer_type='step'`) move (a) flips the destination step `pending → ready` so the receiving operator gets an actionable card with no action by anyone, and (b) calls `from_step._fp_try_autofinish_on_drain()` (best-effort, swallows finish-gate UserErrors). It does **not** auto-START the destination — `button_start` stays explicit to keep the labour timer accurate (S16). No auto-ready/auto-finish for hold/scrap/rework moves.
|
||||
|
||||
5. **The predecessor gate is qty-aware: `_fp_should_block_predecessors()` returns False once `_fp_has_real_incoming()` is true** (an incoming move from a different step with `qty_moved > 0`). A step with parts physically parked at it is startable regardless of whether upstream steps are fully done. This is the single source of truth shared by `can_start`, `_compute_blocker`, `button_start`, and the Move dialog's `_blockers_for_move`. **Don't "fix" the predecessor gate back to pure sequence-based** — it would re-lock the next stage while the rest of the batch is still upstream.
|
||||
5. **The predecessor gate is qty-aware: `_fp_should_block_predecessors()` returns False once `_fp_has_real_incoming()` is true** (an incoming move from a different step with `qty_moved > 0`). A step with parts physically parked at it is startable regardless of whether upstream steps are fully done. This is the single source of truth shared by `can_start`, `_compute_blocker`, `button_start`, and the Move dialog's `_blockers_for_move`. **Don't "fix" the predecessor gate back to pure sequence-based** — it would re-lock the next stage while the rest of the batch is still upstream. **Second, distinct trap (fixed 2026-06-02): the Move dialog's `_blockers_for_move` predecessor check must only flag unfinished steps STRICTLY BETWEEN `from_step` and `to_step` (`from_step.sequence < s.sequence < to_step.sequence`), NOT all steps before `to_step`.** The original `s.sequence < to_step.sequence` filter counted the `from_step` itself (which is in-progress *by definition* when you advance partial parts out of it) as an "unfinished predecessor" of the destination — so EVERY partial advance to a not-yet-started next step showed a hard "Predecessor not done: \<from_step\>" blocker and greyed out SEND (hit on WO-30061). The between-only rule allows the immediate-next advance, still blocks skip-ahead moves over incomplete intermediate stages, and leaves backward (rework) moves unblocked (empty range).
|
||||
|
||||
6. **Move-based scrap (`transfer_type='scrap'`) does NOT touch `job.qty_scrapped`.** At close, `button_mark_done` calls `_fp_scrapped_via_moves()` and folds it into `qty_scrapped`, then auto-fills `qty_done = qty − qty_scrapped` (was: blindly `= job.qty`, which over-counted when parts were scrapped). The reconciliation gate is still the safety net.
|
||||
|
||||
|
||||
@@ -124,8 +124,18 @@ class FpTabletMoveController(http.Controller):
|
||||
hasattr(to_step, '_fp_should_block_predecessors')
|
||||
and to_step._fp_should_block_predecessors()
|
||||
):
|
||||
# Partial-flow (2026-06-02): only an unfinished step STRICTLY
|
||||
# BETWEEN from_step and to_step blocks the move (you'd be skipping
|
||||
# an incomplete intermediate stage). The from_step itself is
|
||||
# in-progress BY DEFINITION when advancing partial parts out of
|
||||
# it — counting it (or any earlier step) as an "unfinished
|
||||
# predecessor" blocked every partial advance to a not-yet-started
|
||||
# next step. Steps before from_step are irrelevant: the parts
|
||||
# being moved are physically at from_step, ready for the next
|
||||
# stage. Backward moves (rework: from > to) yield an empty range
|
||||
# and are never predecessor-blocked.
|
||||
unfinished = to_step.job_id.step_ids.filtered(
|
||||
lambda s: s.sequence < to_step.sequence
|
||||
lambda s: from_step.sequence < s.sequence < to_step.sequence
|
||||
and s.state not in ('done', 'skipped', 'cancelled')
|
||||
)
|
||||
if unfinished:
|
||||
|
||||
Reference in New Issue
Block a user