feat(portal): _fp_group_documents helper for detail-page doc panel

V1 surfaces only the fields directly on fp.portal.job (CoC + packing
list). Other 2 groups (From You, Specifications) render placeholder
rows. V2 will wire in sale.order linking for full doc surfacing.

Also adds _fp_size_label helper for friendly file-size strings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-05-17 02:49:18 -04:00
parent 8c6718e352
commit 4da123c2d3
2 changed files with 114 additions and 0 deletions

View File

@@ -170,6 +170,93 @@ class FpCustomerPortal(CustomerPortal):
})
return out
def _fp_group_documents(self, job):
"""Build the 4-group document panel for the job detail page.
V1: surfaces only the directly-attached fields on fp.portal.job
(coc_attachment_id, packing_list_attachment_id). Other 3 groups
render placeholder/pending rows. V2 (separate change) will wire in
sale_order_id, quote_request_id, part_catalog_id sources.
Returns a list of 4 dicts: {key, label, docs}, where docs is a list
of {label, sub, url, icon_class, icon, pending}.
"""
groups = [
{'key': 'from_you', 'label': 'From You', 'docs': []},
{'key': 'specs', 'label': 'Specifications', 'docs': []},
{'key': 'quality', 'label': 'Quality', 'docs': []},
{'key': 'shipping', 'label': 'Shipping', 'docs': []},
]
# FROM YOU — V1: placeholder (V2 will pull PO + drawings via SO link)
groups[0]['docs'].append({
'label': 'Customer documents',
'sub': 'Upload your PO and drawings via your sales contact for now',
'pending': True,
'icon': '📄',
})
# SPECIFICATIONS — V1: placeholder (V2 will pull customer spec)
groups[1]['docs'].append({
'label': 'Customer Specification',
'sub': 'Will appear when EN Plating links the spec',
'pending': True,
'icon': '📋',
})
# QUALITY — CoC from coc_attachment_id (the legacy direct field)
if job.coc_attachment_id:
groups[2]['docs'].append({
'label': 'Certificate of Conformance',
'sub': 'EN Plating · %s · %s' % (
job.actual_ship_date and job.actual_ship_date.strftime('%b %d') or '',
self._fp_size_label(job.coc_attachment_id),
),
'url': '/my/jobs/%s/coc' % job.id,
'icon_class': 'o_fp_doc_icon_quality',
'icon': '📑',
})
else:
groups[2]['docs'].append({
'label': 'Certificate of Conformance',
'sub': 'Will appear after QC completes',
'pending': True,
'icon': '📑',
})
# SHIPPING — packing list + tracking
if job.packing_list_attachment_id:
groups[3]['docs'].append({
'label': 'Packing Slip',
'sub': 'EN Plating · %s · %s' % (
job.actual_ship_date and job.actual_ship_date.strftime('%b %d') or '',
self._fp_size_label(job.packing_list_attachment_id),
),
'url': '/web/content/%s?download=true' % job.packing_list_attachment_id.id,
'icon_class': 'o_fp_doc_icon_shipping',
'icon': '📦',
})
else:
groups[3]['docs'].append({
'label': 'Packing Slip · Tracking #',
'sub': 'Available when shipped' + ('' + job.tracking_ref if job.tracking_ref else ''),
'pending': not job.tracking_ref,
'icon': '📦',
})
return groups
def _fp_size_label(self, attachment):
"""Render attachment.file_size as a friendly KB/MB string."""
if not attachment or not attachment.file_size:
return ''
size = attachment.file_size
if size < 1024:
return '%d B' % size
if size < 1024 * 1024:
return '%.0f KB' % (size / 1024)
return '%.1f MB' % (size / (1024 * 1024))
# ==========================================================================
# DASHBOARD
# ==========================================================================

View File

@@ -116,3 +116,30 @@ class TestPortalDashboard(TransactionCase):
timeline = FpCustomerPortal()._fp_get_stage_timeline(job)
statuses = [t['status'] for t in timeline]
self.assertEqual(statuses, ['done', 'done', 'done', 'done', 'done'])
def test_group_documents_v1_returns_4_groups(self):
"""V1 doc grouping returns 4 groups; quality populated when CoC set."""
from odoo.addons.fusion_plating_portal.controllers.portal import FpCustomerPortal
Job = self.env['fusion.plating.portal.job']
att = self.env['ir.attachment'].create({
'name': 'CoC_WO-TEST.pdf',
'datas': b'',
'res_model': 'fusion.plating.portal.job',
})
job = Job.create({
'name': 'WO-DOC-001',
'partner_id': self.partner.id,
'state': 'shipped',
'coc_attachment_id': att.id,
})
groups = FpCustomerPortal()._fp_group_documents(job)
self.assertEqual(len(groups), 4)
keys = [g['key'] for g in groups]
self.assertEqual(keys, ['from_you', 'specs', 'quality', 'shipping'])
# Quality group has the CoC populated (not pending)
quality = next(g for g in groups if g['key'] == 'quality')
self.assertTrue(any(d['label'] == 'Certificate of Conformance' and not d.get('pending')
for d in quality['docs']))
# From You + Specifications are placeholders in V1
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']))