Files
Odoo-Modules/fusion_plating/fusion_plating_quality/scripts/sub12_smoke_test.py
gsinghpal f08f328688 changes
2026-04-27 00:11:18 -04:00

157 lines
6.1 KiB
Python

# -*- 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': '<p>Smoke-test RMA. Auto-issued for Sub 12 verification.</p>',
})
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 = '<p>Smoke-test: parts segregated for re-rack.</p>'
ncr.action_disposition()
ncr.disposition = 'rework'
ncr.root_cause = '<p>Smoke-test: rack contact loss during transit.</p>'
# 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 = '<p>Smoke-test: 5 Whys → packaging gap.</p>'
capa.action_start_implementation()
capa.action_plan = '<p>Smoke-test: revise packaging SOP.</p>'
capa.action_start_verification()
capa.effectiveness_notes = '<p>Smoke-test: 30 days no recurrence.</p>'
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()