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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user