fix(plating): compact CoC first column + 3-line part data

- Column titles now render inline "English / French" on one line (was
  stacked), cutting header height. First column drops "Line Item": it is
  now Part Number / No. de pièce, Description / Description, Serial Number /
  Numéro de série with a tight line-height.
- First-column DATA shows three lines — part number, part name, serial
  number — via new fp.certificate._fp_resolve_part_identity() (part name
  from the job's part catalog, serials from the matching SO line; blanks
  fall back to "-"). Bump certificates 19.0.9.2.0, reports 19.0.11.31.0.

Deployed + verified on entech (CoC-30059: ('9876699373',
'VALVE BODY - COMPLETE - ASSY', ''), 243KB render).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-05-28 22:28:04 -04:00
parent ec78fc148d
commit 6a5364e053
4 changed files with 63 additions and 26 deletions

View File

@@ -5,7 +5,7 @@
{ {
'name': 'Fusion Plating — Certificates', 'name': 'Fusion Plating — Certificates',
'version': '19.0.9.1.0', 'version': '19.0.9.2.0',
'category': 'Manufacturing/Plating', 'category': 'Manufacturing/Plating',
'summary': 'Certificate registry for CoC, thickness reports, and quality documents.', 'summary': 'Certificate registry for CoC, thickness reports, and quality documents.',
'description': """ 'description': """

View File

@@ -777,6 +777,49 @@ class FpCertificate(models.Model):
desc = line.name desc = line.name
return (desc or '').strip() return (desc or '').strip()
def _fp_resolve_part_identity(self):
"""Return (part_number, part_name, serials) for the CoC line-item
cell's three lines (client request 2026-05-28).
- part_number: the cert's own field, falling back to the job's
part_catalog part_number.
- part_name: the job's part_catalog.name.
- serials: the matching SO line's x_fc_serial_ids names,
comma-joined.
All cross-module access is guarded so the method stays safe even
if the jobs / configurator layers aren't installed (returns ''
for the unresolved pieces).
"""
self.ensure_one()
job = self.x_fc_job_id if 'x_fc_job_id' in self._fields else False
part = (job.part_catalog_id
if job and 'part_catalog_id' in job._fields else False)
part_number = (
self.part_number
or (part.part_number
if part and 'part_number' in part._fields else '')
or ''
)
part_name = (part.name if part and 'name' in part._fields else '') or ''
serials = ''
so = self.sale_order_id or (
job.sale_order_id
if job and 'sale_order_id' in job._fields else False
)
if so:
lines = so.order_line.filtered(lambda l: not l.display_type)
line = self.env['sale.order.line']
if part and lines and 'x_fc_part_catalog_id' in lines._fields:
line = lines.filtered(
lambda l: l.x_fc_part_catalog_id == part
)[:1]
if not line:
line = lines[:1]
if line and 'x_fc_serial_ids' in line._fields and line.x_fc_serial_ids:
serials = ', '.join(line.x_fc_serial_ids.mapped('name'))
return (part_number, part_name, serials)
def _fp_render_and_attach_pdf(self): def _fp_render_and_attach_pdf(self):
"""Render the CoC PDF via the bound report action, OPTIONALLY """Render the CoC PDF via the bound report action, OPTIONALLY
merge the Fischerscope thickness report PDF (uploaded by the merge the Fischerscope thickness report PDF (uploaded by the

View File

@@ -3,7 +3,7 @@
# License OPL-1 (Odoo Proprietary License v1.0) # License OPL-1 (Odoo Proprietary License v1.0)
{ {
'name': 'Fusion Plating — Reports', 'name': 'Fusion Plating — Reports',
'version': '19.0.11.30.0', 'version': '19.0.11.31.0',
'category': 'Manufacturing/Plating', 'category': 'Manufacturing/Plating',
'summary': 'PDF reports for Fusion Plating: quote, SO, WO, packing, BoL, CoC, invoice, receipt, quality + compliance.', 'summary': 'PDF reports for Fusion Plating: quote, SO, WO, packing, BoL, CoC, invoice, receipt, quality + compliance.',
'depends': [ 'depends': [

View File

@@ -239,16 +239,13 @@
<thead> <thead>
<tr> <tr>
<th style="width: 33%;"> <th style="width: 33%;">
<span class="fp-bl-en-stk">Date of Certification</span> <span class="fp-bl-en">Date of Certification</span><span class="fp-bl-sep">/</span><span class="fp-bl-fr">Date du certificat</span>
<span class="fp-bl-fr-stk">Date du certificat</span>
</th> </th>
<th style="width: 34%;"> <th style="width: 34%;">
<span class="fp-bl-en-stk">Generated By</span> <span class="fp-bl-en">Generated By</span><span class="fp-bl-sep">/</span><span class="fp-bl-fr">Créé par</span>
<span class="fp-bl-fr-stk">Créé par</span>
</th> </th>
<th style="width: 33%;"> <th style="width: 33%;">
<span class="fp-bl-en-stk">Work Order #</span> <span class="fp-bl-en">Work Order #</span><span class="fp-bl-sep">/</span><span class="fp-bl-fr">Bon de travail</span>
<span class="fp-bl-fr-stk">Bon de travail</span>
</th> </th>
</tr> </tr>
</thead> </thead>
@@ -276,39 +273,36 @@
page-break-inside: avoid;"> page-break-inside: avoid;">
<thead> <thead>
<tr> <tr>
<th style="width: 20%;"> <th style="width: 20%; line-height: 1.25;">
<span class="fp-bl-en-stk">Part Number / Line Item</span> <div><span class="fp-bl-en">Part Number</span><span class="fp-bl-sep">/</span><span class="fp-bl-fr">No. de pièce</span></div>
<span class="fp-bl-fr-stk">No. de pièce / Ligne</span> <div><span class="fp-bl-en">Description</span><span class="fp-bl-sep">/</span><span class="fp-bl-fr">Description</span></div>
<span class="fp-bl-en-stk" style="margin-top: 4px;">Description</span> <div><span class="fp-bl-en">Serial Number</span><span class="fp-bl-sep">/</span><span class="fp-bl-fr">Numéro de série</span></div>
<span class="fp-bl-fr-stk">Description</span>
<span class="fp-bl-en-stk" style="margin-top: 4px;">Serial Number</span>
<span class="fp-bl-fr-stk">Numéro de série</span>
</th> </th>
<th style="width: 32%;"> <th style="width: 32%;">
<span class="fp-bl-en-stk">Process</span> <span class="fp-bl-en">Process</span><span class="fp-bl-sep">/</span><span class="fp-bl-fr">Procédé</span>
<span class="fp-bl-fr-stk">Procédé</span>
</th> </th>
<th style="width: 16%;"> <th style="width: 16%;">
<span class="fp-bl-en-stk">Customer PO</span> <span class="fp-bl-en">Customer PO</span><span class="fp-bl-sep">/</span><span class="fp-bl-fr">Bon de commande</span>
<span class="fp-bl-fr-stk">Bon de commande</span>
</th> </th>
<th style="width: 10%;"> <th style="width: 10%;">
<span class="fp-bl-en-stk">Shipped</span> <span class="fp-bl-en">Shipped</span><span class="fp-bl-sep">/</span><span class="fp-bl-fr">Expédié</span>
<span class="fp-bl-fr-stk">Expédié</span>
</th> </th>
<th style="width: 10%;"> <th style="width: 10%;">
<span class="fp-bl-en-stk">NC Qty</span> <span class="fp-bl-en">NC Qty</span><span class="fp-bl-sep">/</span><span class="fp-bl-fr">Qté NC</span>
<span class="fp-bl-fr-stk">Qté NC</span>
</th> </th>
<th style="width: 12%;"> <th style="width: 12%;">
<span class="fp-bl-en-stk">Customer Job No.</span> <span class="fp-bl-en">Customer Job No.</span><span class="fp-bl-sep">/</span><span class="fp-bl-fr">Bon de travail client</span>
<span class="fp-bl-fr-stk">Bon de travail client</span>
</th> </th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td class="text-center"><t t-esc="doc.part_number or '-'"/></td> <td class="text-center" style="line-height: 1.3;">
<t t-set="pid" t-value="doc._fp_resolve_part_identity()"/>
<div><t t-esc="pid[0] or '-'"/></div>
<div><t t-esc="pid[1] or '-'"/></div>
<div><t t-esc="pid[2] or '-'"/></div>
</td>
<td> <td>
<!-- Customer-facing description is the cert's <!-- Customer-facing description is the cert's
spec / certificate info (client request spec / certificate info (client request