fix(workspace): required-inputs gate fires + manager bypass dialog

Two bugs:

1. Gate silently passed when step.recipe_node_id was NULL — happened
   to every WO-30057 step after this morning's clone delete (the FK
   ON DELETE SET NULL wiped the link). _fp_missing_required_step_inputs
   returned an empty recordset when node was None, so the gate had
   nothing to fail on and button_finish succeeded with zero audit.
   Fix: _fp_check_step_inputs_complete now treats NULL recipe_node_id
   as an explicit "no recipe link" hard block. Operator can't finish;
   manager bypass posts chatter audit.

2. No tablet UI for the manager bypass. The gate's bypass was a
   Python context flag — invisible from the JS layer, so managers
   were stuck behind the same hard error as operators.
   Fix: new /fp/workspace/finish_step endpoint returns structured
   errors (gate type, missing_prompts list, bypass_available bool).
   Server-side enforces manager group when bypass=True (can't trust
   the client). New FpFinishBlockDialog OWL modal renders:
   - Non-manager: Cancel + Record Inputs
   - Manager:     Cancel + Record Inputs + ⚠ Bypass & Finish (audit)

JobWorkspace.onFinishStep routes plain finishes through the new
endpoint; signature-required steps still go through /fp/workspace/sign_off
(separate gate). Added is_manager to /fp/workspace/load payload so
the JS knows which dialog variant to render.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-05-24 19:30:39 -04:00
parent 0371624afb
commit fc17754996
8 changed files with 319 additions and 10 deletions

View File

@@ -797,6 +797,13 @@ class FpJobStep(models.Model):
per-step data trail; finishing a step with missing prompts breaks
the audit chain.
2026-05-24: also blocks orphaned steps (recipe_node_id NULL —
happens when the source recipe was deleted, e.g. a per-part clone
cleanup). Without a recipe link there's no way to verify required
prompts; defaulting to "let it through" was a silent compliance
gap. Managers can bypass via the same flag, audit chatter records
the override.
Manager bypass via context fp_skip_required_inputs_gate=True
(e.g. paper-form catch-up or documented customer deviation).
Bypasses are posted to chatter naming the user.
@@ -809,6 +816,17 @@ class FpJobStep(models.Model):
)) % (step.name, self.env.user.name))
return
for step in self:
# Orphan-step block — NULL recipe_node means we can't list
# required prompts, so we conservatively refuse to finish.
if not step.recipe_node_id:
raise UserError(_(
'Step "%(step)s" cannot be finished — this step has '
'no recipe link (the source recipe was deleted or the '
'job was created before recipes were assigned). '
'Required-input verification is impossible without '
'the recipe. Escalate to a manager — they can bypass '
'with an audit-chatter entry.'
) % {'step': step.name})
missing = step._fp_missing_required_step_inputs()
if not missing:
continue