diff --git a/fusion_plating/fusion_plating_reports/__manifest__.py b/fusion_plating/fusion_plating_reports/__manifest__.py index fdaadf7a..0d5b32ac 100644 --- a/fusion_plating/fusion_plating_reports/__manifest__.py +++ b/fusion_plating/fusion_plating_reports/__manifest__.py @@ -3,7 +3,7 @@ # License OPL-1 (Odoo Proprietary License v1.0) { 'name': 'Fusion Plating — Reports', - 'version': '19.0.4.2.0', + 'version': '19.0.4.3.0', 'category': 'Manufacturing/Plating', 'summary': 'PDF reports for Fusion Plating: quote, SO, WO, packing, BoL, CoC, invoice, receipt, quality + compliance.', 'depends': [ diff --git a/fusion_plating/fusion_plating_reports/report/report_base_styles.xml b/fusion_plating/fusion_plating_reports/report/report_base_styles.xml index 84eb82e5..44ded659 100644 --- a/fusion_plating/fusion_plating_reports/report/report_base_styles.xml +++ b/fusion_plating/fusion_plating_reports/report/report_base_styles.xml @@ -46,10 +46,13 @@ .fp-report .status-ok { color: #2e7d32; font-weight: bold; } .fp-report .status-warning { color: #f57f17; font-weight: bold; } .fp-report .status-fail { color: #c62828; font-weight: bold; } - .fp-report .sig-box { border: 1px solid #000; padding: 14px 12px 8px 12px; min-height: 110px; display: flex; flex-direction: column; justify-content: flex-end; } + .fp-report .sig-box { border: 1px solid #000; padding: 14px 12px 8px 12px; min-height: 110px; display: flex; flex-direction: column; justify-content: flex-end; page-break-inside: avoid; break-inside: avoid; } .fp-report .sig-line { border-bottom: 1px solid #000; min-height: 60px; } .fp-report .small-muted { font-size: 8pt; color: #666; } .fp-report .fp-cell-mid { vertical-align: middle !important; } + .fp-report .fp-keep-together { page-break-inside: avoid; break-inside: avoid; } + .fp-report .fp-keep-together .row, .fp-report .fp-keep-together .col-4 { page-break-inside: avoid; break-inside: avoid; } + .fp-report table tr { page-break-inside: avoid; break-inside: avoid; } @@ -83,10 +86,13 @@ .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: 14px 12px 8px 12px; min-height: 130px; display: flex; flex-direction: column; justify-content: flex-end; } + .fp-landscape .sig-box { border: 1px solid #000; padding: 14px 12px 8px 12px; min-height: 130px; display: flex; flex-direction: column; justify-content: flex-end; page-break-inside: avoid; break-inside: avoid; } .fp-landscape .sig-line { border-bottom: 1px solid #000; min-height: 70px; } .fp-landscape .small-muted { font-size: 9pt; color: #666; } .fp-landscape .fp-cell-mid { vertical-align: middle !important; } + .fp-landscape .fp-keep-together { page-break-inside: avoid; break-inside: avoid; } + .fp-landscape .fp-keep-together .row, .fp-landscape .fp-keep-together .col-4 { page-break-inside: avoid; break-inside: avoid; } + .fp-landscape table tr { page-break-inside: avoid; break-inside: avoid; } diff --git a/fusion_plating/fusion_plating_reports/report/report_fp_bol.xml b/fusion_plating/fusion_plating_reports/report/report_fp_bol.xml index 8a3bd819..8095201c 100644 --- a/fusion_plating/fusion_plating_reports/report/report_fp_bol.xml +++ b/fusion_plating/fusion_plating_reports/report/report_fp_bol.xml @@ -174,31 +174,33 @@ - -
- This is to certify that the above-named materials are properly classified, - packaged, marked, and labelled, and are in proper condition for transportation - according to the applicable regulations of the Department of Transportation. -
+ +
+
+ This is to certify that the above-named materials are properly classified, + packaged, marked, and labelled, and are in proper condition for transportation + according to the applicable regulations of the Department of Transportation. +
- -
-
-
-
-
Shipper (Signature / Date)
+
+
+
+
+
Shipper (Signature / Date)
+
-
-
-
-
-
Carrier / Driver (Signature / Date)
+
+
+
+
Carrier / Driver (Signature / Date)
+
-
-
-
-
-
Consignee (Signature / Date)
+
+
+
+
Consignee (Signature / Date)
+
diff --git a/fusion_plating/scripts/fp_bol_pagecount.py b/fusion_plating/scripts/fp_bol_pagecount.py new file mode 100644 index 00000000..9a6237d9 --- /dev/null +++ b/fusion_plating/scripts/fp_bol_pagecount.py @@ -0,0 +1,10 @@ +env = env # noqa +import re +for variant in ('portrait', 'landscape'): + rep = env.ref(f'fusion_plating_reports.action_report_fp_bol_{variant}') + dlv = env['fusion.plating.delivery'].search([], order='id desc', limit=1) + pdf, _ = rep.with_context(force_report_rendering=True + )._render_qweb_pdf(rep.report_name, [dlv.id]) + # Count pages by looking at the /Type /Page (not /Pages) markers + n_pages = len(re.findall(rb'/Type\s*/Page[^s]', pdf)) + print(f'{variant:10s} {len(pdf)/1024:6.1f} KB pages={n_pages}') diff --git a/fusion_plating/scripts/fp_bol_pageverify.py b/fusion_plating/scripts/fp_bol_pageverify.py new file mode 100644 index 00000000..c82c9eda --- /dev/null +++ b/fusion_plating/scripts/fp_bol_pageverify.py @@ -0,0 +1,31 @@ +env = env # noqa +import subprocess, os +rep = env.ref('fusion_plating_reports.action_report_fp_bol_landscape') +dlv = env['fusion.plating.delivery'].search([], order='id desc', limit=1) +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'wrote {len(pdf)/1024:.1f} KB to {path}') + +# Extract text per page using pdftotext (poppler-utils) +try: + for p in (1, 2, 3): + out = subprocess.run( + ['pdftotext', '-layout', '-f', str(p), '-l', str(p), path, '-'], + capture_output=True, text=True, timeout=10 + ) + if out.returncode != 0 or not out.stdout.strip(): + continue + text = out.stdout + sig_labels = [ + 'Shipper (Signature' in text, + 'Carrier / Driver' in text, + 'Consignee (Signature' in text, + ] + cert_present = 'is to certify' in text + print(f'PAGE {p}: cert={cert_present} sigs={sig_labels} ' + f'(all-3-sigs-together={all(sig_labels)})') +except FileNotFoundError: + print('pdftotext not installed — skipping per-page text check')