fix(reports): compact landscape BoL so it fits on one page

Last fix kept signatures intact but the landscape BoL still overflowed
to a second page (with the signature row pushed entirely to page 2).
The real ask was for the landscape variant to fit on one page since
landscape has plenty of vertical room.

Aggressive landscape compaction:
- Body font 11pt → 10pt, td font 10 → 9.5pt, th font 10 → 9pt
- Cell padding 8/10px → 4/8px
- Table margin-bottom 12px → 6px
- h2 title 26pt → 18pt with tighter top/bottom margins
- BoL # subtitle 14pt → 11pt
- Shipper/consignee row height 120 → 70px
- highlight-box (cert) padding 10px → 6/10, font 10 → 9pt
- sig-box padding 12 → 8/10px
- sig-line height 70 → 45px

Verified with pypdf: landscape BoL now renders as exactly 1 page
with cert + all 3 signature labels + company info all present.
137 KB clean PDF.

Portrait variant left untouched (it already fit on one page and
the bigger title is appropriate for portrait).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-04-19 07:43:25 -04:00
parent 3217fd685e
commit ed72ed496b
4 changed files with 39 additions and 14 deletions

View File

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

View File

@@ -65,11 +65,11 @@
<t t-set="_fp_company" t-value="doc.company_id if doc and 'company_id' in doc._fields else (company if company else user.company_id)"/>
<t t-set="fp_primary" t-value="(_fp_company.primary_color if _fp_company else False) or '#1d1f1e'"/>
<style>
.fp-landscape { font-family: Arial, sans-serif; font-size: 11pt; color: #000; }
.fp-landscape table { width: 100%; border-collapse: collapse; margin-bottom: 12px; }
.fp-landscape { font-family: Arial, sans-serif; font-size: 10pt; color: #000; }
.fp-landscape table { width: 100%; border-collapse: collapse; margin-bottom: 6px; }
.fp-landscape table.bordered, .fp-landscape table.bordered th, .fp-landscape table.bordered td { border: 1px solid #000; }
.fp-landscape th { background-color: <t t-out="fp_primary"/>; color: white; padding: 8px 10px; font-weight: bold; font-size: 10pt; }
.fp-landscape td { padding: 6px 8px; vertical-align: top; font-size: 10pt; }
.fp-landscape th { background-color: <t t-out="fp_primary"/>; color: white; padding: 4px 8px; font-weight: bold; font-size: 9pt; }
.fp-landscape td { padding: 4px 8px; vertical-align: top; font-size: 9.5pt; }
.fp-landscape .text-center { text-align: center; }
.fp-landscape .text-end { text-align: right; }
.fp-landscape .text-start { text-align: left; }
@@ -77,20 +77,20 @@
.fp-landscape .client-bg { background-color: #fff3e0; }
.fp-landscape .section-row { background-color: #f0f0f0; font-weight: bold; }
.fp-landscape .note-row { font-style: italic; color: #555; }
.fp-landscape h2 { color: <t t-out="fp_primary"/>; margin: 10px 0; font-size: 18pt; }
.fp-landscape h2 { color: <t t-out="fp_primary"/>; margin: 4px 0; font-size: 18pt; }
.fp-landscape .info-table td { padding: 8px 12px; font-size: 11pt; }
.fp-landscape .info-table th { background-color: #f5f5f5; color: #333; font-size: 10pt; padding: 6px 12px; }
.fp-landscape .totals-table { border: 1px solid #000; }
.fp-landscape .totals-table td { border: 1px solid #000; padding: 8px 12px; font-size: 11pt; }
.fp-landscape .highlight-box { border: 2px solid <t t-out="fp_primary"/>; background-color: #eaf2f8; padding: 10px; margin: 10px 0; }
.fp-landscape .highlight-box { border: 2px solid <t t-out="fp_primary"/>; background-color: #eaf2f8; padding: 6px 10px; margin: 6px 0; font-size: 9pt; }
.fp-landscape .fp-header-primary { background-color: <t t-out="fp_primary"/>; color: white; }
.fp-landscape .paid-stamp { color: #28a745; font-size: 42pt; font-weight: bold; border: 4px solid #28a745; padding: 10px 20px; transform: rotate(-8deg); display: inline-block; }
.fp-landscape .status-ok { color: #2e7d32; font-weight: bold; }
.fp-landscape .status-warning { color: #f57f17; font-weight: bold; }
.fp-landscape .status-fail { color: #c62828; font-weight: bold; }
.fp-landscape .sig-box { border: 1px solid #000; padding: 12px; page-break-inside: avoid; break-inside: avoid; }
.fp-landscape .sig-line { border-bottom: 1px solid #000; height: 70px; margin-bottom: 4px; }
.fp-landscape .sig-table { width: 100%; border-collapse: separate; border-spacing: 8px 0; margin-top: 16px; page-break-inside: avoid; break-inside: avoid; }
.fp-landscape .sig-box { border: 1px solid #000; padding: 8px 10px; page-break-inside: avoid; break-inside: avoid; }
.fp-landscape .sig-line { border-bottom: 1px solid #000; height: 45px; margin-bottom: 3px; }
.fp-landscape .sig-table { width: 100%; border-collapse: separate; border-spacing: 6px 0; margin-top: 6px; page-break-inside: avoid; break-inside: avoid; }
.fp-landscape .sig-table td { padding: 0; vertical-align: top; page-break-inside: avoid; break-inside: avoid; }
.fp-landscape .small-muted { font-size: 9pt; color: #666; }
.fp-landscape .fp-cell-mid { vertical-align: middle !important; }

View File

@@ -227,8 +227,8 @@
<t t-set="ship_co" t-value="doc.company_id or env.company"/>
<h2 style="text-align: center; font-size: 26pt; margin: 0 0 6px 0;">BILL OF LADING</h2>
<div class="text-center" style="text-align: center; margin-bottom: 14px; font-size: 14pt;">
<h2 style="text-align: center; font-size: 18pt; margin: 0 0 2px 0;">BILL OF LADING</h2>
<div class="text-center" style="text-align: center; margin-bottom: 6px; font-size: 11pt;">
<strong>BoL #: <span t-field="doc.name"/></strong>
</div>
@@ -242,7 +242,7 @@
</thead>
<tbody>
<tr>
<td style="height: 120px; font-size: 12pt;">
<td style="height: 70px; font-size: 10pt;">
<strong><span t-esc="ship_co.name"/></strong><br/>
<t t-if="doc.source_facility_id">
<em t-field="doc.source_facility_id.name"/><br/>
@@ -250,7 +250,7 @@
<div t-field="ship_co.partner_id"
t-options="{'widget': 'contact', 'fields': ['address', 'phone', 'email'], 'no_marker': True}"/>
</td>
<td style="height: 120px; font-size: 12pt;">
<td style="height: 70px; font-size: 10pt;">
<strong><span t-field="doc.partner_id.name"/></strong><br/>
<t t-if="doc.delivery_address_id">
<div t-field="doc.delivery_address_id"

View File

@@ -0,0 +1,25 @@
env = env # noqa
import re, subprocess, tempfile, os
rep = env.ref('fusion_plating_reports.action_report_fp_bol_landscape')
dlv = env['fusion.plating.delivery'].search([], order='id desc', limit=1)
html, _ = rep.with_context(force_report_rendering=True
)._render_qweb_html(rep.report_name, [dlv.id])
out = html.decode() if isinstance(html, bytes) else str(html)
# Strip styles/scripts and just count visible text length per major block
def find_block(label, txt):
i = txt.find(label)
if i < 0: return None
return txt[i:i+200]
print('=== blocks present ===')
for label in ['BILL OF LADING','BoL #','SHIPPER','SHIP DATE','CARGO DESCRIPTION',
'CoC','PACKING LIST','is to certify','Shipper (Signature']:
print(f' {label!r}:', 'found' if label in out else 'MISSING')
# Render PDF, save, and count pages
pdf, _ = rep.with_context(force_report_rendering=True
)._render_qweb_pdf(rep.report_name, [dlv.id])
path = '/tmp/bol_landscape.pdf'
with open(path, 'wb') as f: f.write(pdf)
print(f'\nPDF: {len(pdf)/1024:.1f} KB at {path}')
n = len(re.findall(rb'/Type\s*/Page[^s]', pdf))
print(f'pages: {n}')