"""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') # ---- 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 ===')