The qty gate I added refused Finish on steps where qty_at_step > 0,
to force operators to move parts forward first. But the first-step
seed in _compute_qty_at_step gives the earliest non-terminal step
a notional qty = job.qty — a UI hint, not actual parked parts.
Paperwork steps (Contract Review, Inspection-by-paperwork, etc.)
sit on that seed, and the gate was blocking Finish with a misleading
error.
Fixes:
- button_finish gate now checks for REAL incoming moves before
refusing. Seed-only qty (no incoming_move_ids filtered to non-
self-loop) is exempt.
- _fp_record_one_piece_auto_move detects seed-only qty and bulk-
moves ALL parts in one shot to the downstream step. Correct for
paperwork / first steps where parts don't physically wait
per-piece — one click finishes the paperwork and pushes the whole
batch forward.
For steps with REAL incoming moves (parts actually moved here via
a Move record), the original gate semantics still apply: qty == 1
auto-moves one part; qty > 1 raises with the "use Complete 1 → Next
or Move…" message.
Verified on entech: Contract Review with seed qty=6 now finishes
cleanly, bulk-moving all 6 parts to the next step in one move.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>