feat(plating): CoC spec-optional + SO-style header + thickness for any cert
- Drop the hard spec_reference gate on fp.certificate.action_issue. The customer-facing description (_fp_resolve_customer_facing_description, walks job -> SO line, reuses fp_customer_description) now drives the CoC Process column; spec_reference prints only when an estimator fills it. - CoC EN/FR reports swap web.external_layout for fp_external_layout_clean + paperformat_fp_a4_portrait. New shared coc_header (company logo + address left, Nadcap logo centre, title + Code128 barcode right) mirrors the Sale Order header. Removed the 3-logo Nadcap/AS9100/CGP accreditation strip and the body H1s; padding-top 0 on both body wrappers. - Un-gate the Issue Certs wizard thickness upload (was invisible unless the customer was thickness-flagged) so a Fischerscope report can be attached to ANY cert; merge (page 2) + inline readings already render unconditionally. - Update issue-gate tests, bump versions (certificates 19.0.9.1.0, reports 19.0.11.27.0, jobs 19.0.11.2.0), record CLAUDE.md rule 14c. Deployed + render-verified on entech (CoC-30065, 223KB PDF, no QWeb errors). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
{
|
||||
'name': 'Fusion Plating — Reports',
|
||||
'version': '19.0.11.26.31',
|
||||
'version': '19.0.11.27.0',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'PDF reports for Fusion Plating: quote, SO, WO, packing, BoL, CoC, invoice, receipt, quality + compliance.',
|
||||
'depends': [
|
||||
|
||||
@@ -122,8 +122,10 @@
|
||||
|
||||
<!-- ============================================================= -->
|
||||
<!-- Formal Certificate of Conformance — English -->
|
||||
<!-- Uses Odoo's default paperformat so web.external_layout's -->
|
||||
<!-- header/footer band gets its reserved space correctly. -->
|
||||
<!-- Compact portrait paperformat (margin_top=8) — same as the SO -->
|
||||
<!-- confirmation. The CoC now renders its own header (coc_header) -->
|
||||
<!-- via fp_external_layout_clean, so it needs the top of the page, -->
|
||||
<!-- not Odoo's ~40mm default header reservation. -->
|
||||
<!-- ============================================================= -->
|
||||
<record id="action_report_coc_en" model="ir.actions.report">
|
||||
<field name="name">Certificate of Conformance (English)</field>
|
||||
@@ -134,6 +136,7 @@
|
||||
<field name="print_report_name">'CoC EN - %s' % object.name</field>
|
||||
<field name="binding_model_id" ref="fusion_plating_certificates.model_fp_certificate"/>
|
||||
<field name="binding_type">report</field>
|
||||
<field name="paperformat_id" ref="paperformat_fp_a4_portrait"/>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================= -->
|
||||
@@ -148,6 +151,7 @@
|
||||
<field name="print_report_name">'CoC FR - %s' % object.name</field>
|
||||
<field name="binding_model_id" ref="fusion_plating_certificates.model_fp_certificate"/>
|
||||
<field name="binding_type">report</field>
|
||||
<field name="paperformat_id" ref="paperformat_fp_a4_portrait"/>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================= -->
|
||||
|
||||
@@ -22,7 +22,91 @@
|
||||
<odoo>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- Shared CoC body — rendered inside web.external_layout -->
|
||||
<!-- Shared CoC header — logo + Nadcap + title/barcode. Mirrors the -->
|
||||
<!-- Sale Order header (report_fp_sale.xml fp-sale-header-row): company -->
|
||||
<!-- logo + address LEFT, Nadcap accreditation logo CENTRE, document -->
|
||||
<!-- title + Code128 barcode RIGHT. Rendered once by the EN/FR wrappers -->
|
||||
<!-- above the body, in place of web.external_layout's company band. -->
|
||||
<!-- ================================================================== -->
|
||||
<template id="coc_header">
|
||||
<t t-set="is_fr" t-value="LANG == 'fr'"/>
|
||||
<t t-set="logo_uri" t-value="('data:image/png;base64,%s' % company.logo.decode()) if company.logo else False"/>
|
||||
<t t-set="company_fax" t-value="company.partner_id.x_ff_fax_number if 'x_ff_fax_number' in company.partner_id._fields else False"/>
|
||||
<t t-set="coc_barcode_uri" t-value="doc.env['ir.actions.report'].sudo().barcode_data_uri('Code128', doc.name, 600, 100) if doc.name else False"/>
|
||||
<style>
|
||||
/* Float-based 3-column header (avoid HTML tables — the global
|
||||
bordered-table cascade bleeds borders onto nested tables on
|
||||
entech wkhtmltopdf; see CLAUDE.md). Bottom border separates
|
||||
header from body, replacing the old hr.heavy. */
|
||||
.fp-coc-header-row { overflow: hidden; margin-bottom: 10px;
|
||||
padding-bottom: 6px; border-bottom: 2px solid #000; }
|
||||
.fp-coc-header-left { float: left; width: 38%; }
|
||||
.fp-coc-header-mid { float: left; width: 24%; text-align: center; padding-top: 4px; }
|
||||
.fp-coc-header-right { float: right; width: 38%; text-align: center; }
|
||||
.fp-coc-logo { max-height: 50px; max-width: 280px; display: block; margin-bottom: 4px; }
|
||||
.fp-coc-company-addr { font-size: 8.5pt; color: #222; line-height: 1.35; }
|
||||
.fp-coc-company-addr div { margin: 0; }
|
||||
.fp-coc-company-addr a { color: #2e6da4; text-decoration: none; }
|
||||
.fp-coc-nadcap-logo { max-height: 45px; max-width: 115px; display: inline-block; }
|
||||
.fp-coc-title { font-size: 18pt; font-weight: bold; color: #2e2e2e;
|
||||
line-height: 1.1; display: block; }
|
||||
/* Barcode: inline-block wrap so the cert-number label centres
|
||||
under the bars. Explicit no-border (wkhtmltopdf frames
|
||||
inline-data imgs on entech). */
|
||||
.fp-coc-bc-wrap { display: inline-block; text-align: center; margin-top: 4px; }
|
||||
.fp-coc-bc-wrap img { height: 48px; max-width: 240px; border: 0 !important; padding: 0; display: block; }
|
||||
.fp-coc-bc-label { font-size: 14pt; font-weight: bold; color: #000;
|
||||
margin-top: 6px; letter-spacing: 1.2px; }
|
||||
</style>
|
||||
<div class="fp-coc-header-row">
|
||||
<div class="fp-coc-header-left">
|
||||
<t t-if="logo_uri">
|
||||
<img t-att-src="logo_uri" class="fp-coc-logo" alt="Logo"/>
|
||||
</t>
|
||||
<div class="fp-coc-company-addr">
|
||||
<div>
|
||||
<t t-if="company.partner_id.street"><span t-esc="company.partner_id.street"/></t>
|
||||
<t t-if="company.partner_id.city"> | <span t-esc="company.partner_id.city"/></t>
|
||||
<t t-if="company.partner_id.state_id"> | <span t-esc="company.partner_id.state_id.code or company.partner_id.state_id.name"/></t>
|
||||
<t t-if="company.partner_id.zip"> | <span t-esc="company.partner_id.zip"/></t>
|
||||
</div>
|
||||
<div t-if="company.phone or company_fax">
|
||||
<t t-if="company.phone">Tel: <span t-esc="company.phone"/></t>
|
||||
<t t-if="company.phone and company_fax">   </t>
|
||||
<t t-if="company_fax">Fax: <span t-esc="company_fax"/></t>
|
||||
</div>
|
||||
<div t-if="company.partner_id.website">
|
||||
<a t-att-href="company.partner_id.website"><span t-esc="company.partner_id.website"/></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Centre: NADCAP accreditation logo, base64-inlined from
|
||||
company settings (wkhtmltopdf can't fetch over HTTP on
|
||||
entech). Same source as the Sale Order header. -->
|
||||
<div class="fp-coc-header-mid">
|
||||
<t t-if="company.x_fc_nadcap_active and company.x_fc_nadcap_logo">
|
||||
<img class="fp-coc-nadcap-logo"
|
||||
t-att-src="'data:image/png;base64,%s' % company.x_fc_nadcap_logo.decode()"
|
||||
alt="Nadcap Accredited"/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="fp-coc-header-right">
|
||||
<span class="fp-coc-title">
|
||||
<t t-if="not is_fr">Certificate of Conformance</t>
|
||||
<t t-if="is_fr">Certificat de Conformité</t>
|
||||
</span>
|
||||
<t t-if="coc_barcode_uri">
|
||||
<div class="fp-coc-bc-wrap">
|
||||
<img t-att-src="coc_barcode_uri" alt="Cert Barcode"/>
|
||||
<div class="fp-coc-bc-label"><span t-field="doc.name"/></div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- Shared CoC body — rendered inside fp_external_layout_clean -->
|
||||
<!-- ================================================================== -->
|
||||
<template id="coc_body">
|
||||
<t t-set="is_fr" t-value="LANG == 'fr'"/>
|
||||
@@ -38,18 +122,12 @@
|
||||
<t t-set="signer_name" t-value="(signer_user and signer_user.name) or ''"/>
|
||||
|
||||
<style>
|
||||
/* padding-top history: original 50mm wasted too much
|
||||
page-1 space; dropped to 5mm caused the title to
|
||||
overlap the ENTECH header (the rendered header is
|
||||
taller than paperformat margin_top reserves). 20mm
|
||||
is the middle ground — title sits cleanly below the
|
||||
header, still saves ~30mm vs the original 50mm so the
|
||||
signature block fits on page 1. If the header logo /
|
||||
address changes height, bump this in step with
|
||||
paperformat.margin_top. See CLAUDE.md "wkhtmltopdf
|
||||
header overlap". */
|
||||
/* No web.external_layout header band anymore — the CoC
|
||||
renders its own header (coc_header) above the body, the
|
||||
same way the Sale Order does. So no top padding is
|
||||
needed to clear an Odoo header zone. */
|
||||
.fp-coc { font-family: Arial, sans-serif; font-size: 9pt; color: #000;
|
||||
padding-top: 20mm; }
|
||||
padding-top: 0; }
|
||||
.fp-coc h1 { text-align: center; font-size: 20pt; margin: 0 0 10px 0; font-weight: bold; }
|
||||
.fp-coc hr.heavy { border: 0; border-top: 2px solid #000; margin: 6px 0; }
|
||||
.fp-coc table { width: 100%; border-collapse: collapse; margin-bottom: 6px; }
|
||||
@@ -61,20 +139,6 @@
|
||||
.fp-coc td { padding: 5px 8px; vertical-align: top; font-size: 8.5pt; }
|
||||
.fp-coc .text-center { text-align: center; }
|
||||
.fp-coc .text-end { text-align: right; }
|
||||
.fp-coc .accreditation-table { margin: 8px 0; }
|
||||
.fp-coc .accreditation-table td.accreditation-cell {
|
||||
width: 33.33%;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
padding: 10px;
|
||||
border: 1px solid #000;
|
||||
height: 2.8cm;
|
||||
}
|
||||
.fp-coc .accreditation-table img {
|
||||
max-height: 2cm;
|
||||
max-width: 95%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.fp-coc .customer-logo { max-height: 1.8cm; max-width: 3.5cm; }
|
||||
.fp-coc .cert-statement-box { border: 1px solid #000; padding: 10px; font-size: 8.5pt; }
|
||||
.fp-coc .cert-statement-box h4 { margin: 0 0 6px 0; font-size: 9.5pt; font-weight: bold; }
|
||||
@@ -116,40 +180,11 @@
|
||||
|
||||
<div class="fp-coc">
|
||||
|
||||
<!-- Title -->
|
||||
<h1 t-if="not is_fr">Certificate of Conformance</h1>
|
||||
<h1 t-if="is_fr">Certificat de Conformité</h1>
|
||||
|
||||
<!-- Accreditations — 3 bordered columns, one logo per column -->
|
||||
<t t-set="nadcap_on" t-value="company.x_fc_nadcap_active and company.x_fc_nadcap_logo"/>
|
||||
<t t-set="as9100_on" t-value="company.x_fc_as9100_active and company.x_fc_as9100_logo"/>
|
||||
<t t-set="cgp_on" t-value="company.x_fc_cgp_active and company.x_fc_cgp_logo"/>
|
||||
<t t-if="nadcap_on or as9100_on or cgp_on">
|
||||
<table class="bordered accreditation-table">
|
||||
<tr>
|
||||
<td class="accreditation-cell">
|
||||
<t t-if="nadcap_on">
|
||||
<img t-att-src="'data:image/png;base64,%s' % company.x_fc_nadcap_logo.decode()"
|
||||
alt="Nadcap Accredited"/>
|
||||
</t>
|
||||
</td>
|
||||
<td class="accreditation-cell">
|
||||
<t t-if="as9100_on">
|
||||
<img t-att-src="'data:image/png;base64,%s' % company.x_fc_as9100_logo.decode()"
|
||||
alt="AS9100 / ISO 9001"/>
|
||||
</t>
|
||||
</td>
|
||||
<td class="accreditation-cell">
|
||||
<t t-if="cgp_on">
|
||||
<img t-att-src="'data:image/png;base64,%s' % company.x_fc_cgp_logo.decode()"
|
||||
alt="Controlled Goods Program"/>
|
||||
</t>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</t>
|
||||
|
||||
<hr class="heavy"/>
|
||||
<!-- Title + accreditation logos moved to the shared
|
||||
coc_header (rendered above this body by the EN/FR
|
||||
wrapper). The 3-logo accreditation strip was dropped
|
||||
per client request 2026-05-28; the Nadcap logo now
|
||||
lives in the header, mirroring the Sale Order. -->
|
||||
|
||||
<!-- Customer block — 3 columns: address | contact | logo -->
|
||||
<table class="bordered">
|
||||
@@ -272,7 +307,13 @@
|
||||
<tr>
|
||||
<td class="text-center"><t t-esc="doc.part_number or '-'"/></td>
|
||||
<td>
|
||||
<t t-esc="doc.process_description or ''"/>
|
||||
<!-- Customer-facing description is the cert's
|
||||
spec / certificate info (client request
|
||||
2026-05-28). Falls back to the recipe-
|
||||
derived process_description. spec_reference,
|
||||
now optional, still prints below when set. -->
|
||||
<t t-set="cust_desc" t-value="doc._fp_resolve_customer_facing_description()"/>
|
||||
<t t-esc="cust_desc or doc.process_description or ''"/>
|
||||
<t t-if="doc.spec_reference">
|
||||
<br/><em t-esc="doc.spec_reference"/>
|
||||
</t>
|
||||
@@ -562,8 +603,14 @@
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="docs" t-as="doc">
|
||||
<t t-set="company" t-value="(doc.sale_order_id.company_id if doc.sale_order_id else False) or (doc.production_id.company_id if doc.production_id else False) or env.company"/>
|
||||
<t t-call="web.external_layout">
|
||||
<!-- Custom SO-style header instead of web.external_layout's
|
||||
company band. fp_external_layout_clean provides the
|
||||
.article wrapper Odoo needs for correct UTF-8 dispatch
|
||||
plus a minimal page-number footer, with NO auto header
|
||||
div — coc_header renders the visible header instead. -->
|
||||
<t t-call="fusion_plating_reports.fp_external_layout_clean">
|
||||
<t t-set="LANG" t-value="'en'"/>
|
||||
<t t-call="fusion_plating_reports.coc_header"/>
|
||||
<div class="page">
|
||||
<!-- Sub 12c — router picks chronological vs classic body -->
|
||||
<t t-call="fusion_plating_reports.coc_body_router"/>
|
||||
@@ -580,8 +627,10 @@
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="docs" t-as="doc">
|
||||
<t t-set="company" t-value="(doc.sale_order_id.company_id if doc.sale_order_id else False) or (doc.production_id.company_id if doc.production_id else False) or env.company"/>
|
||||
<t t-call="web.external_layout">
|
||||
<!-- Custom SO-style header (see report_coc_en). -->
|
||||
<t t-call="fusion_plating_reports.fp_external_layout_clean">
|
||||
<t t-set="LANG" t-value="'fr'"/>
|
||||
<t t-call="fusion_plating_reports.coc_header"/>
|
||||
<div class="page">
|
||||
<!-- Sub 12c — router picks chronological vs classic body -->
|
||||
<t t-call="fusion_plating_reports.coc_body_router"/>
|
||||
|
||||
@@ -22,8 +22,7 @@
|
||||
<t t-set="moves" t-value="(job and 'move_ids' in job._fields and job.move_ids.sorted('move_datetime')) or []"/>
|
||||
|
||||
<style>
|
||||
.fp-coc-chrono { font-family: Arial, sans-serif; font-size: 9pt; color: #000; padding-top: 8mm; }
|
||||
.fp-coc-chrono h1 { text-align: center; font-size: 18pt; margin: 0 0 6px 0; font-weight: bold; }
|
||||
.fp-coc-chrono { font-family: Arial, sans-serif; font-size: 9pt; color: #000; padding-top: 0; }
|
||||
.fp-coc-chrono h3 { font-size: 11pt; margin: 8px 0 2px 0; font-weight: bold; }
|
||||
.fp-coc-chrono .fp-chrono-meta { font-size: 8.5pt; color: #444; margin-bottom: 4px; }
|
||||
.fp-coc-chrono table.bordered,
|
||||
@@ -38,7 +37,8 @@
|
||||
|
||||
<div class="fp-coc-chrono">
|
||||
|
||||
<h1>Certificate of Conformance</h1>
|
||||
<!-- Title + Nadcap logo render in the shared coc_header above
|
||||
this body (rendered once by the EN/FR wrapper). -->
|
||||
|
||||
<!-- Job header (compact) -->
|
||||
<table class="bordered">
|
||||
|
||||
Reference in New Issue
Block a user