fix(coc): single-page layout — custom paperformat + strip Odoo wrappers

The CoC was rendering on 2 pages with ~35mm of dead whitespace at the
top. Three compounding causes:

1. Default Odoo paperformat reserves header_spacing=35mm (where the
   standard letterhead would sit when using web.external_layout). Our
   CoC has its own full-bleed header so that reservation was pure
   empty space.
   → New paperformat_fp_coc with header_spacing=0, 8mm all-around
     margins, attached to both report_coc_en and report_coc_fr actions.

2. The `<div class="article o_report_layout_boxed">` and nested
   `<div class="page">` wrappers inherited Odoo's CSS which applies
   `page-break-after: always` on `.page` and additional padding on
   `.article`.
   → Dropped both wrappers — template now renders body directly
     inside html_container.

3. Inline style block didn't override Odoo's body/main padding.
   → Aggressive !important reset at the top of the style block on
     html, body, main, .article, .page, and the hidden header/footer
     classes. Also shrunk all paddings by ~30% and bumped base font
     to 9pt to guarantee single-page fit.

Verified: PDF is now 1 page, content starts at the top (title flush
with top margin), accreditation logos + customer logo + signature all
render correctly within the single page.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-04-17 01:50:35 -04:00
parent 96ecf7a9e1
commit 66f7f6c644
2 changed files with 58 additions and 32 deletions

View File

@@ -23,6 +23,24 @@
<field name="dpi">90</field>
</record>
<!-- ============================================================= -->
<!-- CoC paper format — zero header/footer band so the title -->
<!-- starts at the top of the page, not 35mm down. -->
<!-- ============================================================= -->
<record id="paperformat_fp_coc" model="report.paperformat">
<field name="name">Fusion Plating CoC</field>
<field name="default" eval="False"/>
<field name="format">A4</field>
<field name="orientation">Portrait</field>
<field name="margin_top">8</field>
<field name="margin_bottom">8</field>
<field name="margin_left">8</field>
<field name="margin_right">8</field>
<field name="header_line" eval="False"/>
<field name="header_spacing">0</field>
<field name="dpi">90</field>
</record>
<!-- ============================================================= -->
<!-- 1. Certificate of Conformance (Portal Job) — Landscape -->
<!-- ============================================================= -->
@@ -62,6 +80,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_coc"/>
</record>
<!-- ============================================================= -->
@@ -76,6 +95,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_coc"/>
</record>
<!-- ============================================================= -->

View File

@@ -33,29 +33,42 @@
<t t-set="signer_name" t-value="doc.certified_by_id.name or (company.x_fc_owner_user_id.name if company.x_fc_owner_user_id else '')"/>
<style>
@page { margin: 12mm 10mm; }
body, .o_report_layout_boxed { margin: 0 !important; padding: 0 !important; }
.fp-coc { font-family: Arial, sans-serif; font-size: 10pt; color: #000; padding: 0; }
.fp-coc h1 { text-align: center; font-size: 22pt; margin: 0 0 10px 0; font-weight: bold; }
.fp-coc hr.heavy { border: 0; border-top: 2px solid #000; margin: 6px 0; }
/* Kill all Odoo-inherited body/main/page spacing */
html, body { margin: 0 !important; padding: 0 !important; }
main, .article, .page, .o_report_layout_boxed {
margin: 0 !important; padding: 0 !important;
page-break-after: auto !important;
min-height: 0 !important;
}
header.o_company_header, footer.o_company_footer { display: none !important; }
.fp-coc { font-family: Arial, sans-serif; font-size: 9pt; color: #000;
margin: 0; padding: 0; }
.fp-coc h1 { text-align: center; font-size: 20pt; margin: 0 0 6px 0;
font-weight: bold; page-break-before: avoid; }
.fp-coc hr.heavy { border: 0; border-top: 2px solid #000; margin: 4px 0; }
.fp-coc table { width: 100%; border-collapse: collapse; }
.fp-coc table.bordered, .fp-coc table.bordered th, .fp-coc table.bordered td { border: 1px solid #000; }
.fp-coc th { background-color: #ededed; font-weight: bold; padding: 6px 8px; font-size: 9pt; text-align: center; }
.fp-coc td { padding: 6px 8px; vertical-align: top; font-size: 9pt; }
.fp-coc table.bordered,
.fp-coc table.bordered th,
.fp-coc table.bordered td { border: 1px solid #000; }
.fp-coc th { background-color: #ededed; font-weight: bold;
padding: 4px 6px; font-size: 8.5pt; text-align: center; }
.fp-coc td { padding: 4px 6px; vertical-align: top; font-size: 8.5pt; }
.fp-coc .text-center { text-align: center; }
.fp-coc .text-end { text-align: right; }
.fp-coc .hdr-company { font-size: 9pt; line-height: 1.4; }
.fp-coc .hdr-company strong { font-size: 11pt; }
.fp-coc .cert-statement-box { border: 1px solid #000; padding: 12px; font-size: 9pt; }
.fp-coc .cert-statement-box h4 { margin: 0 0 6px 0; font-size: 10pt; font-weight: bold; }
.fp-coc .signature-img { max-height: 2.5cm; max-width: 8cm; }
.fp-coc .accreditations { text-align: center; vertical-align: middle; }
.fp-coc .accreditations img { max-height: 2.2cm; margin: 0 4px; vertical-align: middle; }
.fp-coc .logo-box { text-align: right; vertical-align: middle; }
.fp-coc .logo-box img { max-height: 2.5cm; max-width: 4cm; }
.fp-coc .customer-logo { max-height: 2cm; max-width: 3.5cm; }
.fp-coc .fp-footer-brand { font-size: 8pt; color: #666; text-align: center; margin-top: 14px; }
.fp-coc .small-label { font-size: 8pt; opacity: 0.7; }
.fp-coc .hdr-company { font-size: 8pt; line-height: 1.35; padding: 4px; }
.fp-coc .hdr-company strong { font-size: 10pt; }
.fp-coc .cert-statement-box { border: 1px solid #000; padding: 8px; font-size: 8pt; }
.fp-coc .cert-statement-box h4 { margin: 0 0 4px 0; font-size: 9pt; font-weight: bold; }
.fp-coc .signature-img { max-height: 1.8cm; max-width: 6.5cm; }
.fp-coc .accreditations { text-align: center; vertical-align: middle; padding: 4px; }
.fp-coc .accreditations img { max-height: 1.8cm; margin: 0 3px; vertical-align: middle; }
.fp-coc .logo-box { text-align: right; vertical-align: middle; padding: 4px; }
.fp-coc .logo-box img { max-height: 2cm; max-width: 3.5cm; }
.fp-coc .customer-logo-box img { max-height: 1.6cm; max-width: 3cm; }
.fp-coc .fp-footer-brand { font-size: 7.5pt; color: #666; text-align: center;
margin-top: 8px; }
.fp-coc .small-label { font-size: 7.5pt; opacity: 0.7; }
</style>
<div class="fp-coc">
@@ -318,19 +331,16 @@
</template>
<!-- ================================================================== -->
<!-- English CoC — uses html_container directly (no basic_layout -->
<!-- wrapper) so the full page is ours to style, like the competitor -->
<!-- English CoC — renders directly in html_container with NO wrapper -->
<!-- templates, so Odoo's `.article` and `.page` styles (which force -->
<!-- page-break-after + reserved header padding) don't apply. -->
<!-- ================================================================== -->
<template id="report_coc_en">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="doc">
<t t-set="company" t-value="(doc.portal_job_id.company_id if doc.portal_job_id else False) or (doc.sale_order_id.company_id if doc.sale_order_id else False) or env.company"/>
<t t-set="LANG" t-value="'en'"/>
<div class="article o_report_layout_boxed">
<div class="page">
<t t-call="fusion_plating_reports.coc_body"/>
</div>
</div>
<t t-call="fusion_plating_reports.coc_body"/>
</t>
</t>
</template>
@@ -343,11 +353,7 @@
<t t-foreach="docs" t-as="doc">
<t t-set="company" t-value="(doc.portal_job_id.company_id if doc.portal_job_id else False) or (doc.sale_order_id.company_id if doc.sale_order_id else False) or env.company"/>
<t t-set="LANG" t-value="'fr'"/>
<div class="article o_report_layout_boxed">
<div class="page">
<t t-call="fusion_plating_reports.coc_body"/>
</div>
</div>
<t t-call="fusion_plating_reports.coc_body"/>
</t>
</t>
</template>