fix(contract-review): WO step routes to QA-005 + auto-stage on part create

Two bugs fixed in one drop, both targeting the contract review (QA-005)
enforcement gap reported on entech.

## Bug 1 — WO step routed to wrong wizard

Symptom: clicking Finish & Next or Record on a Contract Review step in
WH/JOB/00339 opened the generic measurement wizard with three fake
prompts (Reviewer Initials / Date Reviewed / QA-005 Approved). No path
to the actual QA-005 form from the work order.

Root cause: action_finish_and_advance + action_open_input_wizard had no
branch for recipe_node.default_kind == 'contract_review'. The step.kind
mapping collapses contract_review -> 'other' so kind-based detection
wouldn't have worked either; gate has to live at the recipe-node layer.

Fix in fusion_plating_jobs/models/fp_job_step.py (v19.0.8.14.6):
- action_finish_and_advance:329 calls _fp_contract_review_redirect
  before the input-wizard branch
- action_open_input_wizard:844 same gate, keeps Record button consistent
- _fp_contract_review_redirect:866 (new) returns the part's
  action_start_contract_review() unless review.state in
  (complete, dismissed) — gate clears so the step can finish after
  the operator signs QA-005.

## Bug 2 — Part create did not enforce contract review

Symptom: spec called for a banner-only UX. User wanted true automatic
enforcement on first part creation under an enforced customer.

Fix in fusion_plating_quality/models/fp_part_catalog.py (v19.0.4.10.0):
- @api.model_create_multi def create() override
- _fp_enforce_contract_review_on_create() helper auto-stages the
  fp.contract.review record AND surfaces three prominent reminders:
    1. Sticky bus.bus warning toast (top-right, doesn't auto-dismiss)
    2. mail.activity (To Do) on the part for the current user
    3. Smart button on the part form lights up (review now exists)
- Idempotent: skips parts that already carry a review id
- Soft-fails: bus or activity outage doesn't block part creation
- create()-only — write/update flows never re-trigger

Sub 4's existing info banner stays as a fourth surface.

## Tests

- fusion_plating_jobs/tests/test_fp_job_extensions.py:
  +TestContractReviewStepRouting (5 tests covering both routing methods,
  the complete/dismissed gate-clear, and non-CR step regression)
- fusion_plating_quality/tests/test_part_catalog_contract_review_enforcement.py
  (NEW): 9 tests covering auto-create, batch create, idempotency,
  activity surface, bus surface, write-must-not-retrigger, soft-fail.
- docs/superpowers/tests/2026-04-22-sub4-smoke.py: flipped the
  "no review yet" assertion to "review auto-created" to match new
  behavior. Sign-flow assertions unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-05-03 20:02:52 -04:00
parent 1da27ed6bf
commit ee80673579
8 changed files with 584 additions and 9 deletions

View File

@@ -342,6 +342,15 @@ class FpJobStep(models.Model):
"Step '%s' is in state '%s' — start it before clicking Finish."
) % (self.name, self.state))
# Contract Review (QA-005) routing — when the recipe step is
# flagged as a contract-review step, the operator should land on
# the part's QA-005 form rather than the generic measurement
# wizard. Once the review is complete or dismissed we fall
# through to the normal finish path so the step can advance.
cr_action = self._fp_contract_review_redirect()
if cr_action:
return cr_action
# Prompt-first behaviour: show the Record Inputs dialog when the
# recipe step has authored prompts and nothing has been captured
# in this run. Bypass when context flag is set (i.e. we're being
@@ -833,8 +842,16 @@ class FpJobStep(models.Model):
}
def action_open_input_wizard(self):
"""Open the Input Recording wizard for this step."""
"""Open the Input Recording wizard for this step.
Contract-review steps redirect to the QA-005 form (same gate as
action_finish_and_advance) so the per-row "Record" button stays
consistent with "Finish & Next".
"""
self.ensure_one()
cr_action = self._fp_contract_review_redirect()
if cr_action:
return cr_action
return {
'type': 'ir.actions.act_window',
'res_model': 'fp.job.step.input.wizard',
@@ -846,6 +863,41 @@ class FpJobStep(models.Model):
},
}
def _fp_contract_review_redirect(self):
"""Return an ir.actions.act_window opening the part's QA-005
Contract Review form, or False to indicate "no redirect needed".
Triggers when:
* the recipe node is flagged default_kind='contract_review', AND
* the linked part has no review yet OR the review is still in
a non-terminal state (draft / assistant_review / manager_review).
Once the review reaches state 'complete' or 'dismissed' the step
is allowed to finish through the normal path, which is how the
operator clears the contract-review gate after signing QA-005.
Soft-fail: if the job has no part_catalog_id we cannot route to
a per-part review, so we fall through to the standard wizard
rather than blocking the operator.
"""
self.ensure_one()
node = self.recipe_node_id
if not node or node.default_kind != 'contract_review':
return False
part = self.job_id.part_catalog_id
if not part:
_logger.warning(
"Contract-review step '%s' on job %s has no part_catalog_id "
"— cannot redirect to QA-005 form, falling through to "
"standard wizard.",
self.name, self.job_id.name,
)
return False
review = part.x_fc_contract_review_id
if review and review.state in ('complete', 'dismissed'):
return False
return part.action_start_contract_review()
# ------------------------------------------------------------------
# Live duration helper — view binds to a non-stored compute that
# ticks each time the form re-reads. For a true live ticking clock