"""Sub 4 smoke test — runs inside odoo-shell on entech. Verifies: toggle-triggered banner, lazy review creation, roster-enforced sign flow, compute-driven auto-dismiss on confirmed MO, re-open flow, risk-band matrix. """ env = env # odoo-shell injects this Partner = env['res.partner'] Part = env['fp.part.catalog'] Review = env['fp.contract.review'] Users = env['res.users'] Company = env.company # ---- Roster: add admin to both rosters --------------------------------- admin = env.ref('base.user_admin') Company.write({ 'x_fc_qa_assistant_user_ids': [(6, 0, [admin.id])], 'x_fc_qa_manager_user_ids': [(6, 0, [admin.id])], }) assert admin in Company.x_fc_qa_assistant_user_ids assert admin in Company.x_fc_qa_manager_user_ids print('[OK] Company rosters populated') # ---- Customer with contract_review_required ---------------------------- cust = Partner.create({ 'name': 'Sub4 Smoke Customer', 'is_company': True, 'customer_rank': 1, 'x_fc_contract_review_required': True, }) assert cust.x_fc_contract_review_required print('[OK] Customer toggle set') # ---- Part under that customer → banner visible ------------------------- part = Part.create({ 'partner_id': cust.id, 'part_number': 'SUB4-SMOKE-001', 'revision': 'A', }) part.invalidate_recordset() assert part.x_fc_customer_requires_contract_review assert part.x_fc_contract_review_banner_visible, 'banner should be visible' assert not part.x_fc_contract_review_id print('[OK] Banner visible on fresh part') # ---- Start contract review → record created + state = assistant_review action = part.action_start_contract_review() part.invalidate_recordset() review = part.x_fc_contract_review_id assert review, 'review should be created' assert review.state == 'assistant_review' assert review.customer_id == cust assert review.part_number == 'SUB4-SMOKE-001' print('[OK] Review created by action_start_contract_review') # ---- Sign section 2.0 as admin (roster ok) ----------------------------- review.with_user(admin).action_sign_section_20() review.invalidate_recordset() assert review.s20_locked assert review.s20_signed_by == admin assert review.state == 'manager_review' print('[OK] Section 2.0 signed; state advanced') # ---- Sign section 3.0 → state = complete ------------------------------- review.s30_risk_consequence = '4' review.s30_risk_likelihood = '4' assert review.s30_risk_band == 'red', f'expected red, got {review.s30_risk_band}' review.with_user(admin).action_sign_section_30() review.invalidate_recordset() assert review.s30_locked assert review.state == 'complete' print('[OK] Section 3.0 signed; review complete; risk matrix computed') # ---- Banner now hidden (review complete) ------------------------------- part.invalidate_recordset() assert not part.x_fc_contract_review_banner_visible print('[OK] Banner hidden after completion') # ---- Re-open (admin is manager) ---------------------------------------- review.action_reopen() review.invalidate_recordset() assert review.state == 'assistant_review' assert not review.s20_locked assert not review.s30_locked print('[OK] Re-open cleared both sections') # ---- Risk matrix parity with paper form -------------------------------- expected = { (1, 1): 'green', (1, 2): 'green', (1, 3): 'green', (1, 4): 'green', (1, 5): 'green', (2, 1): 'green', (2, 2): 'green', (2, 3): 'yellow', (2, 4): 'yellow', (2, 5): 'yellow', (3, 1): 'green', (3, 2): 'yellow', (3, 3): 'yellow', (3, 4): 'yellow', (3, 5): 'red', (4, 1): 'yellow', (4, 2): 'yellow', (4, 3): 'yellow', (4, 4): 'red', (4, 5): 'red', (5, 1): 'yellow', (5, 2): 'yellow', (5, 3): 'red', (5, 4): 'red', (5, 5): 'red', } for (c, l), band in expected.items(): review.s30_risk_consequence = str(c) review.s30_risk_likelihood = str(l) assert review.s30_risk_band == band, ( f'matrix mismatch at c={c} l={l}: got {review.s30_risk_band} expected {band}' ) print('[OK] 25-cell risk matrix matches paper form') # ---- Dismiss flow ------------------------------------------------------ part2 = Part.create({ 'partner_id': cust.id, 'part_number': 'SUB4-SMOKE-002', 'revision': 'A', }) part2.invalidate_recordset() assert part2.x_fc_contract_review_banner_visible part2.action_dismiss_contract_review() part2.invalidate_recordset() assert part2.x_fc_contract_review_dismissed assert not part2.x_fc_contract_review_banner_visible print('[OK] Dismiss hides banner') # ---- Non-roster user blocked ------------------------------------------ demo_user = Users.search([('login', '=', 'demo')], limit=1) if demo_user: part3 = Part.create({ 'partner_id': cust.id, 'part_number': 'SUB4-SMOKE-003', 'revision': 'A', }) part3.action_start_contract_review() part3.invalidate_recordset() rev3 = part3.x_fc_contract_review_id try: rev3.with_user(demo_user).action_sign_section_20() assert False, 'non-roster user should have been blocked' except Exception as e: assert 'authorised' in str(e) or 'Plating Manager' in str(e) print('[OK] Non-roster user blocked') else: print('[SKIP] No demo user for non-roster check') # ---- Bulk-toggle (Check All / Clear All) buttons ---------------------- part4 = Part.create({ 'partner_id': cust.id, 'part_number': 'SUB4-SMOKE-004', 'revision': 'A', }) part4.action_start_contract_review() part4.invalidate_recordset() rev4 = part4.x_fc_contract_review_id rev4.action_check_all_section_20() for f in rev4._SECTION_20_CHECKLIST: assert rev4[f] is True, f'{f} should be True after Check All' print('[OK] Section 2.0 Check All ticks all 10 boxes') rev4.action_clear_all_section_20() for f in rev4._SECTION_20_CHECKLIST: assert rev4[f] is False, f'{f} should be False after Clear All' print('[OK] Section 2.0 Clear All clears all 10 boxes') rev4.action_check_all_section_30() for f in rev4._SECTION_30_CHECKLIST: assert rev4[f] is True, f'{f} should be True after Check All' print('[OK] Section 3.0 Check All ticks all 11 boxes') rev4.action_clear_all_section_30() for f in rev4._SECTION_30_CHECKLIST: assert rev4[f] is False, f'{f} should be False after Clear All' print('[OK] Section 3.0 Clear All clears all 11 boxes') # Lock section 2.0, bulk toggle should refuse rev4.with_user(admin).action_sign_section_20() try: rev4.action_check_all_section_20() assert False, 'bulk toggle should fail on locked section' except Exception as e: assert 'locked' in str(e).lower() or 'signed' in str(e).lower() print('[OK] Bulk toggle blocked on locked section') # ---- QWeb render ------------------------------------------------------- report = env.ref('fusion_plating_quality.action_report_contract_review') pdf_bytes, mime = report._render_qweb_pdf('fusion_plating_quality.report_contract_review_qa005', [review.id]) assert pdf_bytes and pdf_bytes[:4] == b'%PDF' print(f'[OK] QA-005 PDF rendered ({len(pdf_bytes)} bytes)') # ---- Cleanup ----------------------------------------------------------- env.cr.rollback() print('\n=== SUB 4 SMOKE PASS — all assertions held ===')