chore(plating): de-dash shipped code + intake-neutral customer emails
Replace em-dashes and en-dashes with hyphens across 789 shipped source files (py/xml/js/scss) so the delivered module reads as human-written; em-dashes had become a recognizable AI-generated tell. Internal .md dev notes are excluded. The WO-sticker mojibake strippers keep their dash search targets (now written — / –). No logic changes: comments and display strings only; validated with py_compile + lxml parse. Rewrite the 7 customer notification emails to be intake-neutral (ship-in / drop-off / pickup) and repair-aware, and fix the Shipped email documents line (packing slip vs bill of lading; certificate only when issued). Subjects use a hyphen separator. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1
|
||||
|
||||
Sub 2 — shared QWeb macro for customer-facing line rendering.
|
||||
Sub 2 - shared QWeb macro for customer-facing line rendering.
|
||||
|
||||
Called from report_fp_sale, report_fp_invoice, report_fp_packing_slip,
|
||||
report_fp_bol. Prints the customer's part number + revision + the
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
For non-part lines (rush fees, freight, expedite) where
|
||||
x_fc_part_catalog_id is blank, falls back to Odoo's standard product
|
||||
display — safe for fee/service lines that shouldn't look like parts.
|
||||
display - safe for fee/service lines that shouldn't look like parts.
|
||||
|
||||
Params expected in the calling context:
|
||||
line - the sale.order.line / account.move.line / stock.picking line
|
||||
@@ -22,7 +22,7 @@
|
||||
<odoo>
|
||||
|
||||
<!-- ==========================================================
|
||||
customer_line_header (legacy — kept for backward compat)
|
||||
customer_line_header (legacy - kept for backward compat)
|
||||
|
||||
Prints part number + revision + description in ONE td.
|
||||
Reports written before the 2026-04-23 column split still
|
||||
@@ -37,7 +37,7 @@
|
||||
</template>
|
||||
|
||||
<!-- ==========================================================
|
||||
customer_line_part_number — just the part number + rev
|
||||
customer_line_part_number - just the part number + rev
|
||||
Renders as a single-line "PN-1234 Rev A" block, intended
|
||||
for the "Part Number" td in customer-facing tables.
|
||||
========================================================== -->
|
||||
@@ -60,17 +60,17 @@
|
||||
</t>
|
||||
<t t-else="">
|
||||
<!-- Fee / freight / non-part line: no part number to show -->
|
||||
<span class="text-muted">—</span>
|
||||
<span class="text-muted">-</span>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- ==========================================================
|
||||
customer_line_description — customer-facing description
|
||||
customer_line_description - customer-facing description
|
||||
plus serial + thickness only.
|
||||
|
||||
Per client request (2026-04-29): customer-facing reports
|
||||
show ONLY description, serial, and thickness. Job # was
|
||||
previously shown here but is internal-only — it lives on
|
||||
previously shown here but is internal-only - it lives on
|
||||
the traveller / WO sticker / packing slip header, not on
|
||||
what the customer sees. Process variant, treatment names,
|
||||
and recipe codes deliberately don't render either.
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</record>
|
||||
|
||||
<!-- ============================================================= -->
|
||||
<!-- CoC paper format — zero header/footer band so the title -->
|
||||
<!-- 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">
|
||||
@@ -61,7 +61,7 @@
|
||||
the body clears the header via padding-top on the .fp-sale
|
||||
wrapper (same way .fp-coc { padding-top: 20mm } clears it
|
||||
on the CoC). DO NOT set margin_top to "the header height"
|
||||
— that forces the header to live entirely inside the
|
||||
- that forces the header to live entirely inside the
|
||||
reserved zone and any header growth = body overlap.
|
||||
margin_top=8 + padding-on-wrapper is the proven shape. -->
|
||||
<field name="margin_top">8</field>
|
||||
@@ -94,7 +94,7 @@
|
||||
</record>
|
||||
|
||||
<!-- ============================================================= -->
|
||||
<!-- 1. Certificate of Conformance (Portal Job) — Landscape -->
|
||||
<!-- 1. Certificate of Conformance (Portal Job) - Landscape -->
|
||||
<!-- ============================================================= -->
|
||||
<record id="action_report_coc" model="ir.actions.report">
|
||||
<field name="name">Certificate of Conformance (Landscape)</field>
|
||||
@@ -108,7 +108,7 @@
|
||||
<field name="paperformat_id" ref="paperformat_fp_a4_landscape"/>
|
||||
</record>
|
||||
|
||||
<!-- Certificate of Conformance — Portrait (legacy, Portal Job) -->
|
||||
<!-- Certificate of Conformance - Portrait (legacy, Portal Job) -->
|
||||
<record id="action_report_coc_portrait" model="ir.actions.report">
|
||||
<field name="name">Certificate of Conformance (Portrait)</field>
|
||||
<field name="model">fusion.plating.portal.job</field>
|
||||
@@ -121,7 +121,7 @@
|
||||
</record>
|
||||
|
||||
<!-- ============================================================= -->
|
||||
<!-- Certificate of Conformance — single bilingual cert. -->
|
||||
<!-- Certificate of Conformance - single bilingual cert. -->
|
||||
<!-- The body renders English + the French translation together, so -->
|
||||
<!-- the separate French print action was removed 2026-05-28 (orphan -->
|
||||
<!-- DB row + report_coc_fr template unlinked on entech). Compact -->
|
||||
@@ -289,7 +289,7 @@
|
||||
<field name="paperformat_id" ref="paperformat_fp_a4_landscape"/>
|
||||
</record>
|
||||
|
||||
<!-- 12. Work Order Margin Report (mrp.production binding) — REMOVED.
|
||||
<!-- 12. Work Order Margin Report (mrp.production binding) - REMOVED.
|
||||
Replaced by the fp.job-bound version in
|
||||
fusion_plating_jobs/report/report_fp_job_margin.xml.
|
||||
The QWeb template (report_wo_margin) remains in templates for
|
||||
@@ -322,12 +322,12 @@
|
||||
<field name="paperformat_id" ref="paperformat_fp_a4_landscape"/>
|
||||
</record>
|
||||
|
||||
<!-- 14. Work Order Traveller (mrp.workorder bindings) — REMOVED.
|
||||
<!-- 14. Work Order Traveller (mrp.workorder bindings) - REMOVED.
|
||||
Replaced by fp.job-bound traveller in
|
||||
fusion_plating_jobs/report/report_fp_job_traveller.xml. -->
|
||||
|
||||
<!-- ============================================================= -->
|
||||
<!-- 14b. Box Sticker — 4x3" label for parts-box identification -->
|
||||
<!-- 14b. Box Sticker - 4x3" label for parts-box identification -->
|
||||
<!-- Prints an ENTECH-style sticker with a QR code that -->
|
||||
<!-- warehouse staff scan to jump straight to the WO form. -->
|
||||
<!-- ============================================================= -->
|
||||
@@ -351,18 +351,18 @@
|
||||
<field name="disable_shrinking" eval="True"/>
|
||||
<!-- dpi=300 is the calibrated value for this paperformat; the
|
||||
sticker inner's px-based geometry is tuned against it. Do
|
||||
NOT bump (see CLAUDE.md rule 14 — 600 broke layout). -->
|
||||
NOT bump (see CLAUDE.md rule 14 - 600 broke layout). -->
|
||||
<field name="dpi">300</field>
|
||||
</record>
|
||||
|
||||
<!-- WO Box Sticker (mrp.workorder + mrp.production bindings) —
|
||||
<!-- WO Box Sticker (mrp.workorder + mrp.production bindings) -
|
||||
REMOVED. Replaced by the fp.job-bound version in
|
||||
fusion_plating_jobs/report/report_fp_job_sticker.xml.
|
||||
The shared inner templates (report_fp_wo_sticker_inner /
|
||||
report_fp_wo_sticker_defaults) stay registered because
|
||||
fp.job + sale.order stickers both t-call them. -->
|
||||
|
||||
<!-- Same sticker bound to sale.order — prints one sticker per
|
||||
<!-- Same sticker bound to sale.order - prints one sticker per
|
||||
order line that carries a part, so estimators / receiving can
|
||||
hand them to the floor before fp.jobs even exist. Uses the
|
||||
same paperformat (6x4") so estimators don't need to think
|
||||
@@ -380,7 +380,7 @@
|
||||
<field name="paperformat_id" ref="paperformat_fp_wo_sticker"/>
|
||||
</record>
|
||||
|
||||
<!-- SO Internal sticker — same layout, prints internal description
|
||||
<!-- SO Internal sticker - same layout, prints internal description
|
||||
instead of the customer-facing line.name. Shop-floor variant. -->
|
||||
<record id="action_report_fp_so_sticker_internal" model="ir.actions.report">
|
||||
<field name="name">Internal Sticker</field>
|
||||
@@ -451,7 +451,7 @@
|
||||
<!-- 17. Invoice (Portrait + Landscape) -->
|
||||
<!-- ============================================================= -->
|
||||
<record id="action_report_fp_invoice_portrait" model="ir.actions.report">
|
||||
<field name="name">Invoice — Plating (Portrait)</field>
|
||||
<field name="name">Invoice - Plating (Portrait)</field>
|
||||
<field name="model">account.move</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">fusion_plating_reports.report_fp_invoice_portrait</field>
|
||||
@@ -463,12 +463,12 @@
|
||||
<!-- Same compact paperformat as the SO confirmation so the
|
||||
inline custom header sits at the top of the page (not 40mm
|
||||
down under Odoo's default margin). See CLAUDE.md
|
||||
"wkhtmltopdf header overlap — the CoC pattern". -->
|
||||
"wkhtmltopdf header overlap - the CoC pattern". -->
|
||||
<field name="paperformat_id" ref="paperformat_fp_a4_portrait"/>
|
||||
</record>
|
||||
|
||||
<record id="action_report_fp_invoice_landscape" model="ir.actions.report">
|
||||
<field name="name">Invoice — Plating (Landscape)</field>
|
||||
<field name="name">Invoice - Plating (Landscape)</field>
|
||||
<field name="model">account.move</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">fusion_plating_reports.report_fp_invoice_landscape</field>
|
||||
@@ -509,7 +509,7 @@
|
||||
</record>
|
||||
|
||||
<!-- ============================================================= -->
|
||||
<!-- 19. Job Traveller — Sales Order bindings only. -->
|
||||
<!-- 19. Job Traveller - Sales Order bindings only. -->
|
||||
<!-- The MO-bound bindings (mrp.production -> traveller) were -->
|
||||
<!-- removed because fusion_plating_jobs ships the canonical -->
|
||||
<!-- fp.job-bound traveller. The SO bindings stay; estimators -->
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
|
||||
Section-header band: #c1c1c1 (neutral grey) with #4e4e4e text
|
||||
Document titles (h2/h4): #4e4e4e
|
||||
Hardcoded — used to follow `res.company.primary_color` but the
|
||||
Hardcoded - used to follow `res.company.primary_color` but the
|
||||
client wanted a uniform neutral palette across every FP report
|
||||
regardless of company branding. `fp_primary` is kept in scope for
|
||||
any per-report template that still wants the company colour.
|
||||
|
||||
To keep section-header markup concise in individual report files,
|
||||
a utility class `.fp-header-primary` is exposed — apply that class
|
||||
a utility class `.fp-header-primary` is exposed - apply that class
|
||||
to any `<th>` or `<td>` that should render as a section banner
|
||||
(e.g. CARGO DESCRIPTION, PAYMENT DETAILS).
|
||||
-->
|
||||
@@ -31,7 +31,7 @@
|
||||
/* Standard collapse + longhand borders + background-clip.
|
||||
Tried border-collapse:separate with single-side-per-cell
|
||||
(right+bottom on cell, top+left on table) to fix wkhtmltopdf's
|
||||
slightly-lighter-verticals quirk — but the `separate` model
|
||||
slightly-lighter-verticals quirk - but the `separate` model
|
||||
makes column widths drift between tables with different
|
||||
column counts, so tables stacked on the page no longer
|
||||
line up at the outer edges. Reverted. The collapse pattern
|
||||
@@ -112,7 +112,7 @@
|
||||
<style>
|
||||
.fp-landscape { font-family: Arial, sans-serif; font-size: 10pt; color: #000; }
|
||||
.fp-landscape table { width: 100%; border-collapse: collapse; border-spacing: 0; margin-bottom: 6px; }
|
||||
/* Standard collapse + longhand + background-clip — see comment in fp_portrait_styles. */
|
||||
/* Standard collapse + longhand + background-clip - see comment in fp_portrait_styles. */
|
||||
.fp-landscape table.bordered { border: 0; border-collapse: collapse; border-spacing: 0; }
|
||||
.fp-landscape table.bordered th,
|
||||
.fp-landscape table.bordered td {
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
<!--
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
Fusion Plating — Certificate of Conformance
|
||||
Fusion Plating - Certificate of Conformance
|
||||
|
||||
Design note:
|
||||
Single bilingual CoC — English and the French translation render
|
||||
Single bilingual CoC - English and the French translation render
|
||||
together. The body wraps in fp_external_layout_clean + a custom
|
||||
coc_header (logo + Nadcap + title/barcode, mirroring the Sale Order)
|
||||
instead of web.external_layout; coc_body_router picks the classic vs
|
||||
@@ -21,7 +21,7 @@
|
||||
<odoo>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- Shared CoC header — logo + Nadcap + title/barcode. Mirrors the -->
|
||||
<!-- 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 -->
|
||||
@@ -33,9 +33,9 @@
|
||||
<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
|
||||
/* Float-based 3-column header (avoid HTML tables - the global
|
||||
bordered-table cascade bleeds borders onto nested tables on
|
||||
entech wkhtmltopdf; see CLAUDE.md). No bottom border —
|
||||
entech wkhtmltopdf; see CLAUDE.md). No bottom border -
|
||||
matches the Sale Order header; spacing alone separates it
|
||||
from the body. */
|
||||
.fp-coc-header-row { overflow: hidden; margin-bottom: 14px;
|
||||
@@ -106,7 +106,7 @@
|
||||
</template>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- Shared CoC body — rendered inside fp_external_layout_clean -->
|
||||
<!-- Shared CoC body - rendered inside fp_external_layout_clean -->
|
||||
<!-- ================================================================== -->
|
||||
<template id="coc_body">
|
||||
<t t-set="is_fr" t-value="LANG == 'fr'"/>
|
||||
@@ -122,7 +122,7 @@
|
||||
<t t-set="signer_name" t-value="(signer_user and signer_user.name) or ''"/>
|
||||
|
||||
<style>
|
||||
/* No web.external_layout header band anymore — the CoC
|
||||
/* 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. */
|
||||
@@ -137,7 +137,7 @@
|
||||
.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; }
|
||||
/* Bilingual stacked column titles — English (bold) over the
|
||||
/* Bilingual stacked column titles - English (bold) over the
|
||||
French translation (italic grey), same pattern as the SO
|
||||
report's narrow-cell headers. Added 2026-05-28. */
|
||||
.fp-coc .fp-bl-en-stk { display: block; font-weight: bold; }
|
||||
@@ -155,7 +155,7 @@
|
||||
.fp-coc .small-label { font-size: 7.5pt; opacity: 0.7; }
|
||||
.fp-coc .brand-note { font-size: 7.5pt; color: #888; text-align: center;
|
||||
margin-top: 10px; font-style: italic; }
|
||||
/* Thickness block — single outer border, internal-only
|
||||
/* Thickness block - single outer border, internal-only
|
||||
cell dividers so the title / metadata / image+readings
|
||||
look like one connected section. No nested .bordered
|
||||
class on inner tables; each cell explicitly draws the
|
||||
@@ -195,7 +195,7 @@
|
||||
per client request 2026-05-28; the Nadcap logo now
|
||||
lives in the header, mirroring the Sale Order. -->
|
||||
|
||||
<!-- Customer block — 2 columns: address | contact. The
|
||||
<!-- Customer block - 2 columns: address | contact. The
|
||||
customer-logo column was dropped 2026-05-28 (usually
|
||||
empty; the company logo is now in the header). -->
|
||||
<table class="bordered">
|
||||
@@ -263,7 +263,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- Line-item table — the column headers already speak
|
||||
<!-- Line-item table - the column headers already speak
|
||||
for themselves (Shipped / NC Qty / etc.), so the
|
||||
hovering "Quantities" caption above was just visual
|
||||
noise. Removed 2026-05-21. Header row + body row
|
||||
@@ -322,7 +322,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- Thickness readings (Fischerscope XRF) — full report
|
||||
<!-- Thickness readings (Fischerscope XRF) - full report
|
||||
block mirroring the original XDAL 600 export so the
|
||||
customer/auditor sees the same context the gauge
|
||||
produced: equipment, operator, calibration, product,
|
||||
@@ -361,13 +361,13 @@
|
||||
readings table mid-row on page 2. -->
|
||||
<div class="fp-thickness-block">
|
||||
|
||||
<!-- Section header — full-width bar, drawn by the
|
||||
<!-- Section header - full-width bar, drawn by the
|
||||
div's bottom-border, no internal table needed. -->
|
||||
<div class="ftk-title">
|
||||
<span class="fp-bl-en">Fischerscope XRF Thickness Report</span><span class="fp-bl-sep">/</span><span class="fp-bl-fr">Rapport d'épaisseur Fischerscope XRF</span>
|
||||
</div>
|
||||
|
||||
<!-- Equipment metadata — 4-column key/value grid.
|
||||
<!-- Equipment metadata - 4-column key/value grid.
|
||||
Per-cell border-right + per-row border-bottom
|
||||
draw the internal grid; the outer perimeter
|
||||
comes from .fp-thickness-block's border. Last
|
||||
@@ -379,49 +379,49 @@
|
||||
<span class="fp-bl-en">Equipment</span><span class="fp-bl-sep">/</span><span class="fp-bl-fr">Équipement</span>
|
||||
</td>
|
||||
<td class="ftk-cell-divider" style="width: 32%;">
|
||||
<t t-esc="doc.x_fc_thickness_equipment or '—'"/>
|
||||
<t t-esc="doc.x_fc_thickness_equipment or '-'"/>
|
||||
</td>
|
||||
<td class="ftk-label ftk-cell-divider" style="width: 18%;">
|
||||
<span class="fp-bl-en">Calibration Std.</span><span class="fp-bl-sep">/</span><span class="fp-bl-fr">Étalon</span>
|
||||
</td>
|
||||
<td style="width: 32%;"><t t-esc="calib or '—'"/></td>
|
||||
<td style="width: 32%;"><t t-esc="calib or '-'"/></td>
|
||||
</tr>
|
||||
<tr class="ftk-row-divider">
|
||||
<td class="ftk-label ftk-cell-divider">
|
||||
<span class="fp-bl-en">Product</span><span class="fp-bl-sep">/</span><span class="fp-bl-fr">Produit</span>
|
||||
</td>
|
||||
<td class="ftk-cell-divider"><t t-esc="doc.x_fc_thickness_product or '—'"/></td>
|
||||
<td class="ftk-cell-divider"><t t-esc="doc.x_fc_thickness_product or '-'"/></td>
|
||||
<td class="ftk-label ftk-cell-divider">
|
||||
<span class="fp-bl-en">Operator</span><span class="fp-bl-sep">/</span><span class="fp-bl-fr">Opérateur</span>
|
||||
</td>
|
||||
<td><t t-esc="doc.x_fc_thickness_operator or '—'"/></td>
|
||||
<td><t t-esc="doc.x_fc_thickness_operator or '-'"/></td>
|
||||
</tr>
|
||||
<tr class="ftk-row-divider">
|
||||
<td class="ftk-label ftk-cell-divider">
|
||||
<span class="fp-bl-en">Application</span><span class="fp-bl-sep">/</span><span class="fp-bl-fr">Application</span>
|
||||
</td>
|
||||
<td class="ftk-cell-divider"><t t-esc="doc.x_fc_thickness_application or '—'"/></td>
|
||||
<td class="ftk-cell-divider"><t t-esc="doc.x_fc_thickness_application or '-'"/></td>
|
||||
<td class="ftk-label ftk-cell-divider">
|
||||
<span class="fp-bl-en">Measured</span><span class="fp-bl-sep">/</span><span class="fp-bl-fr">Mesuré le</span>
|
||||
</td>
|
||||
<td>
|
||||
<t t-if="doc.x_fc_thickness_datetime"
|
||||
t-esc="doc.x_fc_thickness_datetime.strftime('%Y-%m-%d %H:%M')"/>
|
||||
<t t-if="not doc.x_fc_thickness_datetime">—</t>
|
||||
<t t-if="not doc.x_fc_thickness_datetime">-</t>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ftk-row-divider">
|
||||
<td class="ftk-label ftk-cell-divider">
|
||||
<span class="fp-bl-en">Directory</span><span class="fp-bl-sep">/</span><span class="fp-bl-fr">Répertoire</span>
|
||||
</td>
|
||||
<td class="ftk-cell-divider"><t t-esc="doc.x_fc_thickness_directory or '—'"/></td>
|
||||
<td class="ftk-cell-divider"><t t-esc="doc.x_fc_thickness_directory or '-'"/></td>
|
||||
<td class="ftk-label ftk-cell-divider">
|
||||
<span class="fp-bl-en">Measuring Time</span><span class="fp-bl-sep">/</span><span class="fp-bl-fr">Durée de mesure</span>
|
||||
</td>
|
||||
<td>
|
||||
<t t-if="doc.x_fc_thickness_measuring_time_sec"
|
||||
t-esc="'%d sec' % doc.x_fc_thickness_measuring_time_sec"/>
|
||||
<t t-if="not doc.x_fc_thickness_measuring_time_sec">—</t>
|
||||
<t t-if="not doc.x_fc_thickness_measuring_time_sec">-</t>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -510,7 +510,7 @@
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- Source-file footnote — italic + opacity:0.7
|
||||
<!-- Source-file footnote - italic + opacity:0.7
|
||||
(from .small-label) renders jagged/washed-out
|
||||
on entech wkhtmltopdf. Solid #555 grey at
|
||||
normal weight prints cleanly. -->
|
||||
@@ -523,7 +523,7 @@
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- Signature + certification statement — never split
|
||||
<!-- Signature + certification statement - never split
|
||||
across pages (page-break-inside on the row works on
|
||||
entech wkhtmltopdf; see CLAUDE.md). Bordered table draws
|
||||
the outer box + the column divider between Certified By
|
||||
@@ -581,7 +581,7 @@
|
||||
</template>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- English CoC — wrapped in web.external_layout -->
|
||||
<!-- English CoC - wrapped in web.external_layout -->
|
||||
<!-- ================================================================== -->
|
||||
<template id="report_coc_en">
|
||||
<t t-call="web.html_container">
|
||||
@@ -591,12 +591,12 @@
|
||||
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. -->
|
||||
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 -->
|
||||
<!-- Sub 12c - router picks chronological vs classic body -->
|
||||
<t t-call="fusion_plating_reports.coc_body_router"/>
|
||||
</div>
|
||||
</t>
|
||||
@@ -604,7 +604,7 @@
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- French CoC template removed 2026-05-28 — the single bilingual
|
||||
<!-- French CoC template removed 2026-05-28 - the single bilingual
|
||||
report_coc_en renders English + French together. -->
|
||||
|
||||
<!-- ================================================================== -->
|
||||
@@ -618,7 +618,7 @@
|
||||
<div class="fp-report">
|
||||
<div class="page">
|
||||
<h4>
|
||||
Certificate of Conformance —
|
||||
Certificate of Conformance -
|
||||
<span t-field="doc.name"/>
|
||||
</h4>
|
||||
<table class="bordered">
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
Part of the Fusion Plating product family.
|
||||
|
||||
Sub 12c — Chronological CoC body.
|
||||
Sub 12c - Chronological CoC body.
|
||||
Walks fp.job.step.move records in time order (chain-of-custody),
|
||||
rendering each transition as a heading ("Step Name (Tank Code)")
|
||||
with a "Moved By / Time" meta line + a 5-column measurement
|
||||
@@ -54,28 +54,28 @@
|
||||
<tr>
|
||||
<td>
|
||||
<t t-if="job and 'part_catalog_id' in job._fields and job.part_catalog_id">
|
||||
<span t-esc="job.part_catalog_id.part_number or job.product_id.default_code or '—'"/>
|
||||
<span t-esc="job.part_catalog_id.part_number or job.product_id.default_code or '-'"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<span t-esc="(job and job.product_id and job.product_id.default_code) or '—'"/>
|
||||
<span t-esc="(job and job.product_id and job.product_id.default_code) or '-'"/>
|
||||
</t>
|
||||
</td>
|
||||
<td>
|
||||
<t t-if="job and 'part_catalog_id' in job._fields and job.part_catalog_id">
|
||||
<span t-esc="job.part_catalog_id.name or job.product_id.name or '—'"/>
|
||||
<span t-esc="job.part_catalog_id.name or job.product_id.name or '-'"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<span t-esc="(job and job.product_id and job.product_id.name) or '—'"/>
|
||||
<span t-esc="(job and job.product_id and job.product_id.name) or '-'"/>
|
||||
</t>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<span t-esc="(job and job.qty) or ''"/>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<span t-esc="(job and job.name) or '—'"/>
|
||||
<span t-esc="(job and job.name) or '-'"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="(job and job.sale_order_id and job.sale_order_id.client_order_ref) or '—'"/>
|
||||
<span t-esc="(job and job.sale_order_id and job.sale_order_id.client_order_ref) or '-'"/>
|
||||
</td>
|
||||
<td/>
|
||||
<td>
|
||||
@@ -86,7 +86,7 @@
|
||||
|
||||
<h3 style="margin-top: 6px;">Specification(s):
|
||||
<span style="font-weight: normal;"
|
||||
t-esc="(job and job.recipe_id and job.recipe_id.name) or '—'"/>
|
||||
t-esc="(job and job.recipe_id and job.recipe_id.name) or '-'"/>
|
||||
</h3>
|
||||
|
||||
<hr class="heavy"/>
|
||||
@@ -95,13 +95,13 @@
|
||||
<t t-foreach="moves" t-as="mv">
|
||||
<t t-set="dest" t-value="mv.to_step_id"/>
|
||||
<t t-set="tank_code" t-value="(mv.to_tank_id and mv.to_tank_id.code) or (dest and dest.tank_id and dest.tank_id.code) or ''"/>
|
||||
<!-- Sub 12d — input_ids lives on recipe_node, not job.step.
|
||||
<!-- Sub 12d - input_ids lives on recipe_node, not job.step.
|
||||
Walk via recipe_node_id; filter to step_input + collect=True. -->
|
||||
<t t-set="recipe_node" t-value="(dest and dest.recipe_node_id) or False"/>
|
||||
<t t-set="captured" t-value="(recipe_node and recipe_node.input_ids.filtered(lambda i: (i.kind or 'step_input') == 'step_input' and (i.collect if 'collect' in i._fields else True)).sorted('sequence')) or []"/>
|
||||
|
||||
<h3>
|
||||
<span t-esc="(dest and dest.name) or '—'"/>
|
||||
<span t-esc="(dest and dest.name) or '-'"/>
|
||||
<t t-if="tank_code"> (<span t-esc="tank_code"/>)</t>
|
||||
</h3>
|
||||
<div class="fp-chrono-meta">
|
||||
@@ -115,11 +115,11 @@
|
||||
</t>
|
||||
</div>
|
||||
|
||||
<!-- Sub 12c+ — index captured values from the move's transition_input_value_ids
|
||||
<!-- Sub 12c+ - index captured values from the move's transition_input_value_ids
|
||||
by node_input_id so we can render Actual alongside Target. -->
|
||||
<t t-set="captured_values_by_input" t-value="{v.node_input_id.id: v for v in mv.transition_input_value_ids}"/>
|
||||
|
||||
<!-- Measurement sub-table — show whenever destination has any
|
||||
<!-- Measurement sub-table - show whenever destination has any
|
||||
step_input prompts OR the move recorded any captured values. -->
|
||||
<t t-set="prompts" t-value="captured"/>
|
||||
<t t-if="not prompts and mv.transition_input_value_ids">
|
||||
@@ -173,7 +173,7 @@
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<t t-if="'target_min' in inp._fields and inp.target_min and inp.target_max">
|
||||
<span t-esc="inp.target_min"/>–<span t-esc="inp.target_max"/>
|
||||
<span t-esc="inp.target_min"/>-<span t-esc="inp.target_max"/>
|
||||
<t t-if="'target_unit' in inp._fields and inp.target_unit">
|
||||
<span> </span><span t-esc="inp.target_unit"/>
|
||||
</t>
|
||||
@@ -208,7 +208,7 @@
|
||||
|
||||
<hr class="heavy"/>
|
||||
|
||||
<!-- Sign-off block — unified with WO Detail / CoC (2026-05-17).
|
||||
<!-- Sign-off block - unified with WO Detail / CoC (2026-05-17).
|
||||
Signer = cert's certified_by user → falls back to company
|
||||
owner. Signature image = signer's Plating Signature
|
||||
(x_fc_signature_image from Preferences → My Profile). -->
|
||||
@@ -216,7 +216,7 @@
|
||||
<t t-set="signature_img" t-value="(signer_user and 'x_fc_signature_image' in signer_user._fields and signer_user.x_fc_signature_image) or False"/>
|
||||
<t t-set="signer_name" t-value="(signer_user and signer_user.name) or ''"/>
|
||||
|
||||
<!-- Sub 12c+ — cert statement: per-customer override → company default → hardcoded fallback -->
|
||||
<!-- Sub 12c+ - cert statement: per-customer override → company default → hardcoded fallback -->
|
||||
<t t-set="_cust_stmt" t-value="('x_fc_cert_statement' in doc.partner_id._fields and doc.partner_id.x_fc_cert_statement) or False"/>
|
||||
<t t-set="_co_stmt" t-value="('x_fc_default_cert_statement' in company._fields and company.x_fc_default_cert_statement) or False"/>
|
||||
<t t-set="cert_statement" t-value="_cust_stmt or _co_stmt or 'We certify that the parts listed above have been processed in accordance with the specifications referenced and that all required tests have been performed. Records on file at our facility per AS9100 / ISO 9001 retention policy.'"/>
|
||||
@@ -245,7 +245,7 @@
|
||||
</template>
|
||||
|
||||
<!-- ============================================================== -->
|
||||
<!-- Router — picks chronological vs classic body -->
|
||||
<!-- Router - picks chronological vs classic body -->
|
||||
<!-- Wired into the existing CoC actions in report_coc.xml. -->
|
||||
<!-- ============================================================== -->
|
||||
<template id="coc_body_router">
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
|
||||
Sub 12 Phase E — 8D Report (NCR + linked CAPA combined).
|
||||
Sub 12 Phase E - 8D Report (NCR + linked CAPA combined).
|
||||
Bound to fusion.plating.ncr. Renders all 8 disciplines in one PDF.
|
||||
Degraded mode if no CAPA is linked: D4–D8 sections show a placeholder
|
||||
Degraded mode if no CAPA is linked: D4-D8 sections show a placeholder
|
||||
note that the CAPA hasn't been opened yet.
|
||||
-->
|
||||
<odoo>
|
||||
@@ -25,9 +25,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- D1 — Team -->
|
||||
<!-- D1 - Team -->
|
||||
<div style="margin-bottom: 14px;">
|
||||
<h2 style="font-size: 14px; background: #eee; padding: 6px 10px; margin: 0 0 6px 0;">D1 — Team</h2>
|
||||
<h2 style="font-size: 14px; background: #eee; padding: 6px 10px; margin: 0 0 6px 0;">D1 - Team</h2>
|
||||
<table style="width: 100%; padding: 6px;">
|
||||
<tr><td style="width: 130px; color: #666;">Lead</td><td><span t-out="ncr.team_id.lead_user_id.name or ncr.reported_by_id.name"/></td></tr>
|
||||
<tr><td style="color: #666;">Team</td><td><span t-out="ncr.team_id.name or 'Unassigned'"/></td></tr>
|
||||
@@ -35,9 +35,9 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- D2 — Problem Description -->
|
||||
<!-- D2 - Problem Description -->
|
||||
<div style="margin-bottom: 14px;">
|
||||
<h2 style="font-size: 14px; background: #eee; padding: 6px 10px; margin: 0 0 6px 0;">D2 — Problem Description</h2>
|
||||
<h2 style="font-size: 14px; background: #eee; padding: 6px 10px; margin: 0 0 6px 0;">D2 - Problem Description</h2>
|
||||
<div style="padding: 6px;">
|
||||
<table style="width: 100%; margin-bottom: 8px;">
|
||||
<tr><td style="width: 130px; color: #666;">Severity</td><td><span t-field="ncr.severity"/></td></tr>
|
||||
@@ -52,17 +52,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- D3 — Containment -->
|
||||
<!-- D3 - Containment -->
|
||||
<div style="margin-bottom: 14px;">
|
||||
<h2 style="font-size: 14px; background: #eee; padding: 6px 10px; margin: 0 0 6px 0;">D3 — Containment Action</h2>
|
||||
<h2 style="font-size: 14px; background: #eee; padding: 6px 10px; margin: 0 0 6px 0;">D3 - Containment Action</h2>
|
||||
<div style="padding: 6px; background: #fafafa; border-left: 3px solid #ddd;">
|
||||
<t t-out="ncr.containment or 'No containment narrative recorded.'"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- D4 — Root Cause -->
|
||||
<!-- D4 - Root Cause -->
|
||||
<div style="margin-bottom: 14px;">
|
||||
<h2 style="font-size: 14px; background: #eee; padding: 6px 10px; margin: 0 0 6px 0;">D4 — Root Cause Analysis</h2>
|
||||
<h2 style="font-size: 14px; background: #eee; padding: 6px 10px; margin: 0 0 6px 0;">D4 - Root Cause Analysis</h2>
|
||||
<div style="padding: 6px;">
|
||||
<table t-if="capa or ncr.reason_id" style="width: 100%; margin-bottom: 8px;">
|
||||
<tr t-if="ncr.reason_id"><td style="width: 130px; color: #666;">Classified Reason</td><td><span t-out="ncr.reason_id.name"/> (<span t-field="ncr.reason_id.category"/>)</td></tr>
|
||||
@@ -76,58 +76,58 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- D5 — Permanent Corrective Action -->
|
||||
<!-- D5 - Permanent Corrective Action -->
|
||||
<div style="margin-bottom: 14px;">
|
||||
<h2 style="font-size: 14px; background: #eee; padding: 6px 10px; margin: 0 0 6px 0;">D5 — Permanent Corrective Action</h2>
|
||||
<h2 style="font-size: 14px; background: #eee; padding: 6px 10px; margin: 0 0 6px 0;">D5 - Permanent Corrective Action</h2>
|
||||
<div style="padding: 6px; background: #fafafa; border-left: 3px solid #ddd;">
|
||||
<t t-if="capa and capa.action_plan"><t t-out="capa.action_plan"/></t>
|
||||
<t t-else="">No corrective action plan recorded — open a CAPA from the NCR to populate this section.</t>
|
||||
<t t-else="">No corrective action plan recorded - open a CAPA from the NCR to populate this section.</t>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- D6 — Implement & Verify -->
|
||||
<!-- D6 - Implement & Verify -->
|
||||
<div style="margin-bottom: 14px;">
|
||||
<h2 style="font-size: 14px; background: #eee; padding: 6px 10px; margin: 0 0 6px 0;">D6 — Implement & Verify</h2>
|
||||
<h2 style="font-size: 14px; background: #eee; padding: 6px 10px; margin: 0 0 6px 0;">D6 - Implement & Verify</h2>
|
||||
<div style="padding: 6px;">
|
||||
<table t-if="capa" style="width: 100%; margin-bottom: 8px;">
|
||||
<tr><td style="width: 130px; color: #666;">CAPA State</td><td><span t-field="capa.state"/></td></tr>
|
||||
<tr><td style="color: #666;">Owner</td><td><span t-out="capa.owner_id.name or '—'"/></td></tr>
|
||||
<tr><td style="color: #666;">Due</td><td><span t-out="capa.due_date or '—'"/></td></tr>
|
||||
<tr><td style="color: #666;">Owner</td><td><span t-out="capa.owner_id.name or '-'"/></td></tr>
|
||||
<tr><td style="color: #666;">Due</td><td><span t-out="capa.due_date or '-'"/></td></tr>
|
||||
<tr><td style="color: #666;">Verification</td><td><span t-out="capa.verification_date or 'Pending'"/></td></tr>
|
||||
<tr><td style="color: #666;">Effective</td><td><span t-out="'Yes' if capa.is_effective else 'Pending'"/></td></tr>
|
||||
</table>
|
||||
<div style="background: #fafafa; padding: 8px; border-left: 3px solid #ddd;" t-if="capa">
|
||||
<t t-out="capa.effectiveness_notes or 'No effectiveness notes yet.'"/>
|
||||
</div>
|
||||
<div t-if="not capa" style="color: #666;">No CAPA opened — implementation tracking unavailable.</div>
|
||||
<div t-if="not capa" style="color: #666;">No CAPA opened - implementation tracking unavailable.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- D7 — Prevent Recurrence -->
|
||||
<!-- D7 - Prevent Recurrence -->
|
||||
<div style="margin-bottom: 14px;">
|
||||
<h2 style="font-size: 14px; background: #eee; padding: 6px 10px; margin: 0 0 6px 0;">D7 — Prevent Recurrence</h2>
|
||||
<h2 style="font-size: 14px; background: #eee; padding: 6px 10px; margin: 0 0 6px 0;">D7 - Prevent Recurrence</h2>
|
||||
<div style="padding: 6px; background: #fafafa; border-left: 3px solid #ddd;">
|
||||
<t t-if="capa and capa.type == 'preventive' and capa.action_plan">
|
||||
<t t-out="capa.action_plan"/>
|
||||
</t>
|
||||
<t t-elif="capa and capa.action_plan">
|
||||
<em>Refer to D5 — corrective action plan covers preventive measures.</em>
|
||||
<em>Refer to D5 - corrective action plan covers preventive measures.</em>
|
||||
</t>
|
||||
<t t-else="">No preventive actions recorded. Open a Preventive-type CAPA to track recurrence-prevention measures separately.</t>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- D8 — Recognise the Team -->
|
||||
<!-- D8 - Recognise the Team -->
|
||||
<div style="margin-bottom: 14px;">
|
||||
<h2 style="font-size: 14px; background: #eee; padding: 6px 10px; margin: 0 0 6px 0;">D8 — Recognise the Team</h2>
|
||||
<h2 style="font-size: 14px; background: #eee; padding: 6px 10px; margin: 0 0 6px 0;">D8 - Recognise the Team</h2>
|
||||
<div style="padding: 6px;">
|
||||
<p t-if="capa and capa.state in ('effective', 'closed')">
|
||||
Closure verified <span t-out="capa.verification_date"/> by <span t-out="capa.verification_by_id.name or '—'"/>.
|
||||
Closure verified <span t-out="capa.verification_date"/> by <span t-out="capa.verification_by_id.name or '-'"/>.
|
||||
</p>
|
||||
<p t-else="">Pending closure.</p>
|
||||
<table style="width: 100%; margin-top: 8px; font-size: 11px; color: #666;">
|
||||
<tr><td>NCR Closed:</td><td><span t-out="ncr.closed_date or 'Open'"/></td></tr>
|
||||
<tr t-if="capa"><td>CAPA Verified:</td><td><span t-out="capa.verification_date or '—'"/></td></tr>
|
||||
<tr t-if="capa"><td>CAPA Verified:</td><td><span t-out="capa.verification_date or '-'"/></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<!--
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
Fusion Plating — Bill of Lading (Portrait + Landscape).
|
||||
Fusion Plating - Bill of Lading (Portrait + Landscape).
|
||||
Binds to fusion.plating.delivery. Includes shipper, consignee, carrier,
|
||||
cargo description, special instructions, and sign-off lines.
|
||||
-->
|
||||
@@ -19,7 +19,7 @@
|
||||
<div class="fp-report">
|
||||
<div class="page">
|
||||
|
||||
<!-- Resolve shipper company defensively — fall back to env.company
|
||||
<!-- Resolve shipper company defensively - fall back to env.company
|
||||
when delivery.company_id is missing on legacy records. -->
|
||||
<t t-set="ship_co" t-value="doc.company_id or env.company"/>
|
||||
|
||||
@@ -116,9 +116,9 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- Cargo description — iterate the linked fp.job's SO lines
|
||||
<!-- Cargo description - iterate the linked fp.job's SO lines
|
||||
so each part renders with its customer part number via
|
||||
the shared macro. Sub 11 — replaced mrp.production lookup. -->
|
||||
the shared macro. Sub 11 - replaced mrp.production lookup. -->
|
||||
<t t-set="_job" t-value="env['fp.job'].sudo().search([('name', '=', doc.job_ref)], limit=1) if doc.job_ref else env['fp.job']"/>
|
||||
<t t-set="_so" t-value="_job.sale_order_id if _job else False"/>
|
||||
<t t-set="_lines" t-value="_so.order_line.filtered(lambda l: l.product_id and l.product_uom_qty > 0) if _so else False"/>
|
||||
@@ -152,7 +152,7 @@
|
||||
<td class="text-center fp-cell-mid">
|
||||
<span t-esc="int(line.product_uom_qty) if line.product_uom_qty == int(line.product_uom_qty) else line.product_uom_qty"/>
|
||||
</td>
|
||||
<td class="text-center fp-cell-mid">—</td>
|
||||
<td class="text-center fp-cell-mid">-</td>
|
||||
<td class="text-center fp-cell-mid">
|
||||
<span t-if="doc.tdg_required">TDG</span>
|
||||
<span t-else="">NON-HAZ</span>
|
||||
@@ -164,15 +164,15 @@
|
||||
<tr>
|
||||
<td class="text-center fp-cell-mid">1</td>
|
||||
<td class="fp-cell-mid">
|
||||
Plated parts — Job <span t-esc="doc.job_ref or doc.name"/>
|
||||
Plated parts - Job <span t-esc="doc.job_ref or doc.name"/>
|
||||
<t t-if="doc.notes">
|
||||
<br/><span t-field="doc.notes"/>
|
||||
</t>
|
||||
</td>
|
||||
<td class="text-center fp-cell-mid">
|
||||
<span t-esc="int(_mo.product_qty) if _mo else '—'"/>
|
||||
<span t-esc="int(_mo.product_qty) if _mo else '-'"/>
|
||||
</td>
|
||||
<td class="text-center fp-cell-mid">—</td>
|
||||
<td class="text-center fp-cell-mid">-</td>
|
||||
<td class="text-center fp-cell-mid">
|
||||
<span t-if="doc.tdg_required">TDG</span>
|
||||
<span t-else="">NON-HAZ</span>
|
||||
@@ -194,11 +194,11 @@
|
||||
<tr>
|
||||
<td class="text-center fp-cell-mid">
|
||||
<span t-if="doc.coc_attachment_id" class="status-ok">✓ Attached</span>
|
||||
<span t-else="">—</span>
|
||||
<span t-else="">-</span>
|
||||
</td>
|
||||
<td class="text-center fp-cell-mid">
|
||||
<span t-if="doc.packing_list_attachment_id" class="status-ok">✓ Attached</span>
|
||||
<span t-else="">—</span>
|
||||
<span t-else="">-</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -328,8 +328,8 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- Cargo description — iterate the linked fp.job's SO lines.
|
||||
Sub 11 — replaced mrp.production lookup. -->
|
||||
<!-- Cargo description - iterate the linked fp.job's SO lines.
|
||||
Sub 11 - replaced mrp.production lookup. -->
|
||||
<t t-set="_job" t-value="env['fp.job'].sudo().search([('name', '=', doc.job_ref)], limit=1) if doc.job_ref else env['fp.job']"/>
|
||||
<t t-set="_so" t-value="_job.sale_order_id if _job else False"/>
|
||||
<t t-set="_lines" t-value="_so.order_line.filtered(lambda l: l.product_id and l.product_uom_qty > 0) if _so else False"/>
|
||||
@@ -364,7 +364,7 @@
|
||||
<td class="text-center">
|
||||
<span t-esc="int(line.product_uom_qty) if line.product_uom_qty == int(line.product_uom_qty) else line.product_uom_qty"/>
|
||||
</td>
|
||||
<td class="text-center">—</td>
|
||||
<td class="text-center">-</td>
|
||||
<td class="text-center">
|
||||
<span t-if="doc.tdg_required">TDG</span>
|
||||
<span t-else="">NON-HAZ</span>
|
||||
@@ -380,13 +380,13 @@
|
||||
<tr>
|
||||
<td class="text-center">1</td>
|
||||
<td>
|
||||
Plated parts — Job <span t-esc="doc.job_ref or doc.name"/>
|
||||
Plated parts - Job <span t-esc="doc.job_ref or doc.name"/>
|
||||
<t t-if="doc.notes">
|
||||
<br/><span t-field="doc.notes"/>
|
||||
</t>
|
||||
</td>
|
||||
<td class="text-center">—</td>
|
||||
<td class="text-center">—</td>
|
||||
<td class="text-center">-</td>
|
||||
<td class="text-center">-</td>
|
||||
<td class="text-center">
|
||||
<span t-if="doc.tdg_required">TDG</span>
|
||||
<span t-else="">NON-HAZ</span>
|
||||
@@ -413,11 +413,11 @@
|
||||
<tr>
|
||||
<td class="text-center">
|
||||
<span t-if="doc.coc_attachment_id" class="status-ok">✓ Attached</span>
|
||||
<span t-else="">—</span>
|
||||
<span t-else="">-</span>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<span t-if="doc.packing_list_attachment_id" class="status-ok">✓ Attached</span>
|
||||
<span t-else="">—</span>
|
||||
<span t-else="">-</span>
|
||||
</td>
|
||||
<td class="text-center"><span t-field="doc.custody_event_count"/></td>
|
||||
</tr>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<!--
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
Fusion Plating — Invoice (Portrait + Landscape).
|
||||
Fusion Plating - Invoice (Portrait + Landscape).
|
||||
Binds to account.move. Includes invoice strategy, deposit context,
|
||||
payment details, and amount due.
|
||||
-->
|
||||
@@ -153,8 +153,8 @@
|
||||
<td class="text-center"><span t-field="doc.invoice_date"/></td>
|
||||
<td class="text-center"><span t-field="doc.invoice_date_due"/></td>
|
||||
<td class="text-center"><span t-field="doc.invoice_user_id"/></td>
|
||||
<td class="text-center"><span t-esc="po_number or '—'"/></td>
|
||||
<td class="text-center"><span t-esc="doc.payment_reference or '—'"/></td>
|
||||
<td class="text-center"><span t-esc="po_number or '-'"/></td>
|
||||
<td class="text-center"><span t-esc="doc.payment_reference or '-'"/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -174,8 +174,8 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center"><span t-esc="source_so.x_fc_customer_job_number or '—'"/></td>
|
||||
<td class="text-center"><span t-esc="delivery_method_label or '—'"/></td>
|
||||
<td class="text-center"><span t-esc="source_so.x_fc_customer_job_number or '-'"/></td>
|
||||
<td class="text-center"><span t-esc="delivery_method_label or '-'"/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -231,14 +231,14 @@
|
||||
<t t-if="line.x_fc_part_catalog_id and line.x_fc_part_catalog_id.name">
|
||||
<span t-esc="line.x_fc_part_catalog_id.name"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</div>
|
||||
<div>
|
||||
<strong>S/N:</strong>
|
||||
<t t-if="line.x_fc_serial_ids">
|
||||
<span t-esc="', '.join(line.x_fc_serial_ids.mapped('name'))"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@@ -386,7 +386,7 @@
|
||||
<t t-set="delivery_method_label" t-value="dict(source_so._fields['x_fc_delivery_method'].selection).get(source_so.x_fc_delivery_method, '') if source_so and source_so.x_fc_delivery_method else ''"/>
|
||||
|
||||
<div class="fp-landscape fp-sale">
|
||||
<!-- 3-column inline header — same as portrait -->
|
||||
<!-- 3-column inline header - same as portrait -->
|
||||
<div class="fp-sale-header-row">
|
||||
<div class="fp-sale-header-left">
|
||||
<t t-if="logo_uri">
|
||||
@@ -497,8 +497,8 @@
|
||||
<td class="text-center"><span t-field="doc.invoice_date"/></td>
|
||||
<td class="text-center"><span t-field="doc.invoice_date_due"/></td>
|
||||
<td class="text-center"><span t-field="doc.invoice_user_id"/></td>
|
||||
<td class="text-center"><span t-esc="po_number or '—'"/></td>
|
||||
<td class="text-center"><span t-esc="doc.payment_reference or '—'"/></td>
|
||||
<td class="text-center"><span t-esc="po_number or '-'"/></td>
|
||||
<td class="text-center"><span t-esc="doc.payment_reference or '-'"/></td>
|
||||
<td class="text-center"><span t-field="doc.currency_id.name"/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -519,8 +519,8 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center"><span t-esc="source_so.x_fc_customer_job_number or '—'"/></td>
|
||||
<td class="text-center"><span t-esc="delivery_method_label or '—'"/></td>
|
||||
<td class="text-center"><span t-esc="source_so.x_fc_customer_job_number or '-'"/></td>
|
||||
<td class="text-center"><span t-esc="delivery_method_label or '-'"/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -582,14 +582,14 @@
|
||||
<t t-if="line.x_fc_part_catalog_id and line.x_fc_part_catalog_id.name">
|
||||
<span t-esc="line.x_fc_part_catalog_id.name"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</div>
|
||||
<div>
|
||||
<strong>S/N:</strong>
|
||||
<t t-if="line.x_fc_serial_ids">
|
||||
<span t-esc="', '.join(line.x_fc_serial_ids.mapped('name'))"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@@ -605,7 +605,7 @@
|
||||
</td>
|
||||
<td t-if="has_discount" class="text-center">
|
||||
<t t-if="line.discount"><span t-esc="line.discount"/>%</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<span t-field="line.price_subtotal" t-options='{"widget": "monetary", "display_currency": doc.currency_id}'/>
|
||||
@@ -697,8 +697,8 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="3" style="background-color: #28a745; color: white;">
|
||||
<t t-if="doc.payment_state in ('paid', 'in_payment')">✓ PAYMENT DETAILS — PAID IN FULL</t>
|
||||
<t t-elif="doc.payment_state == 'partial'">PAYMENT DETAILS — PARTIALLY PAID</t>
|
||||
<t t-if="doc.payment_state in ('paid', 'in_payment')">✓ PAYMENT DETAILS - PAID IN FULL</t>
|
||||
<t t-elif="doc.payment_state == 'partial'">PAYMENT DETAILS - PARTIALLY PAID</t>
|
||||
<t t-else="">PAYMENT DETAILS</t>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
Part of the Fusion Plating product family.
|
||||
|
||||
Fusion Plating — Job Traveller (Shop Router).
|
||||
Fusion Plating - Job Traveller (Shop Router).
|
||||
|
||||
One centralised document that follows a job through the shop. Pulls
|
||||
together everything about the MO: customer + PO, receiving, recipe,
|
||||
@@ -22,7 +22,7 @@
|
||||
<odoo>
|
||||
|
||||
<!-- ============================================================= -->
|
||||
<!-- INNER BODY — shared between portrait and landscape -->
|
||||
<!-- INNER BODY - shared between portrait and landscape -->
|
||||
<!-- Receives `mo` (mrp.production) in the t-call context. -->
|
||||
<!-- ============================================================= -->
|
||||
<template id="report_fp_job_traveller_body">
|
||||
@@ -40,7 +40,7 @@
|
||||
<!-- ===== Title bar ===== -->
|
||||
<div style="display: flex; justify-content: space-between; align-items: flex-end; margin-bottom: 10px;">
|
||||
<h2 style="margin: 0; font-size: 20pt;">
|
||||
Job Traveller — <span t-field="mo.name"/>
|
||||
Job Traveller - <span t-field="mo.name"/>
|
||||
</h2>
|
||||
<div style="text-align: right;">
|
||||
<t t-if="mo.x_fc_is_rework">
|
||||
@@ -59,7 +59,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ===== 1. JOB HEADER — Customer / PO / Part / Qty / Dates ===== -->
|
||||
<!-- ===== 1. JOB HEADER - Customer / PO / Part / Qty / Dates ===== -->
|
||||
<table class="bordered">
|
||||
<thead>
|
||||
<tr><th colspan="6" class="fp-header-primary">JOB HEADER</th></tr>
|
||||
@@ -73,12 +73,12 @@
|
||||
<th class="info-header" style="width: 10%;">Sale Order</th>
|
||||
<td class="text-center" style="width: 15%;">
|
||||
<t t-if="so"><span t-field="so.name"/></t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
<th class="info-header" style="width: 10%;">Customer PO #</th>
|
||||
<td class="text-center" style="width: 15%;">
|
||||
<t t-if="so and so.x_fc_po_number"><span t-field="so.x_fc_po_number"/></t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -115,7 +115,7 @@
|
||||
<t t-if="so and so.x_fc_customer_spec_id">
|
||||
<span t-field="so.x_fc_customer_spec_id"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
<th class="info-header">Recipe</th>
|
||||
<td>
|
||||
@@ -131,7 +131,7 @@
|
||||
<t t-if="mo.date_start">
|
||||
<span t-field="mo.date_start" t-options="{'widget': 'date'}"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
<th class="info-header">Target Ship</th>
|
||||
<td class="text-center">
|
||||
@@ -141,7 +141,7 @@
|
||||
<t t-elif="so and so.commitment_date">
|
||||
<span t-field="so.commitment_date" t-options="{'widget': 'date'}"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
<th class="info-header">Current Location</th>
|
||||
<td class="text-center">
|
||||
@@ -151,7 +151,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- ===== 1b. PART DESCRIPTIONS (Sub 2) — internal report keeps the service
|
||||
<!-- ===== 1b. PART DESCRIPTIONS (Sub 2) - internal report keeps the service
|
||||
SKU visible while surfacing customer-facing + internal workflow text. -->
|
||||
<t t-set="_trav_line" t-value="(mo.x_fc_sale_order_line_ids.filtered(lambda l: l.product_id == mo.product_id)[:1] or mo.x_fc_sale_order_line_ids[:1]) if 'x_fc_sale_order_line_ids' in mo._fields else mo.env['sale.order.line']"/>
|
||||
<t t-if="(not _trav_line) and so">
|
||||
@@ -165,11 +165,11 @@
|
||||
<tr>
|
||||
<th class="info-header" style="width: 20%;">Customer-Facing Description</th>
|
||||
<td style="width: 40%;">
|
||||
<t t-if="_trav_line"><span t-esc="_trav_line.name or '—'"/></t>
|
||||
<t t-else="">—</t>
|
||||
<t t-if="_trav_line"><span t-esc="_trav_line.name or '-'"/></t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
<th class="info-header" style="width: 15%;">Service SKU</th>
|
||||
<td style="width: 25%;"><span t-esc="mo.product_id.default_code or '—'"/></td>
|
||||
<td style="width: 25%;"><span t-esc="mo.product_id.default_code or '-'"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="info-header">Internal Description / Workflow</th>
|
||||
@@ -177,7 +177,7 @@
|
||||
<t t-if="_trav_line and 'x_fc_internal_description' in _trav_line._fields and _trav_line.x_fc_internal_description">
|
||||
<span t-esc="_trav_line.x_fc_internal_description" style="white-space: pre-wrap;"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -203,11 +203,11 @@
|
||||
<t t-if="rec.received_date">
|
||||
<span t-field="rec.received_date" t-options="{'widget': 'datetime'}"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<t t-if="'quantity_received' in rec._fields"><span t-esc="rec.quantity_received"/></t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
<td class="text-center"><span t-field="rec.state"/></td>
|
||||
<td>
|
||||
@@ -216,7 +216,7 @@
|
||||
<i class="fa fa-warning"/> <span t-esc="len(rec.damage_ids)"/> damage entries
|
||||
</span>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
@@ -228,7 +228,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- ===== 3. ROUTING TABLE — the main event ===== -->
|
||||
<!-- ===== 3. ROUTING TABLE - the main event ===== -->
|
||||
<table class="bordered" style="margin-top: 12px;">
|
||||
<thead>
|
||||
<tr><th colspan="12" class="fp-header-primary">PROCESS ROUTING</th></tr>
|
||||
@@ -260,19 +260,19 @@
|
||||
<span t-esc="wo.x_fc_thickness_target"/>
|
||||
<span t-esc="dict(wo._fields['x_fc_thickness_uom'].selection).get(wo.x_fc_thickness_uom, '')"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<t t-if="wo.x_fc_dwell_time_minutes">
|
||||
<span t-esc="wo.x_fc_dwell_time_minutes"/>m
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<t t-if="wo.duration_expected">
|
||||
<span t-esc="'%.0f' % wo.duration_expected"/>m
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
<td class="sig-line"/>
|
||||
<td class="sig-line"/>
|
||||
@@ -312,9 +312,9 @@
|
||||
<span t-field="bath.name"/>
|
||||
</td>
|
||||
<td><span t-field="p.parameter_id"/></td>
|
||||
<td class="text-center"><span t-esc="p.target_min or '—'"/></td>
|
||||
<td class="text-center"><span t-esc="p.target_max or '—'"/></td>
|
||||
<td class="text-center"><span t-esc="p.uom or '—'"/></td>
|
||||
<td class="text-center"><span t-esc="p.target_min or '-'"/></td>
|
||||
<td class="text-center"><span t-esc="p.target_max or '-'"/></td>
|
||||
<td class="text-center"><span t-esc="p.uom or '-'"/></td>
|
||||
</tr>
|
||||
</t>
|
||||
</t>
|
||||
@@ -322,7 +322,7 @@
|
||||
</table>
|
||||
</t>
|
||||
|
||||
<!-- ===== 5. QUALITY HOLDS — only if present ===== -->
|
||||
<!-- ===== 5. QUALITY HOLDS - only if present ===== -->
|
||||
<t t-if="holds">
|
||||
<div class="highlight-box" style="border-color: #c62828; background-color: #ffebee; margin-top: 12px;">
|
||||
<strong class="status-fail">
|
||||
@@ -354,7 +354,7 @@
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- ===== 6. CERTIFICATES + DELIVERY — side by side ===== -->
|
||||
<!-- ===== 6. CERTIFICATES + DELIVERY - side by side ===== -->
|
||||
<table style="margin-top: 12px; border: none;">
|
||||
<tr style="border: none;">
|
||||
<td style="width: 50%; padding-right: 6px; border: none; vertical-align: top;">
|
||||
@@ -401,13 +401,13 @@
|
||||
<td class="text-center"><span t-field="dlv.state"/></td>
|
||||
<td class="text-center">
|
||||
<t t-if="dlv.assigned_driver_id"><span t-field="dlv.assigned_driver_id"/></t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<t t-if="'tracking_ref' in dlv._fields and dlv.tracking_ref">
|
||||
<span t-field="dlv.tracking_ref"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
@@ -420,7 +420,7 @@
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- ===== 7. REWORK REASON — only if rework ===== -->
|
||||
<!-- ===== 7. REWORK REASON - only if rework ===== -->
|
||||
<t t-if="mo.x_fc_is_rework and mo.x_fc_rework_reason">
|
||||
<div class="highlight-box" style="margin-top: 12px;">
|
||||
<strong>Rework Reason:</strong>
|
||||
@@ -459,12 +459,12 @@
|
||||
|
||||
<p class="small-muted" style="margin-top: 12px; text-align: right;">
|
||||
Generated <span t-esc="mo.env.cr.now()" t-options="{'widget': 'datetime'}"/>
|
||||
— This traveller must remain with the parts through all operations.
|
||||
- This traveller must remain with the parts through all operations.
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<!-- ============================================================= -->
|
||||
<!-- MO-based Traveller — LANDSCAPE (default) -->
|
||||
<!-- MO-based Traveller - LANDSCAPE (default) -->
|
||||
<!-- ============================================================= -->
|
||||
<template id="report_fp_job_traveller_mo_landscape">
|
||||
<t t-call="web.html_container">
|
||||
@@ -483,7 +483,7 @@
|
||||
</template>
|
||||
|
||||
<!-- ============================================================= -->
|
||||
<!-- MO-based Traveller — PORTRAIT -->
|
||||
<!-- MO-based Traveller - PORTRAIT -->
|
||||
<!-- ============================================================= -->
|
||||
<template id="report_fp_job_traveller_mo_portrait">
|
||||
<t t-call="web.html_container">
|
||||
@@ -502,13 +502,13 @@
|
||||
</template>
|
||||
|
||||
<!-- ============================================================= -->
|
||||
<!-- SO-based Traveller — iterates the SO's MOs -->
|
||||
<!-- SO-based Traveller - iterates the SO's MOs -->
|
||||
<!-- Landscape default -->
|
||||
<!-- ============================================================= -->
|
||||
<template id="report_fp_job_traveller_so_landscape">
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="docs" t-as="so">
|
||||
<!-- Sub 11 — MRP gone; find fp.jobs for this SO and delegate
|
||||
<!-- Sub 11 - MRP gone; find fp.jobs for this SO and delegate
|
||||
to the native fp.job traveller template (jobs module). -->
|
||||
<t t-set="jobs" t-value="so.env['fp.job'].search([('sale_order_id', '=', so.id)])"/>
|
||||
<t t-if="not jobs">
|
||||
@@ -517,7 +517,7 @@
|
||||
<t t-call="fusion_plating_reports.fp_landscape_styles"/>
|
||||
<div class="fp-landscape">
|
||||
<div class="page">
|
||||
<h2>Job Traveller — <span t-field="so.name"/></h2>
|
||||
<h2>Job Traveller - <span t-field="so.name"/></h2>
|
||||
<div class="highlight-box">
|
||||
<strong class="status-warning">
|
||||
<i class="fa fa-info-circle"/>
|
||||
@@ -531,7 +531,7 @@
|
||||
<t t-foreach="jobs" t-as="job">
|
||||
<t t-call="web.external_layout">
|
||||
<div class="page">
|
||||
<h1>Job Traveller — <span t-esc="job.name"/></h1>
|
||||
<h1>Job Traveller - <span t-esc="job.name"/></h1>
|
||||
<table class="table table-sm" style="margin-top: 1em;">
|
||||
<tr><th>Customer</th><td><span t-esc="job.partner_id.name"/></td></tr>
|
||||
<tr><th>SO</th><td><span t-esc="job.sale_order_id.name or '-'"/></td></tr>
|
||||
@@ -563,7 +563,7 @@
|
||||
</template>
|
||||
|
||||
<!-- ============================================================= -->
|
||||
<!-- SO-based Traveller — PORTRAIT -->
|
||||
<!-- SO-based Traveller - PORTRAIT -->
|
||||
<!-- ============================================================= -->
|
||||
<template id="report_fp_job_traveller_so_portrait">
|
||||
<t t-call="web.html_container">
|
||||
@@ -575,7 +575,7 @@
|
||||
<t t-call="fusion_plating_reports.fp_portrait_styles"/>
|
||||
<div class="fp-report">
|
||||
<div class="page">
|
||||
<h4>Job Traveller — <span t-field="so.name"/></h4>
|
||||
<h4>Job Traveller - <span t-field="so.name"/></h4>
|
||||
<div class="highlight-box">
|
||||
<strong class="status-warning">
|
||||
<i class="fa fa-info-circle"/>
|
||||
@@ -589,7 +589,7 @@
|
||||
<t t-foreach="jobs" t-as="job">
|
||||
<t t-call="web.external_layout">
|
||||
<div class="page">
|
||||
<h2>Job Traveller — <span t-esc="job.name"/></h2>
|
||||
<h2>Job Traveller - <span t-esc="job.name"/></h2>
|
||||
<table class="table table-sm" style="margin-top: 1em;">
|
||||
<tr><th>Customer</th><td><span t-esc="job.partner_id.name"/></td></tr>
|
||||
<tr><th>SO</th><td><span t-esc="job.sale_order_id.name or '-'"/></td></tr>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<!--
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
Fusion Plating — Packing Slip / Shipping Confirmation (Portrait + Landscape).
|
||||
Fusion Plating - Packing Slip / Shipping Confirmation (Portrait + Landscape).
|
||||
Binds to stock.picking. Bill-To / Ship-To boxes, bilingual column
|
||||
headers, Received-By signature block and a QR code for scan-to-sign.
|
||||
-->
|
||||
@@ -17,7 +17,7 @@
|
||||
margin_top=8mm; the body padding-top clears the header
|
||||
band. Same trick as .fp-coc and .fp-sale. */
|
||||
.fp-report.fp-ps .page { padding-top: 20mm; }
|
||||
/* Title bar: float div layout (NO table — see CLAUDE.md
|
||||
/* Title bar: float div layout (NO table - see CLAUDE.md
|
||||
wkhtmltopdf overlap §2). Stacked bilingual title with
|
||||
English bold on top and French italic-grey below. */
|
||||
.fp-ps-titlebar { margin: 0 0 10px 0; padding: 0; overflow: hidden; }
|
||||
@@ -54,7 +54,7 @@
|
||||
<!-- Address box content (shared by portrait + landscape).
|
||||
|
||||
NOTE: `t-field` in Odoo 19 requires a dotted path
|
||||
("record.field_name") — passing a bare `partner` variable
|
||||
("record.field_name") - passing a bare `partner` variable
|
||||
via t-set and then `t-field="partner"` raises
|
||||
`AssertionError: t-field must have at least a dot`.
|
||||
The contact-widget pattern (`t-field="x.partner_id"
|
||||
@@ -204,7 +204,7 @@
|
||||
<t t-set="has_carrier" t-value="'carrier_id' in doc._fields and doc.carrier_id"/>
|
||||
<t t-set="ship_via" t-value="(doc.carrier_id.name if has_carrier else (doc.sale_id.x_fc_ship_via if doc.sale_id and 'x_fc_ship_via' in doc.sale_id._fields and doc.sale_id.x_fc_ship_via else 'CUSTOMER PICKUP'))"/>
|
||||
<t t-set="tracking_ref" t-value="doc.carrier_tracking_ref if 'carrier_tracking_ref' in doc._fields and doc.carrier_tracking_ref else False"/>
|
||||
<t t-set="tracking_text" t-value="tracking_ref if tracking_ref else ('Ready for pick up' if not has_carrier else '—')"/>
|
||||
<t t-set="tracking_text" t-value="tracking_ref if tracking_ref else ('Ready for pick up' if not has_carrier else '-')"/>
|
||||
<t t-set="po_number" t-value="(doc.sale_id.client_order_ref if doc.sale_id and doc.sale_id.client_order_ref else '')"/>
|
||||
|
||||
<!-- Packing slip number derived from the SO so it
|
||||
@@ -224,7 +224,7 @@
|
||||
<!-- Code128 barcode encoding the packing-slip number so
|
||||
the printed label matches the visible title. Inlined
|
||||
via barcode_data_uri (no /report/barcode/ HTTP fetch
|
||||
— wkhtmltopdf network calls fail on entech). -->
|
||||
- wkhtmltopdf network calls fail on entech). -->
|
||||
<t t-set="ps_barcode_uri" t-value="doc.env['ir.actions.report'].sudo().barcode_data_uri('Code128', ps_number, 600, 100) if ps_number else False"/>
|
||||
|
||||
<t t-set="qr_payload" t-value="doc.name or ''"/>
|
||||
@@ -292,7 +292,7 @@
|
||||
<t t-if="doc.scheduled_date">
|
||||
<span t-field="doc.scheduled_date" t-options="{'widget': 'date'}"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
<td><span t-esc="tracking_text"/></td>
|
||||
</tr>
|
||||
@@ -344,7 +344,7 @@
|
||||
<t t-set="has_carrier" t-value="'carrier_id' in doc._fields and doc.carrier_id"/>
|
||||
<t t-set="ship_via" t-value="(doc.carrier_id.name if has_carrier else (doc.sale_id.x_fc_ship_via if doc.sale_id and 'x_fc_ship_via' in doc.sale_id._fields and doc.sale_id.x_fc_ship_via else 'CUSTOMER PICKUP'))"/>
|
||||
<t t-set="tracking_ref" t-value="doc.carrier_tracking_ref if 'carrier_tracking_ref' in doc._fields and doc.carrier_tracking_ref else False"/>
|
||||
<t t-set="tracking_text" t-value="tracking_ref if tracking_ref else ('Ready for pick up' if not has_carrier else '—')"/>
|
||||
<t t-set="tracking_text" t-value="tracking_ref if tracking_ref else ('Ready for pick up' if not has_carrier else '-')"/>
|
||||
<t t-set="po_number" t-value="(doc.sale_id.client_order_ref if doc.sale_id and doc.sale_id.client_order_ref else '')"/>
|
||||
|
||||
<!-- See portrait template for the numbering logic. -->
|
||||
@@ -357,7 +357,7 @@
|
||||
<!-- Code128 barcode encoding the packing-slip number so
|
||||
the printed label matches the visible title. Inlined
|
||||
via barcode_data_uri (no /report/barcode/ HTTP fetch
|
||||
— wkhtmltopdf network calls fail on entech). -->
|
||||
- wkhtmltopdf network calls fail on entech). -->
|
||||
<t t-set="ps_barcode_uri" t-value="doc.env['ir.actions.report'].sudo().barcode_data_uri('Code128', ps_number, 600, 100) if ps_number else False"/>
|
||||
|
||||
<t t-set="qr_payload" t-value="doc.name or ''"/>
|
||||
@@ -423,7 +423,7 @@
|
||||
<t t-if="doc.scheduled_date">
|
||||
<span t-field="doc.scheduled_date" t-options="{'widget': 'date'}"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
<td><span t-esc="tracking_text"/></td>
|
||||
</tr>
|
||||
@@ -575,7 +575,7 @@
|
||||
<t t-set="bill_partner" t-value="(_so.partner_invoice_id if _so and _so.partner_invoice_id else (ship_partner.commercial_partner_id or ship_partner))"/>
|
||||
<t t-set="has_carrier" t-value="m == 'fusion.plating.delivery' and 'x_fc_carrier_id' in doc._fields and doc.x_fc_carrier_id"/>
|
||||
<t t-set="ship_via" t-value="(doc.x_fc_carrier_id.name if has_carrier else (_so.x_fc_ship_via if _so and 'x_fc_ship_via' in _so._fields and _so.x_fc_ship_via else 'CUSTOMER PICKUP'))"/>
|
||||
<t t-set="tracking_text" t-value="'Ready for pick up' if not has_carrier else '—'"/>
|
||||
<t t-set="tracking_text" t-value="'Ready for pick up' if not has_carrier else '-'"/>
|
||||
<t t-set="po_number" t-value="(_so.client_order_ref if _so and _so.client_order_ref else '')"/>
|
||||
<t t-set="_scheduled" t-value="doc.scheduled_date if m == 'fusion.plating.delivery' else (doc.commitment_date if m == 'sale.order' else (_so.commitment_date if _so else False))"/>
|
||||
<t t-set="_notes" t-value="doc.notes if m == 'fusion.plating.delivery' else (doc.note if m == 'sale.order' else False)"/>
|
||||
@@ -643,7 +643,7 @@
|
||||
<t t-if="_scheduled">
|
||||
<span t-out="_scheduled" t-options="{'widget': 'date'}"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
<td><span t-esc="tracking_text"/></td>
|
||||
</tr>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
|
||||
Sub 12 Phase E — Monthly Quality Summary report.
|
||||
Sub 12 Phase E - Monthly Quality Summary report.
|
||||
On-demand from the Quality Dashboard. Counts by record type / severity /
|
||||
customer. Overdue ageing buckets. CAPA effectiveness rate. Repeat-customer
|
||||
flag (>2 NCRs same customer in 90 days). Run via menu action
|
||||
@@ -20,7 +20,7 @@
|
||||
<h1 style="margin: 0; font-size: 24px;">Monthly Quality Summary</h1>
|
||||
<div style="font-size: 13px; color: #666; margin-top: 4px;">
|
||||
<span t-out="company.name"/>
|
||||
— <span t-out="data['period_label']"/>
|
||||
- <span t-out="data['period_label']"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
Part of the Fusion Plating product family.
|
||||
|
||||
Sub 12c+ — Rack Travel Ticket.
|
||||
Sub 12c+ - Rack Travel Ticket.
|
||||
Closes the gap left by Sub 12b's Rack Parts dialog 'Save + Print'
|
||||
button. Operator presses Save + Print → tablet opens
|
||||
/web/report/pdf/fp.rack.travel/<rack_id> in a new tab → this report
|
||||
@@ -17,7 +17,7 @@
|
||||
<odoo>
|
||||
|
||||
<record id="paperformat_fp_rack_travel" model="report.paperformat">
|
||||
<field name="name">FP Rack Travel — A5 landscape</field>
|
||||
<field name="name">FP Rack Travel - A5 landscape</field>
|
||||
<field name="format">A5</field>
|
||||
<field name="orientation">Landscape</field>
|
||||
<field name="margin_top">8</field>
|
||||
@@ -70,9 +70,9 @@
|
||||
 · 
|
||||
<strong>State:</strong>
|
||||
<t t-if="'racking_state' in rack._fields">
|
||||
<span t-esc="dict(rack._fields['racking_state'].selection).get(rack.racking_state, rack.racking_state) or '—'"/>
|
||||
<span t-esc="dict(rack._fields['racking_state'].selection).get(rack.racking_state, rack.racking_state) or '-'"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</div>
|
||||
<div style="margin-top: 6px;">
|
||||
<t t-if="'tag_ids' in rack._fields">
|
||||
@@ -106,13 +106,13 @@
|
||||
<td><span t-esc="(b.qty_done or 0) - (b.qty_scrapped or 0)"/></td>
|
||||
<td>
|
||||
<t t-if="b.job_id and b.job_id.product_id">
|
||||
<span t-esc="b.job_id.product_id.default_code or b.job_id.product_id.name or '—'"/>
|
||||
<span t-esc="b.job_id.product_id.default_code or b.job_id.product_id.name or '-'"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
<td><span t-esc="(b.job_id and b.job_id.name) or '—'"/></td>
|
||||
<td><span t-esc="(b.job_id and b.job_id.partner_id and b.job_id.partner_id.name) or '—'"/></td>
|
||||
<td><span t-esc="b.name or '—'"/></td>
|
||||
<td><span t-esc="(b.job_id and b.job_id.name) or '-'"/></td>
|
||||
<td><span t-esc="(b.job_id and b.job_id.partner_id and b.job_id.partner_id.name) or '-'"/></td>
|
||||
<td><span t-esc="b.name or '-'"/></td>
|
||||
</tr>
|
||||
</t>
|
||||
<t t-else="">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<!--
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
Fusion Plating — Payment Receipt (Portrait + Landscape).
|
||||
Fusion Plating - Payment Receipt (Portrait + Landscape).
|
||||
Binds to account.payment. Shows amount paid, method, reference,
|
||||
applied invoices, and a "PAID" stamp.
|
||||
-->
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
|
||||
Sub 12 Phase E — RMA Authorisation PDF.
|
||||
Sub 12 Phase E - RMA Authorisation PDF.
|
||||
Single-page customer-facing PDF emailed when an RMA is authorised.
|
||||
Contains: our header, customer info, RMA number, parts table, return
|
||||
address, QR code linking to /fp/rma/<id>, and carrier instructions.
|
||||
@@ -19,7 +19,7 @@
|
||||
<div>
|
||||
<h1 style="margin: 0; font-size: 26px;">Return Material Authorisation</h1>
|
||||
<p style="margin: 4px 0 0 0; font-size: 14px; color: #666;">
|
||||
EN Technologies — Plating & Metal Finishing
|
||||
EN Technologies - Plating & Metal Finishing
|
||||
</p>
|
||||
</div>
|
||||
<div style="text-align: right;">
|
||||
@@ -95,13 +95,13 @@
|
||||
<li>Print this authorisation and include it with your shipment.</li>
|
||||
<li>Pack returned parts in their <strong>original boxes</strong> (we ship back in the same boxes per shop policy).</li>
|
||||
<li>Mark each box clearly with the RMA number <strong t-out="rma.name"/>.</li>
|
||||
<li>Ship to the address below — pre-paid carrier of your choice.</li>
|
||||
<li>Ship to the address below - pre-paid carrier of your choice.</li>
|
||||
<li>Send your tracking number to your account contact so we can monitor the return.</li>
|
||||
</ol>
|
||||
<p style="font-size: 12px; margin-top: 12px;"><strong>Return Address:</strong></p>
|
||||
<p style="font-size: 12px; margin-left: 14px;">
|
||||
EN Technologies<br/>
|
||||
Receiving — RMA <span t-out="rma.name"/><br/>
|
||||
Receiving - RMA <span t-out="rma.name"/><br/>
|
||||
[shop street address]<br/>
|
||||
[city, province, postal code]
|
||||
</p>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<!--
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
Fusion Plating — Quotation / Sales Order (Portrait + Landscape)
|
||||
Fusion Plating - Quotation / Sales Order (Portrait + Landscape)
|
||||
Renders the same sale.order with a title that flips between
|
||||
"Quotation" (draft/sent) and "Sales Order" (confirmed/done).
|
||||
-->
|
||||
@@ -20,14 +20,14 @@
|
||||
margin-top) pushed the title up INTO the wkhtmltopdf header zone
|
||||
(the company logo band) and clipped the top of the H1 glyphs.
|
||||
External_layout already places the page body at the bottom of
|
||||
the reserved margin-top — don't fight that. Use a small positive
|
||||
the reserved margin-top - don't fight that. Use a small positive
|
||||
gap and shrink the title text instead. -->
|
||||
<!-- Custom minimal layout — same .article wrapper that Odoo's
|
||||
<!-- Custom minimal layout - same .article wrapper that Odoo's
|
||||
report pipeline expects (so UTF-8 charset handling works
|
||||
correctly via the standard processing path), but with NO
|
||||
auto company .header div. Includes a minimal .footer div
|
||||
that ONLY carries the wkhtmltopdf page-number placeholders
|
||||
(`<span class="page"/> / <span class="topage"/>`) — those
|
||||
(`<span class="page"/> / <span class="topage"/>`) - those
|
||||
only get filled in when the .footer div is extracted into
|
||||
wkhtmltopdf's footer-html stream. The .footer is otherwise
|
||||
empty so no boilerplate company info shows. -->
|
||||
@@ -69,12 +69,12 @@
|
||||
and wraps to two naturally if the cell is narrow. Apply
|
||||
this everywhere except super-narrow cells (QTY, UOM)
|
||||
where the cell is physically too tight even for the
|
||||
shortest French word — those use the stacked variant
|
||||
shortest French word - those use the stacked variant
|
||||
below. */
|
||||
.fp-bl-en { font-weight: bold; }
|
||||
.fp-bl-sep { color: #999; margin: 0 3px; font-weight: normal; }
|
||||
.fp-bl-fr { font-weight: normal; font-style: italic; color: #555; }
|
||||
/* Stacked variant for narrow cells — EN on top line, FR
|
||||
/* Stacked variant for narrow cells - EN on top line, FR
|
||||
below in italic-grey. */
|
||||
.fp-bl-en-stk { display: block; font-weight: bold; }
|
||||
.fp-bl-fr-stk { display: block; font-weight: normal; font-style: italic; color: #555; font-size: 80%; margin-top: 1px; }
|
||||
@@ -125,12 +125,12 @@
|
||||
.fp-sale-company-addr div { margin: 0; }
|
||||
.fp-sale-company-addr a { color: #2e6da4; text-decoration: none; }
|
||||
|
||||
/* Inline footer line — phone | email | website | tax id.
|
||||
/* Inline footer line - phone | email | website | tax id.
|
||||
One-time render at the bottom of page 1 (multi-page SO
|
||||
reports are rare; if we ever need it, switch to
|
||||
wkhtmltopdf --footer-html). */
|
||||
.fp-sale-customfooter { text-align: center; font-size: 8pt; color: #666; margin-top: 24px; padding-top: 8px; border-top: 1px solid #ccc; }
|
||||
/* Title bar uses float-based div layout, NOT an HTML table —
|
||||
/* Title bar uses float-based div layout, NOT an HTML table -
|
||||
the global ".fp-report table" rule was applying borders
|
||||
to every nested table even with "border: 0 !important",
|
||||
so the only reliable fix is to avoid the table element. */
|
||||
@@ -178,7 +178,7 @@
|
||||
|
||||
<div class="fp-report fp-sale">
|
||||
<!-- Inline header (drops web.external_layout for this
|
||||
report — see CSS comment for context). Left: logo
|
||||
report - see CSS comment for context). Left: logo
|
||||
+ address + tel/fax + URL. Right: bilingual title
|
||||
+ Code128 barcode of the order number. -->
|
||||
<div class="fp-sale-header-row">
|
||||
@@ -228,7 +228,7 @@
|
||||
|
||||
<div class="page">
|
||||
|
||||
<!-- Billing / Shipping (wide cells — inline) -->
|
||||
<!-- Billing / Shipping (wide cells - inline) -->
|
||||
<table class="bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -254,7 +254,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- Row 1: 5 narrow cells (20% each) — stacked
|
||||
<!-- Row 1: 5 narrow cells (20% each) - stacked
|
||||
so the French label doesn't overflow into
|
||||
the next column. -->
|
||||
<table class="bordered">
|
||||
@@ -282,7 +282,7 @@
|
||||
<tr>
|
||||
<td class="text-center"><span t-field="doc.date_order" t-options="{'widget': 'date'}"/></td>
|
||||
<td class="text-center"><span t-field="doc.user_id"/></td>
|
||||
<td class="text-center"><span t-esc="doc.x_fc_po_number or '—'"/></td>
|
||||
<td class="text-center"><span t-esc="doc.x_fc_po_number or '-'"/></td>
|
||||
<td class="text-center">
|
||||
<!-- Lead Time renders from the
|
||||
computed display string on
|
||||
@@ -301,7 +301,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- Row 2: 2 wide cells (50% each) — inline. -->
|
||||
<!-- Row 2: 2 wide cells (50% each) - inline. -->
|
||||
<t t-if="doc.x_fc_customer_job_number or delivery_method_label">
|
||||
<table class="bordered">
|
||||
<thead>
|
||||
@@ -316,8 +316,8 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center"><span t-esc="doc.x_fc_customer_job_number or '—'"/></td>
|
||||
<td class="text-center"><span t-esc="delivery_method_label or '—'"/></td>
|
||||
<td class="text-center"><span t-esc="doc.x_fc_customer_job_number or '-'"/></td>
|
||||
<td class="text-center"><span t-esc="delivery_method_label or '-'"/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -337,7 +337,7 @@
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- Order lines. Taxes column dropped — taxes
|
||||
<!-- Order lines. Taxes column dropped - taxes
|
||||
summarized in the totals block below; per-line
|
||||
tax labels were noise on a single-tax-region
|
||||
plating order. The part-number cell appends
|
||||
@@ -397,19 +397,19 @@
|
||||
<t t-if="line.x_fc_part_catalog_id and line.x_fc_part_catalog_id.name">
|
||||
<span t-esc="line.x_fc_part_catalog_id.name"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</div>
|
||||
<div>
|
||||
<strong>S/N:</strong>
|
||||
<t t-if="line.x_fc_serial_ids">
|
||||
<span t-esc="', '.join(line.x_fc_serial_ids.mapped('name'))"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Rebind `line` with fp_no_serial_in_desc=True so the
|
||||
shared description macro skips its Serial line — we
|
||||
shared description macro skips its Serial line - we
|
||||
already render S/N in the part-number cell above. -->
|
||||
<t t-set="line" t-value="line.with_context(fp_no_serial_in_desc=True)"/>
|
||||
<t t-call="fusion_plating_reports.customer_line_description"/>
|
||||
@@ -648,7 +648,7 @@
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- Order lines — hide discount column unless at least one line has a discount -->
|
||||
<!-- Order lines - hide discount column unless at least one line has a discount -->
|
||||
<t t-set="has_discount" t-value="any(l.discount for l in doc.order_line)"/>
|
||||
<t t-set="col_count" t-value="8 if has_discount else 7"/>
|
||||
<table class="bordered">
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
|
||||
Parts-box identification sticker — printed on a 4x3" label.
|
||||
Parts-box identification sticker - printed on a 4x3" label.
|
||||
|
||||
Bound to mrp.production (MO), mrp.workorder (WO), fp.job, and
|
||||
sale.order. The shop talks in "WO #" terms (Steelhead legacy) but
|
||||
@@ -13,20 +13,20 @@
|
||||
resolves them itself from `_mo` when called from an mrp.* context.
|
||||
Variables an outer template MAY pre-set (otherwise falls back to
|
||||
`_mo`-based resolution):
|
||||
* _order_id — number to print as "WO #"
|
||||
* _scan_id — id encoded into the QR URL
|
||||
* _scan_path — '/fp/job/' or '/fp/wo/' prefix (default '/fp/wo/')
|
||||
* _mo — the mrp.production record (or False)
|
||||
* _so, _line — the originating sale order / line
|
||||
* _part — fp.part.catalog
|
||||
* _spec — fusion.plating.customer.spec (audit-tracked spec)
|
||||
* _process — the resolved fusion.plating.process.node tree
|
||||
* _due — datetime/date for "Due Date" row
|
||||
* _qty — float for "Qty" row
|
||||
* _po_number — overrides _so.x_fc_po_number
|
||||
* _partner_name — overrides _so.partner_id.name
|
||||
* _mo_ref — string shown muted in "(WH/MO/...)" — '' to hide
|
||||
* _internal_note— free text for "Notes" row
|
||||
* _order_id - number to print as "WO #"
|
||||
* _scan_id - id encoded into the QR URL
|
||||
* _scan_path - '/fp/job/' or '/fp/wo/' prefix (default '/fp/wo/')
|
||||
* _mo - the mrp.production record (or False)
|
||||
* _so, _line - the originating sale order / line
|
||||
* _part - fp.part.catalog
|
||||
* _spec - fusion.plating.customer.spec (audit-tracked spec)
|
||||
* _process - the resolved fusion.plating.process.node tree
|
||||
* _due - datetime/date for "Due Date" row
|
||||
* _qty - float for "Qty" row
|
||||
* _po_number - overrides _so.x_fc_po_number
|
||||
* _partner_name - overrides _so.partner_id.name
|
||||
* _mo_ref - string shown muted in "(WH/MO/...)" - '' to hide
|
||||
* _internal_note- free text for "Notes" row
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
else (_mo and _mo.product_qty) or 0"/>
|
||||
<t t-set="_po_number" t-value="_po_number or (_so and _so.x_fc_po_number) or '-'"/>
|
||||
<t t-set="_partner_name" t-value="_partner_name or (_so and _so.partner_id.name) or '-'"/>
|
||||
<!-- Customer short-code for shop-floor "secrecy cover" — operators
|
||||
<!-- Customer short-code for shop-floor "secrecy cover" - operators
|
||||
see "ABC-MANU" instead of "ABC Manufacturing Inc", so visiting
|
||||
customers / unauthorised passers-by can't immediately tell whose
|
||||
parts are on which rack. Rule: first 3 chars of word[0] + "-"
|
||||
@@ -84,36 +84,36 @@
|
||||
or (_so and _so.x_fc_internal_note
|
||||
and _so.x_fc_internal_note.striptags()[:100])
|
||||
or '-'"/>
|
||||
<!-- Serial number — Sub 5 added x_fc_serial_id (M2O fp.serial) on
|
||||
<!-- Serial number - Sub 5 added x_fc_serial_id (M2O fp.serial) on
|
||||
the SO line. The serial record's `name` is the printable label. -->
|
||||
<t t-set="_serial_number" t-value="(_line and 'x_fc_serial_id' in _line._fields and _line.x_fc_serial_id and _line.x_fc_serial_id.name) or '-'"/>
|
||||
<!-- Thickness — operator-typed Char range, e.g. "0.0005-0.0008 mils".
|
||||
<!-- Thickness - operator-typed Char range, e.g. "0.0005-0.0008 mils".
|
||||
Stored as-typed; ASCII-safe by convention. Strip en/em-dash
|
||||
defensively for the wkhtmltopdf font path on entech. -->
|
||||
<t t-set="_thickness_raw" t-value="_line and 'x_fc_thickness_range' in _line._fields and _line.x_fc_thickness_range"/>
|
||||
<t t-set="_thickness" t-value="(_thickness_raw and _thickness_raw.replace(u'–', '-').replace(u'—', '-')) or '-'"/>
|
||||
<!-- Notes content — outer can pre-set this (e.g. the Internal
|
||||
<t t-set="_thickness" t-value="(_thickness_raw and _thickness_raw.replace(u'\u2013', '-').replace(u'\u2014', '-')) or '-'"/>
|
||||
<!-- Notes content - outer can pre-set this (e.g. the Internal
|
||||
variant passes line.x_fc_internal_description). Otherwise
|
||||
falls back to line.name (customer-facing description per
|
||||
Sub 2 Q6), then to part.name. Strip en/em-dash, smart
|
||||
quotes, and ellipsis defensively for the wkhtmltopdf font
|
||||
path on entech — same treatment as thickness above, which
|
||||
otherwise turns "—" into the "â€"" mojibake. -->
|
||||
path on entech - same treatment as thickness above, which
|
||||
otherwise turns "-" into the "â€"" mojibake. -->
|
||||
<t t-set="_notes_raw" t-value="_notes_content
|
||||
or (_line and _line.name)
|
||||
or (_part and _part.name)
|
||||
or '-'"/>
|
||||
<t t-set="_notes_content" t-value="_notes_raw
|
||||
.replace(u'—', '-').replace(u'–', '-')
|
||||
.replace(u'\u2014', '-').replace(u'\u2013', '-')
|
||||
.replace(u'‘', "'").replace(u'’', "'")
|
||||
.replace(u'“', '"').replace(u'”', '"')
|
||||
.replace(u'…', '...')"/>
|
||||
<!-- Inline the QR as base64 data URI so wkhtmltopdf doesn't need
|
||||
to fetch /report/barcode/ over the network during rendering.
|
||||
1000x1000 source — Odoo core caps barcode area at 1.2M pixels
|
||||
1000x1000 source - Odoo core caps barcode area at 1.2M pixels
|
||||
(`width * height > 1200000` raises "Barcode too large"), so we
|
||||
stay under that ceiling. 1000x1000 at the 31mm wrapper gives
|
||||
~821ppi effective — far above the 203dpi thermal printer. -->
|
||||
~821ppi effective - far above the 203dpi thermal printer. -->
|
||||
<t t-set="_qr_src" t-value="env['ir.actions.report'].barcode_data_uri(
|
||||
'QR', _scan_url, width=1000, height=1000)"/>
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
}
|
||||
/* 3-cell header (Logo | WO# | QR) + 2-region body (fields left,
|
||||
Notes column right). Absolute positioning + % heights/widths
|
||||
are mandatory — wkhtmltopdf ignores vh/vw/flex. ----------- */
|
||||
are mandatory - wkhtmltopdf ignores vh/vw/flex. ----------- */
|
||||
.fp-sticker {
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
color: #000;
|
||||
@@ -251,7 +251,7 @@
|
||||
/* Values used to be bold via .fp-sticker-strong (PO/Part#/Qty).
|
||||
Per ops, only the field title and the WO# header should be
|
||||
bold; values stay regular weight. Font sizes unchanged from
|
||||
the original layout — bumping them broke wkhtmltopdf's row
|
||||
the original layout - bumping them broke wkhtmltopdf's row
|
||||
packing on entech, so we accept the same visual weight as
|
||||
before. -->
|
||||
.fp-sticker-strong { font-weight: 400; }
|
||||
@@ -415,7 +415,7 @@
|
||||
</template>
|
||||
|
||||
<!-- =====================================================
|
||||
Reusable defaults block — every outer template t-calls
|
||||
Reusable defaults block - every outer template t-calls
|
||||
this BEFORE the sticker inner so `_so`, `_line`, etc.
|
||||
are always defined. The inner's `_so or fallback`
|
||||
pattern relies on these names existing in scope.
|
||||
@@ -442,7 +442,7 @@
|
||||
<t t-set="_multi_line" t-value="False"/>
|
||||
</template>
|
||||
|
||||
<!-- ========== Outer template — mrp.workorder entry ========== -->
|
||||
<!-- ========== Outer template - mrp.workorder entry ========== -->
|
||||
<template id="report_fp_wo_sticker">
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="docs" t-as="doc">
|
||||
@@ -455,7 +455,7 @@
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- ========== Outer template — mrp.production entry ========== -->
|
||||
<!-- ========== Outer template - mrp.production entry ========== -->
|
||||
<template id="report_fp_mo_sticker">
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="docs" t-as="doc">
|
||||
@@ -471,16 +471,16 @@
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- ========== Outer template — sale.order entry ==========
|
||||
<!-- ========== Outer template - sale.order entry ==========
|
||||
Prints one box sticker per order line that has a part. Lines
|
||||
without x_fc_part_catalog_id (service lines, freight, etc.) are
|
||||
skipped — they don't go through plating so they don't need a
|
||||
skipped - they don't go through plating so they don't need a
|
||||
box sticker.
|
||||
|
||||
The "WO#" header shows the SO name (e.g. SO-30019). The body
|
||||
carries the part-specific fields (Part #, Customer, etc.) which
|
||||
disambiguate multi-line SOs without needing a sequence suffix.
|
||||
The QR encodes /fp/so-line/<line.id> — the controller can
|
||||
The QR encodes /fp/so-line/<line.id> - the controller can
|
||||
decide whether to land on the parent SO, the line, or (later)
|
||||
the spawned job. -->
|
||||
<template id="report_fp_so_sticker">
|
||||
@@ -492,7 +492,7 @@
|
||||
<!-- Multi-line PO: one consolidated sticker.
|
||||
Part # prints "Multiple Line Items", Qty is the
|
||||
sum of all part-line qtys. Per-box loop disabled
|
||||
(_qty_total=1) — the consolidated label is the
|
||||
(_qty_total=1) - the consolidated label is the
|
||||
master-skid label, not per-physical-box. -->
|
||||
<t t-call="fusion_plating_reports.report_fp_wo_sticker_defaults"/>
|
||||
<t t-set="_order_id" t-value="so.name"/>
|
||||
@@ -540,7 +540,7 @@
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- ========== Outer template — sale.order Internal variant ==========
|
||||
<!-- ========== Outer template - sale.order Internal variant ==========
|
||||
Same layout + iteration as report_fp_so_sticker, but pre-sets
|
||||
_notes_content from x_fc_internal_description (Sub 2 internal
|
||||
description field) so the Notes column shows the ops-facing
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<!--
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
Fusion Plating — Work Order traveller (Portrait + Landscape).
|
||||
Fusion Plating - Work Order traveller (Portrait + Landscape).
|
||||
Printed shop-floor sheet with step info, bath/tank, chemistry targets,
|
||||
and sign-off rows.
|
||||
-->
|
||||
@@ -92,14 +92,14 @@
|
||||
</t>
|
||||
</strong>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="background-color: #f5f5f5; font-weight: bold;">Customer-Facing Description</td>
|
||||
<td>
|
||||
<t t-if="_line"><span t-esc="_line.name or '—'"/></t>
|
||||
<t t-else="">—</t>
|
||||
<t t-if="_line"><span t-esc="_line.name or '-'"/></t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -108,12 +108,12 @@
|
||||
<t t-if="_line and 'x_fc_internal_description' in _line._fields and _line.x_fc_internal_description">
|
||||
<span t-esc="_line.x_fc_internal_description" style="white-space: pre-wrap;"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="background-color: #f5f5f5; font-weight: bold;">Service SKU</td>
|
||||
<td><span t-esc="doc.product_id.default_code or '—'"/></td>
|
||||
<td><span t-esc="doc.product_id.default_code or '-'"/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -348,19 +348,19 @@
|
||||
</t>
|
||||
</strong>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
<td>
|
||||
<t t-if="_line"><span t-esc="_line.name or '—'"/></t>
|
||||
<t t-else="">—</t>
|
||||
<t t-if="_line"><span t-esc="_line.name or '-'"/></t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
<td>
|
||||
<t t-if="_line and 'x_fc_internal_description' in _line._fields and _line.x_fc_internal_description">
|
||||
<span t-esc="_line.x_fc_internal_description" style="white-space: pre-wrap;"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
<td class="text-center"><span t-esc="doc.product_id.default_code or '—'"/></td>
|
||||
<td class="text-center"><span t-esc="doc.product_id.default_code or '-'"/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<!--
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
Work Order Margin Report — Manufacturing Order
|
||||
Work Order Margin Report - Manufacturing Order
|
||||
-->
|
||||
<odoo>
|
||||
<template id="report_wo_margin">
|
||||
@@ -30,7 +30,7 @@
|
||||
<h2>
|
||||
Work Order Margin Report
|
||||
<span style="font-size: 14pt; color: #333;">
|
||||
— <t t-out="d['mo'].name"/>
|
||||
- <t t-out="d['mo'].name"/>
|
||||
</span>
|
||||
</h2>
|
||||
|
||||
@@ -56,13 +56,13 @@
|
||||
<t t-if="d['mo'].sale_order_id">
|
||||
<t t-out="d['mo'].sale_order_id.name"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<t t-if="d['mo'].date_start">
|
||||
<t t-out="d['mo'].date_start" t-options="{'widget': 'date'}"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
<t t-else="">-</t>
|
||||
</td>
|
||||
</tr></tbody>
|
||||
</table>
|
||||
@@ -218,7 +218,7 @@
|
||||
|
||||
<!-- Report Footer -->
|
||||
<p style="font-size: 8pt; color: #888; margin-top: 20px; text-align: right;">
|
||||
Generated by Fusion Plating — Nexa Systems Inc.
|
||||
Generated by Fusion Plating - Nexa Systems Inc.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user