fix(portal): correct group indices after adding work_order to docs panel
Regression I introduced when adding the WO Detail group: the groups.insert(2, wo_group) ran BEFORE the SPECIFICATIONS / QUALITY / SHIPPING appends, so groups[2] shifted from 'quality' to 'work_order' mid-helper. Result: the CoC got appended to the work_order group's docs and shipping doc went into quality. Test caught it. Restructured to declare the 5-group list up front in display order and use stable indices throughout (0=from_you, 1=specs, 2=work_order, 3=quality, 4=shipping). Added a code comment warning future editors that reordering means updating every groups[N] reference. Test updated to expect 5 groups, asserting both 'work_order' and 'quality' keys are present + pending state in each. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -220,9 +220,13 @@ class FpCustomerPortal(CustomerPortal):
|
|||||||
Returns a list of 4 dicts: {key, label, docs}, where docs is a list
|
Returns a list of 4 dicts: {key, label, docs}, where docs is a list
|
||||||
of {label, sub, url, icon_class, icon, pending}.
|
of {label, sub, url, icon_class, icon, pending}.
|
||||||
"""
|
"""
|
||||||
|
# 5 fixed groups in display order. Indices used in the appends below
|
||||||
|
# — if you reorder, update every groups[N] reference.
|
||||||
|
# 0 = from_you, 1 = specs, 2 = work_order, 3 = quality, 4 = shipping
|
||||||
groups = [
|
groups = [
|
||||||
{'key': 'from_you', 'label': 'From You', 'docs': []},
|
{'key': 'from_you', 'label': 'From You', 'docs': []},
|
||||||
{'key': 'specs', 'label': 'Specifications', 'docs': []},
|
{'key': 'specs', 'label': 'Specifications', 'docs': []},
|
||||||
|
{'key': 'work_order', 'label': 'Work Order', 'docs': []},
|
||||||
{'key': 'quality', 'label': 'Quality', 'docs': []},
|
{'key': 'quality', 'label': 'Quality', 'docs': []},
|
||||||
{'key': 'shipping', 'label': 'Shipping', 'docs': []},
|
{'key': 'shipping', 'label': 'Shipping', 'docs': []},
|
||||||
]
|
]
|
||||||
@@ -277,29 +281,7 @@ class FpCustomerPortal(CustomerPortal):
|
|||||||
'icon': '📄',
|
'icon': '📄',
|
||||||
})
|
})
|
||||||
|
|
||||||
# WORK ORDER DETAIL — separate group between specs and quality.
|
# SPECIFICATIONS (idx 1) — V1: placeholder (V2 will pull customer spec)
|
||||||
# Pulls the EN Plating WO Detail PDF (fusion_plating_jobs report).
|
|
||||||
# Requires a linked backend fp.job; placeholder otherwise.
|
|
||||||
wo_group = {'key': 'work_order', 'label': 'Work Order', 'docs': []}
|
|
||||||
if backend_job:
|
|
||||||
wo_group['docs'].append({
|
|
||||||
'label': 'Work Order Detail · %s' % job.name,
|
|
||||||
'sub': 'EN Plating · process scope + recipe summary',
|
|
||||||
'url': '/my/jobs/%s/wo_detail' % job.id,
|
|
||||||
'icon_class': 'o_fp_doc_icon_spec',
|
|
||||||
'icon': '🛠',
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
wo_group['docs'].append({
|
|
||||||
'label': 'Work Order Detail',
|
|
||||||
'sub': 'Will appear once production is confirmed',
|
|
||||||
'pending': True,
|
|
||||||
'icon': '🛠',
|
|
||||||
})
|
|
||||||
# Insert WO Detail group between Specifications (idx 1) and Quality (idx 2).
|
|
||||||
groups.insert(2, wo_group)
|
|
||||||
|
|
||||||
# SPECIFICATIONS — V1: placeholder (V2 will pull customer spec)
|
|
||||||
groups[1]['docs'].append({
|
groups[1]['docs'].append({
|
||||||
'label': 'Customer Specification',
|
'label': 'Customer Specification',
|
||||||
'sub': 'Will appear when EN Plating links the spec',
|
'sub': 'Will appear when EN Plating links the spec',
|
||||||
@@ -307,9 +289,28 @@ class FpCustomerPortal(CustomerPortal):
|
|||||||
'icon': '📋',
|
'icon': '📋',
|
||||||
})
|
})
|
||||||
|
|
||||||
# QUALITY — CoC from coc_attachment_id (the legacy direct field)
|
# WORK ORDER (idx 2) — EN Plating WO Detail PDF via
|
||||||
if job.coc_attachment_id:
|
# fusion_plating_jobs.report_fp_job_wo_detail_template. Requires a
|
||||||
|
# linked backend fp.job; placeholder otherwise.
|
||||||
|
if backend_job:
|
||||||
groups[2]['docs'].append({
|
groups[2]['docs'].append({
|
||||||
|
'label': 'Work Order Detail · %s' % job.name,
|
||||||
|
'sub': 'EN Plating · process scope + recipe summary',
|
||||||
|
'url': '/my/jobs/%s/wo_detail' % job.id,
|
||||||
|
'icon_class': 'o_fp_doc_icon_spec',
|
||||||
|
'icon': '🛠',
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
groups[2]['docs'].append({
|
||||||
|
'label': 'Work Order Detail',
|
||||||
|
'sub': 'Will appear once production is confirmed',
|
||||||
|
'pending': True,
|
||||||
|
'icon': '🛠',
|
||||||
|
})
|
||||||
|
|
||||||
|
# QUALITY (idx 3) — CoC from coc_attachment_id (the legacy direct field)
|
||||||
|
if job.coc_attachment_id:
|
||||||
|
groups[3]['docs'].append({
|
||||||
'label': 'Certificate of Conformance',
|
'label': 'Certificate of Conformance',
|
||||||
'sub': 'EN Plating · %s · %s' % (
|
'sub': 'EN Plating · %s · %s' % (
|
||||||
job.actual_ship_date and job.actual_ship_date.strftime('%b %d') or '',
|
job.actual_ship_date and job.actual_ship_date.strftime('%b %d') or '',
|
||||||
@@ -320,16 +321,16 @@ class FpCustomerPortal(CustomerPortal):
|
|||||||
'icon': '📑',
|
'icon': '📑',
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
groups[2]['docs'].append({
|
groups[3]['docs'].append({
|
||||||
'label': 'Certificate of Conformance',
|
'label': 'Certificate of Conformance',
|
||||||
'sub': 'Will appear after QC completes',
|
'sub': 'Will appear after QC completes',
|
||||||
'pending': True,
|
'pending': True,
|
||||||
'icon': '📑',
|
'icon': '📑',
|
||||||
})
|
})
|
||||||
|
|
||||||
# SHIPPING — packing list + tracking
|
# SHIPPING (idx 4) — packing list + tracking
|
||||||
if job.packing_list_attachment_id:
|
if job.packing_list_attachment_id:
|
||||||
groups[3]['docs'].append({
|
groups[4]['docs'].append({
|
||||||
'label': 'Packing Slip',
|
'label': 'Packing Slip',
|
||||||
'sub': 'EN Plating · %s · %s' % (
|
'sub': 'EN Plating · %s · %s' % (
|
||||||
job.actual_ship_date and job.actual_ship_date.strftime('%b %d') or '',
|
job.actual_ship_date and job.actual_ship_date.strftime('%b %d') or '',
|
||||||
@@ -340,7 +341,7 @@ class FpCustomerPortal(CustomerPortal):
|
|||||||
'icon': '📦',
|
'icon': '📦',
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
groups[3]['docs'].append({
|
groups[4]['docs'].append({
|
||||||
'label': 'Packing Slip · Tracking #',
|
'label': 'Packing Slip · Tracking #',
|
||||||
'sub': 'Available when shipped' + (' — ' + job.tracking_ref if job.tracking_ref else ''),
|
'sub': 'Available when shipped' + (' — ' + job.tracking_ref if job.tracking_ref else ''),
|
||||||
'pending': not job.tracking_ref,
|
'pending': not job.tracking_ref,
|
||||||
|
|||||||
@@ -117,8 +117,9 @@ class TestPortalDashboard(TransactionCase):
|
|||||||
statuses = [t['status'] for t in timeline]
|
statuses = [t['status'] for t in timeline]
|
||||||
self.assertEqual(statuses, ['done', 'done', 'done', 'done', 'done'])
|
self.assertEqual(statuses, ['done', 'done', 'done', 'done', 'done'])
|
||||||
|
|
||||||
def test_group_documents_v1_returns_4_groups(self):
|
def test_group_documents_returns_5_groups(self):
|
||||||
"""V1 doc grouping returns 4 groups; quality populated when CoC set."""
|
"""V1 doc grouping returns 5 groups: From You / Specs / Work Order /
|
||||||
|
Quality / Shipping. Quality populated when CoC set."""
|
||||||
from odoo.addons.fusion_plating_portal.controllers.portal import FpCustomerPortal
|
from odoo.addons.fusion_plating_portal.controllers.portal import FpCustomerPortal
|
||||||
Job = self.env['fusion.plating.portal.job']
|
Job = self.env['fusion.plating.portal.job']
|
||||||
att = self.env['ir.attachment'].create({
|
att = self.env['ir.attachment'].create({
|
||||||
@@ -133,13 +134,16 @@ class TestPortalDashboard(TransactionCase):
|
|||||||
'coc_attachment_id': att.id,
|
'coc_attachment_id': att.id,
|
||||||
})
|
})
|
||||||
groups = FpCustomerPortal()._fp_group_documents(job)
|
groups = FpCustomerPortal()._fp_group_documents(job)
|
||||||
self.assertEqual(len(groups), 4)
|
self.assertEqual(len(groups), 5)
|
||||||
keys = [g['key'] for g in groups]
|
keys = [g['key'] for g in groups]
|
||||||
self.assertEqual(keys, ['from_you', 'specs', 'quality', 'shipping'])
|
self.assertEqual(keys, ['from_you', 'specs', 'work_order', 'quality', 'shipping'])
|
||||||
# Quality group has the CoC populated (not pending)
|
# Quality group has the CoC populated (not pending)
|
||||||
quality = next(g for g in groups if g['key'] == 'quality')
|
quality = next(g for g in groups if g['key'] == 'quality')
|
||||||
self.assertTrue(any(d['label'] == 'Certificate of Conformance' and not d.get('pending')
|
self.assertTrue(any(d['label'] == 'Certificate of Conformance' and not d.get('pending')
|
||||||
for d in quality['docs']))
|
for d in quality['docs']))
|
||||||
# From You + Specifications are placeholders in V1
|
# From You is a placeholder when no SO link (test job has no x_fc_job_id)
|
||||||
from_you = next(g for g in groups if g['key'] == 'from_you')
|
from_you = next(g for g in groups if g['key'] == 'from_you')
|
||||||
self.assertTrue(all(d.get('pending') for d in from_you['docs']))
|
self.assertTrue(all(d.get('pending') for d in from_you['docs']))
|
||||||
|
# Work Order is placeholder without a backend fp.job link
|
||||||
|
wo = next(g for g in groups if g['key'] == 'work_order')
|
||||||
|
self.assertTrue(all(d.get('pending') for d in wo['docs']))
|
||||||
|
|||||||
Reference in New Issue
Block a user