# -*- coding: utf-8 -*- # Sub 12 Phase F — end-to-end smoke test. # # Run via odoo-shell: # /usr/bin/odoo shell -c /etc/odoo/odoo.conf -d admin # >>> exec(open('/mnt/extra-addons/custom/fusion_plating_quality/scripts/sub12_smoke_test.py').read()) # # Walks the full Sub 12 lifecycle and asserts at each step. import logging _log = logging.getLogger(__name__) def _resolve_test_partner(env): """Pick a partner with at least one sale order so the RMA can bind.""" so = env['sale.order'].search([('state', 'in', ('sale', 'done'))], limit=1) if not so: raise RuntimeError('No confirmed sale.order found — seed one first.') return so.partner_id, so def smoke(): e = env # noqa -- env injected by odoo-shell print('--- Sub 12 smoke test ---') # Reset any stale half-completed test artefacts so the script is idempotent. e['fusion.plating.rma'].search([('name', 'like', 'RMA/SUB12-SMOKE-%')]).unlink() partner, so = _resolve_test_partner(e) print(f' Using partner: {partner.display_name} (SO {so.name})') # 1. Create RMA (draft) rma = e['fusion.plating.rma'].create({ 'partner_id': partner.id, 'sale_order_id': so.id, 'sale_order_line_ids': [(6, 0, so.order_line[:1].ids)] if so.order_line else False, 'trigger_source': 'customer_complaint', 'severity': 'high', 'qty_returned': 5, 'complaint_description': '

Smoke-test RMA. Auto-issued for Sub 12 verification.

', }) print(f' ✓ Created RMA {rma.name} (state={rma.state})') assert rma.state == 'draft', f'Expected draft, got {rma.state}' # 2. Authorise rma.action_authorise() assert rma.state == 'authorised' print(f' ✓ Authorised — state={rma.state}, qr_code present={bool(rma.qr_code)}') # 3. Mark shipped rma.action_mark_shipped_to_us() assert rma.state == 'shipped_to_us' print(f' ✓ Customer shipped — state={rma.state}') # 4. Auto-receive via fp.receiving create. The RMA-link create-hook # walks the receiving from draft → counted → staged → closed in one # shot so the SO's x_fc_receiving_status flips to "received". receiving = e['fp.receiving'].create({ 'sale_order_id': so.id, 'rma_id': rma.id, 'box_count_in': 1, 'expected_qty': 5, 'received_qty': 5, }) print(f' ✓ Created fp.receiving {receiving.name} → RMA state={rma.state}, recv state={receiving.state}, SO status={so.x_fc_receiving_status}') assert rma.state == 'received', f'Expected received, got {rma.state}' assert receiving.state == 'closed', f'Expected receiving closed, got {receiving.state}' assert so.x_fc_receiving_status == 'received', \ f'Expected SO status received, got {so.x_fc_receiving_status}' # 5. Verify auto-spawn fired assert rma.linked_ncr_ids, 'Auto-NCR was not spawned' assert rma.linked_hold_ids, 'Auto-Hold was not spawned' ncr = rma.linked_ncr_ids[0] hold = rma.linked_hold_ids[0] print(f' ✓ Auto-spawned NCR {ncr.name} + Hold {hold.name}') # 6. Set resolution + triage rma.resolution_type = 'rework' rma.action_triage_complete() assert rma.state == 'triaged' print(f' ✓ Triage complete — state={rma.state}, resolution=rework') # 7. Start resolving rma.action_start_resolving() assert rma.state == 'resolving' print(f' ✓ Resolving — state={rma.state}') # 8. NCR walk: open → containment → root cause → close ncr.action_open() ncr.action_containment() ncr.containment = '

Smoke-test: parts segregated for re-rack.

' ncr.action_disposition() ncr.disposition = 'rework' ncr.root_cause = '

Smoke-test: rack contact loss during transit.

' # 9. Spawn CAPA from NCR (uses severity gate — high passes) spawn_action = ncr.action_spawn_capa() capa = e['fusion.plating.capa'].browse(spawn_action.get('res_id')) print(f' ✓ Spawned CAPA {capa.name} from NCR') assert capa.ncr_id == ncr, 'CAPA not linked to NCR' # 10. Walk CAPA: analysis → implementation → verification → effective capa.action_start_analysis() capa.root_cause_analysis = '

Smoke-test: 5 Whys → packaging gap.

' capa.action_start_implementation() capa.action_plan = '

Smoke-test: revise packaging SOP.

' capa.action_start_verification() capa.effectiveness_notes = '

Smoke-test: 30 days no recurrence.

' capa.action_mark_effective() print(f' ✓ CAPA marked effective — state={capa.state}') assert capa.state == 'effective' # 11. Close NCR ncr.action_close() assert ncr.state == 'closed' print(f' ✓ NCR closed — state={ncr.state}') # 11b. Release the auto-spawned Hold (rework path) so the RMA close # gate doesn't block. action_close on RMA refuses if any Hold is # still on_hold or under_review. hold.action_send_to_rework() print(f' ✓ Hold sent to rework — state={hold.state}') # 12. Resolve RMA (will spawn replacement job for rework) rma.action_resolve() print(f' ✓ RMA resolved — state={rma.state}, replacement_job={rma.replacement_job_id.name if rma.replacement_job_id else None}') assert rma.state == 'resolved' # 13. Close RMA rma.action_close() assert rma.state == 'closed' print(f' ✓ RMA closed — state={rma.state}') # 14. Stage_id sync sanity print(f' ✓ NCR stage_id={ncr.stage_id.name if ncr.stage_id else "(none)"}') print(f' ✓ RMA stage_id={rma.stage_id.name if rma.stage_id else "(none)"}') # 15. Counts smoke (read directly — controller needs http context). open_holds = e['fusion.plating.quality.hold'].search_count([ ('state', 'in', ('on_hold', 'under_review')), ]) open_ncrs = e['fusion.plating.ncr'].search_count([ ('state', 'in', ('open', 'containment', 'disposition')), ]) open_rmas = e['fusion.plating.rma'].search_count([ ('state', 'not in', ('closed', 'cancelled')), ]) print(f' ✓ Dashboard counts (post-test): holds={open_holds}, ncrs={open_ncrs}, rmas={open_rmas}') e.cr.commit() print('--- Sub 12 smoke test PASSED ---') smoke()