feat(jobs): button_start auto-routes contract_review steps to QA-005

User feedback on WH/JOB/00341 (S00279 retest): clicking Start on
the Contract Review step changed state to in_progress but didn't
take them to QA-005. They had to then click Finish & Next twice
to land on the form — confusing flow.

Better UX: when an operator clicks Start on a step where
recipe_node.default_kind='contract_review', the step starts AND
the QA-005 form opens immediately. Operator signs/dismisses,
navigates back, hits Finish & Next once → step finishes + advances.

Implementation:
  fp.job.step.button_start, after super() returns and the
  receiving check runs, calls _fp_contract_review_redirect()
  (existing helper). If it returns an action, return that
  instead of the parent's result. Single-record only — bulk
  button_start (job-level start-all) shouldn't navigate.

Helper logic unchanged — same gate matrix:
  * recipe_node.default_kind == 'contract_review'
  * job has part_catalog_id
  * review state NOT in (complete, dismissed)

When review is already complete, the gate clears: button_start
returns the normal True so the operator can advance the step
without bouncing through QA-005 again.

Tests:
  test_button_start_routes_cr_step_to_qa005 — start opens QA-005
  test_button_start_does_not_route_when_review_complete — start
    does NOT redirect once review is signed off

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-05-03 22:33:55 -04:00
parent d53fd53b80
commit 21e42e7b48
3 changed files with 56 additions and 1 deletions

View File

@@ -3,7 +3,7 @@
# License OPL-1 (Odoo Proprietary License v1.0) # License OPL-1 (Odoo Proprietary License v1.0)
{ {
'name': 'Fusion Plating — Native Jobs', 'name': 'Fusion Plating — Native Jobs',
'version': '19.0.8.17.0', 'version': '19.0.8.17.1',
'category': 'Manufacturing/Plating', 'category': 'Manufacturing/Plating',
'summary': 'Native plating job model — replaces mrp.production / mrp.workorder bridge.', 'summary': 'Native plating job model — replaces mrp.production / mrp.workorder bridge.',
'author': 'Nexa Systems Inc.', 'author': 'Nexa Systems Inc.',

View File

@@ -148,6 +148,20 @@ class FpJobStep(models.Model):
'so': so.name or '', 'so': so.name or '',
'status': recv or 'unknown', 'status': recv or 'unknown',
}) })
# Sub 12e v4 — when an operator starts a contract_review step,
# immediately route to the QA-005 form. The step stays
# in_progress in the background; the operator signs (or
# dismisses) the review on QA-005, navigates back to the job,
# then clicks Finish & Next to advance. This removes the
# earlier "click Start, then click Finish & Next, then maybe
# click Finish & Next again" friction.
# Single-record only — multi-record button_start (e.g. job
# bulk-start) shouldn't navigate.
if len(self) == 1:
cr_action = self._fp_contract_review_redirect()
if cr_action:
return cr_action
return result return result
def button_pause(self): def button_pause(self):

View File

@@ -827,6 +827,47 @@ class TestContractReviewStepRouting(TransactionCase):
% action, % action,
) )
def test_button_start_routes_cr_step_to_qa005(self):
"""Sub 12e v4 UX — clicking Start on a contract_review step
should set state=in_progress AND immediately return the QA-005
action so the operator lands on the form without needing to
click Finish & Next."""
# Use the setUp's CR step but reset state to 'ready' so we
# can call button_start. Skip the predecessor gate since
# there are no other steps in this minimal test job.
self.step.state = 'ready'
action = self.step.with_context(
fp_skip_predecessor_check=True,
).button_start()
# State must transition
self.assertEqual(self.step.state, 'in_progress')
# And the action returned must be the QA-005 form
self.assertTrue(
self._is_contract_review_action(action),
'button_start on a contract_review step must return the '
'QA-005 action, got: %r' % action,
)
def test_button_start_does_not_route_when_review_complete(self):
"""If the QA-005 review is already complete, button_start
on the CR step should NOT redirect — operator just starts
the step normally and clicks Finish & Next when ready."""
review = self.env['fp.contract.review'].create({
'part_id': self.part.id,
'state': 'complete',
})
self.part.x_fc_contract_review_id = review.id
self.step.state = 'ready'
action = self.step.with_context(
fp_skip_predecessor_check=True,
).button_start()
self.assertEqual(self.step.state, 'in_progress')
self.assertFalse(
self._is_contract_review_action(action),
'When review is complete, button_start must NOT redirect '
'(the gate is closed). Got: %r' % action,
)
class TestSequentialEnforcement(TransactionCase): class TestSequentialEnforcement(TransactionCase):
"""Sub 13 — recipe-level + per-step sequential enforcement. """Sub 13 — recipe-level + per-step sequential enforcement.