feat(jobs+receiving): confirm->receive flow, lock recipe, reset step, lock steps, fix bake gate

- Confirm->Receive (A): after a single interactive SO confirm, receiving's
  action_confirm returns action_view_receiving() so the user lands straight
  on the Receive Parts screen (opt-out via fp_no_receiving_redirect context).
- Lock recipe (1): recipe_id readonly on the WO form — stick to the
  order-entry recipe.
- Hide spec (2): customer_spec_id invisible on the WO form.
- Reset step (3): new fp.job.step.button_reset (operator-usable, audited) +
  an undo button next to Start. Resets to Ready, clears finish + sign-off,
  closes open timelogs, keeps start audit + move/CoC history.
- Lock steps (4): steps list create=false delete=false (no Add a line / no
  trash) — steps come from the recipe, only skippable, never deleted.
- Bake gate fix (5): _fp_missing_required_step_inputs now honours the node's
  collect_measurements master switch, matching the Record-Inputs wizard.
  collect_measurements=False + required prompts no longer blocks finish
  (wizard shows 0 rows, so the gate must too). Unblocks WO-30098 + 63 other
  affected nodes (bake steps).

Deployed + verified on entech (-u jobs; bake finishes, reset done->ready,
recipe readonly, spec hidden, steps locked, receiving redirect target OK).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-06-04 16:55:34 -04:00
parent 197030a188
commit dcd4955bb7
4 changed files with 70 additions and 4 deletions

View File

@@ -486,6 +486,45 @@ class FpJobStep(models.Model):
step.state = 'cancelled'
return True
def button_reset(self):
"""Reset a step back to 'ready' so it can be redone — operator
self-serve for a mistake, an accidental skip, or a customer redo
request. Clears the finish + sign-off stamps and closes any open
timelog so the redo re-captures them; KEEPS the first-start audit
(date_started / started_by) and the move / CoC history intact.
Posts a chatter audit on the parent job. No-op on a step already
ready/pending (nothing to undo).
"""
now = fields.Datetime.now()
for step in self:
if step.state in ('ready', 'pending'):
continue
prev_label = dict(
step._fields['state'].selection
).get(step.state, step.state)
open_logs = step.time_log_ids.filtered(
lambda l: not l.date_finished)
if open_logs:
open_logs.write({'date_finished': now, 'state': 'stopped'})
vals = {'state': 'ready'}
if 'date_finished' in step._fields:
vals['date_finished'] = False
if 'finished_by_user_id' in step._fields:
vals['finished_by_user_id'] = False
if 'signoff_user_id' in step._fields:
vals['signoff_user_id'] = False
step.write(vals)
if step.job_id:
step.job_id.message_post(body=_(
'Step "%(s)s" reset to Ready (was %(p)s) by %(u)s '
'for redo.'
) % {
's': step.name or '?',
'p': prev_label,
'u': self.env.user.display_name,
})
return True
def write(self, vals):
"""Post a chatter trail on the parent JOB whenever an active
step gets reassigned. The step itself already tracks
@@ -846,6 +885,16 @@ class FpJobStep(models.Model):
Prompt = self.env['fusion.plating.process.node.input']
if not node:
return Prompt
# Master switch (Sub 12d): when the recipe node opts OUT of
# measurement collection, the Record-Inputs wizard returns ZERO
# rows (fp_job_step_input_wizard.default_get). The finish gate MUST
# agree — otherwise required prompts are demanded with no way to
# enter them and the step is permanently stuck (bake nodes with
# collect_measurements=False but required prompts — WO-30098 + 63
# others on entech). Honour the switch here so gate <=> wizard.
if ('collect_measurements' in node._fields
and not node.collect_measurements):
return Prompt
prompts = node.input_ids
if 'kind' in prompts._fields:
prompts = prompts.filtered(lambda i: i.kind == 'step_input')