This commit is contained in:
gsinghpal
2026-04-27 00:11:18 -04:00
parent d9f58b9851
commit f08f328688
116 changed files with 9891 additions and 359 deletions

View File

@@ -0,0 +1,151 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
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: D4D8 sections show a placeholder
note that the CAPA hasn't been opened yet.
-->
<odoo>
<template id="report_fp_8d_doc">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="ncr">
<t t-set="capa" t-value="ncr.capa_ids[:1] if ncr.capa_ids else False"/>
<t t-call="web.external_layout">
<div class="page" style="font-family: 'Helvetica Neue', Arial, sans-serif; color: #2b2b2b; font-size: 12px;">
<div style="border-bottom: 2px solid #333; padding-bottom: 10px; margin-bottom: 18px;">
<h1 style="margin: 0; font-size: 22px;">8D Report</h1>
<div style="display: flex; justify-content: space-between; margin-top: 6px;">
<span><strong>NCR:</strong> <span t-out="ncr.name"/></span>
<span t-if="capa"><strong>CAPA:</strong> <span t-out="capa.name"/></span>
<span><strong>Issued:</strong> <span t-out="ncr.reported_date" t-options='{"widget": "date"}'/></span>
</div>
</div>
<!-- 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>
<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>
<tr t-if="ncr.team_id and ncr.team_id.member_ids"><td style="color: #666;">Members</td><td><span t-out="', '.join(ncr.team_id.member_ids.mapped('name'))"/></td></tr>
</table>
</div>
<!-- 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>
<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>
<tr><td style="color: #666;">Source</td><td><span t-field="ncr.source"/></td></tr>
<tr t-if="ncr.customer_partner_id"><td style="color: #666;">Customer</td><td><span t-out="ncr.customer_partner_id.name"/></td></tr>
<tr t-if="ncr.part_ref"><td style="color: #666;">Part / Lot</td><td><span t-out="ncr.part_ref"/></td></tr>
<tr t-if="ncr.quantity_affected"><td style="color: #666;">Qty Affected</td><td><span t-out="ncr.quantity_affected"/></td></tr>
</table>
<div style="background: #fafafa; padding: 8px; border-left: 3px solid #ddd;">
<t t-out="ncr.description or 'No description recorded.'"/>
</div>
</div>
</div>
<!-- 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>
<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 -->
<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>
<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>
<tr t-if="capa and capa.reason_id"><td style="width: 130px; color: #666;">CAPA Reason</td><td><span t-out="capa.reason_id.name"/></td></tr>
</table>
<div style="background: #fafafa; padding: 8px; border-left: 3px solid #ddd;">
<t t-if="capa and capa.root_cause_analysis"><t t-out="capa.root_cause_analysis"/></t>
<t t-elif="ncr.root_cause"><t t-out="ncr.root_cause"/></t>
<t t-else="">Root cause not yet documented.</t>
</div>
</div>
</div>
<!-- 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>
<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>
</div>
</div>
<!-- D6 — Implement &amp; Verify -->
<div style="margin-bottom: 14px;">
<h2 style="font-size: 14px; background: #eee; padding: 6px 10px; margin: 0 0 6px 0;">D6 — Implement &amp; 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;">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>
</div>
<!-- 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>
<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>
</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 -->
<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>
<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 '—'"/>.
</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>
</table>
</div>
</div>
</div>
</t>
</t>
</t>
</template>
<record id="action_report_fp_8d" model="ir.actions.report">
<field name="name">8D Report</field>
<field name="model">fusion.plating.ncr</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">fusion_plating_reports.report_fp_8d_doc</field>
<field name="report_file">fusion_plating_reports.report_fp_8d_doc</field>
<field name="binding_model_id" ref="fusion_plating_quality.model_fusion_plating_ncr"/>
<field name="binding_type">report</field>
<field name="print_report_name">'8D-' + (object.name or '').replace('/', '-')</field>
</record>
</odoo>

View File

@@ -116,10 +116,12 @@
</tbody>
</table>
<!-- Cargo description — iterate MO finished moves so each part
renders with its customer part number via the shared macro. -->
<t t-set="_mo" t-value="env['mrp.production'].sudo().search([('name', '=', doc.job_ref)], limit=1) if doc.job_ref else False"/>
<t t-set="_finished_moves" t-value="_mo.move_finished_ids.filtered(lambda m: m.state != 'cancel') if _mo else False"/>
<!-- 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. -->
<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 &gt; 0) if _so else False"/>
<table class="bordered">
<thead>
<tr>
@@ -134,22 +136,21 @@
</tr>
</thead>
<tbody>
<t t-if="_finished_moves">
<t t-foreach="_finished_moves" t-as="move">
<t t-if="_lines">
<t t-foreach="_lines" t-as="line">
<tr>
<td class="text-center fp-cell-mid">
<t t-if="move_first">1</t>
<t t-if="line_first">1</t>
<t t-else=""/>
</td>
<td class="fp-cell-mid">
<t t-set="line" t-value="move.sale_line_id or move"/>
<t t-call="fusion_plating_reports.customer_line_header"/>
<t t-if="move_last and doc.notes">
<t t-if="line_last and doc.notes">
<br/><span t-field="doc.notes"/>
</t>
</td>
<td class="text-center fp-cell-mid">
<span t-esc="int(move.product_uom_qty) if move.product_uom_qty == int(move.product_uom_qty) else move.product_uom_qty"/>
<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">
@@ -327,10 +328,11 @@
</tbody>
</table>
<!-- Cargo description — iterate MO finished moves so each part
renders with its customer part number via the shared macro. -->
<t t-set="_mo" t-value="env['mrp.production'].sudo().search([('name', '=', doc.job_ref)], limit=1) if doc.job_ref else False"/>
<t t-set="_finished_moves" t-value="_mo.move_finished_ids.filtered(lambda m: m.state != 'cancel') if _mo else False"/>
<!-- 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 &gt; 0) if _so else False"/>
<table class="bordered">
<thead>
<tr>
@@ -346,22 +348,21 @@
</tr>
</thead>
<tbody>
<t t-if="_finished_moves">
<t t-foreach="_finished_moves" t-as="move">
<t t-if="_lines">
<t t-foreach="_lines" t-as="line">
<tr>
<td class="text-center">
<t t-if="move_first">1</t>
<t t-if="line_first">1</t>
<t t-else=""/>
</td>
<td>
<t t-set="line" t-value="move.sale_line_id or move"/>
<t t-call="fusion_plating_reports.customer_line_header"/>
<t t-if="move_last and doc.notes">
<t t-if="line_last and doc.notes">
<br/><span t-field="doc.notes"/>
</t>
</td>
<td class="text-center">
<span t-esc="int(move.product_uom_qty) if move.product_uom_qty == int(move.product_uom_qty) else move.product_uom_qty"/>
<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">

View File

@@ -508,8 +508,10 @@
<template id="report_fp_job_traveller_so_landscape">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="so">
<t t-set="mos" t-value="so.env['mrp.production'].search([('origin', '=', so.name)])"/>
<t t-if="not mos">
<!-- 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">
<t t-call="web.external_layout">
<t t-set="doc" t-value="so"/>
<t t-call="fusion_plating_reports.fp_landscape_styles"/>
@@ -519,21 +521,40 @@
<div class="highlight-box">
<strong class="status-warning">
<i class="fa fa-info-circle"/>
No Manufacturing Order has been generated for this Sale Order yet.
No plating job has been generated for this Sale Order yet.
</strong>
</div>
</div>
</div>
</t>
</t>
<t t-foreach="mos" t-as="mo">
<t t-foreach="jobs" t-as="job">
<t t-call="web.external_layout">
<t t-set="doc" t-value="mo"/>
<t t-call="fusion_plating_reports.fp_landscape_styles"/>
<div class="fp-landscape">
<div class="page">
<t t-call="fusion_plating_reports.report_fp_job_traveller_body"/>
</div>
<div class="page">
<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>
<tr><th>Qty</th><td><span t-esc="job.qty"/></td></tr>
<tr><th>Recipe</th><td><span t-esc="job.recipe_id.name or '-'"/></td></tr>
<tr><th>Deadline</th><td><span t-esc="job.date_deadline and job.date_deadline.strftime('%b %d, %Y') or '-'"/></td></tr>
<tr><th>Status</th><td><span t-esc="job.state"/></td></tr>
</table>
<h3 style="margin-top: 1.5em;">Steps</h3>
<table class="table table-sm">
<thead>
<tr><th>#</th><th>Step</th><th>Work Centre</th><th>Kind</th><th>State</th></tr>
</thead>
<tbody>
<tr t-foreach="job.step_ids.sorted('sequence')" t-as="step">
<td><span t-esc="step.sequence"/></td>
<td><span t-esc="step.name"/></td>
<td><span t-esc="step.work_centre_id.name or '-'"/></td>
<td><span t-esc="step.kind or '-'"/></td>
<td><span t-esc="step.state"/></td>
</tr>
</tbody>
</table>
</div>
</t>
</t>
@@ -547,8 +568,8 @@
<template id="report_fp_job_traveller_so_portrait">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="so">
<t t-set="mos" t-value="so.env['mrp.production'].search([('origin', '=', so.name)])"/>
<t t-if="not mos">
<t t-set="jobs" t-value="so.env['fp.job'].search([('sale_order_id', '=', so.id)])"/>
<t t-if="not jobs">
<t t-call="web.external_layout">
<t t-set="doc" t-value="so"/>
<t t-call="fusion_plating_reports.fp_portrait_styles"/>
@@ -558,21 +579,40 @@
<div class="highlight-box">
<strong class="status-warning">
<i class="fa fa-info-circle"/>
No Manufacturing Order has been generated for this Sale Order yet.
No plating job has been generated for this Sale Order yet.
</strong>
</div>
</div>
</div>
</t>
</t>
<t t-foreach="mos" t-as="mo">
<t t-foreach="jobs" t-as="job">
<t t-call="web.external_layout">
<t t-set="doc" t-value="mo"/>
<t t-call="fusion_plating_reports.fp_portrait_styles"/>
<div class="fp-report">
<div class="page">
<t t-call="fusion_plating_reports.report_fp_job_traveller_body"/>
</div>
<div class="page">
<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>
<tr><th>Qty</th><td><span t-esc="job.qty"/></td></tr>
<tr><th>Recipe</th><td><span t-esc="job.recipe_id.name or '-'"/></td></tr>
<tr><th>Deadline</th><td><span t-esc="job.date_deadline and job.date_deadline.strftime('%b %d, %Y') or '-'"/></td></tr>
<tr><th>Status</th><td><span t-esc="job.state"/></td></tr>
</table>
<h3 style="margin-top: 1.5em;">Steps</h3>
<table class="table table-sm">
<thead>
<tr><th>#</th><th>Step</th><th>Work Centre</th><th>Kind</th><th>State</th></tr>
</thead>
<tbody>
<tr t-foreach="job.step_ids.sorted('sequence')" t-as="step">
<td><span t-esc="step.sequence"/></td>
<td><span t-esc="step.name"/></td>
<td><span t-esc="step.work_centre_id.name or '-'"/></td>
<td><span t-esc="step.kind or '-'"/></td>
<td><span t-esc="step.state"/></td>
</tr>
</tbody>
</table>
</div>
</t>
</t>

View File

@@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
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
`action_fp_quality_monthly_summary`.
-->
<odoo>
<template id="report_fp_quality_monthly_doc">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="company">
<t t-call="web.external_layout">
<div class="page" style="font-family: 'Helvetica Neue', Arial, sans-serif; color: #2b2b2b; font-size: 12px;">
<div style="border-bottom: 2px solid #333; padding-bottom: 10px; margin-bottom: 20px;">
<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']"/>
</div>
</div>
<!-- Headline counts -->
<table style="width: 100%; border-collapse: collapse; margin-bottom: 24px;">
<thead>
<tr style="background: #eee;">
<th style="padding: 8px; text-align: left;">Record Type</th>
<th style="padding: 8px; text-align: right; width: 110px;">Opened (period)</th>
<th style="padding: 8px; text-align: right; width: 110px;">Closed (period)</th>
<th style="padding: 8px; text-align: right; width: 110px;">Open Total</th>
<th style="padding: 8px; text-align: right; width: 110px;">Overdue</th>
</tr>
</thead>
<tbody>
<tr t-foreach="data['by_type']" t-as="row">
<td style="padding: 6px 8px; border-bottom: 1px solid #eee;"><strong t-out="row['label']"/></td>
<td style="padding: 6px 8px; border-bottom: 1px solid #eee; text-align: right;"><span t-out="row['opened']"/></td>
<td style="padding: 6px 8px; border-bottom: 1px solid #eee; text-align: right;"><span t-out="row['closed']"/></td>
<td style="padding: 6px 8px; border-bottom: 1px solid #eee; text-align: right;"><span t-out="row['open_total']"/></td>
<td style="padding: 6px 8px; border-bottom: 1px solid #eee; text-align: right; color: #c33; font-weight: bold;" t-if="row['overdue'] &gt; 0"><span t-out="row['overdue']"/></td>
<td style="padding: 6px 8px; border-bottom: 1px solid #eee; text-align: right; color: #999;" t-else="">0</td>
</tr>
</tbody>
</table>
<!-- Severity breakdown -->
<h2 style="font-size: 14px; margin: 0 0 8px 0;">NCR Severity Breakdown</h2>
<table style="width: 100%; border-collapse: collapse; margin-bottom: 24px;">
<thead>
<tr style="background: #eee;">
<th style="padding: 6px 8px; text-align: left;">Severity</th>
<th style="padding: 6px 8px; text-align: right; width: 100px;">Count</th>
</tr>
</thead>
<tbody>
<tr t-foreach="data['ncr_severity']" t-as="row">
<td style="padding: 6px 8px; border-bottom: 1px solid #eee;"><span t-out="row['label']"/></td>
<td style="padding: 6px 8px; border-bottom: 1px solid #eee; text-align: right;"><span t-out="row['count']"/></td>
</tr>
</tbody>
</table>
<!-- CAPA effectiveness -->
<h2 style="font-size: 14px; margin: 0 0 8px 0;">CAPA Effectiveness</h2>
<table style="width: 100%; border-collapse: collapse; margin-bottom: 24px;">
<tr><td style="padding: 4px 8px; color: #666; width: 250px;">Closed in period</td><td style="padding: 4px 8px;"><span t-out="data['capa']['closed']"/></td></tr>
<tr><td style="padding: 4px 8px; color: #666;">Verified effective</td><td style="padding: 4px 8px;"><span t-out="data['capa']['effective']"/></td></tr>
<tr><td style="padding: 4px 8px; color: #666;">Not effective (re-opened follow-up)</td><td style="padding: 4px 8px; color: #c33;"><span t-out="data['capa']['not_effective']"/></td></tr>
<tr><td style="padding: 4px 8px; color: #666;">Effectiveness rate</td><td style="padding: 4px 8px;"><strong t-out="data['capa']['rate_pct']"/>%</td></tr>
</table>
<!-- Repeat customers -->
<h2 style="font-size: 14px; margin: 0 0 8px 0;">Repeat-Issue Customers (≥3 NCRs in 90 days)</h2>
<table style="width: 100%; border-collapse: collapse;">
<thead>
<tr style="background: #eee;">
<th style="padding: 6px 8px; text-align: left;">Customer</th>
<th style="padding: 6px 8px; text-align: right; width: 100px;">NCRs (90d)</th>
<th style="padding: 6px 8px; text-align: right; width: 100px;">Open RMAs</th>
</tr>
</thead>
<tbody>
<tr t-foreach="data['repeat_customers']" t-as="row">
<td style="padding: 6px 8px; border-bottom: 1px solid #eee;"><strong t-out="row['name']"/></td>
<td style="padding: 6px 8px; border-bottom: 1px solid #eee; text-align: right;"><span t-out="row['ncr_count']"/></td>
<td style="padding: 6px 8px; border-bottom: 1px solid #eee; text-align: right;"><span t-out="row['rma_count']"/></td>
</tr>
<tr t-if="not data['repeat_customers']">
<td colspan="3" style="padding: 10px; color: #666; font-style: italic;">No customers with ≥3 NCRs in the last 90 days. Nice work.</td>
</tr>
</tbody>
</table>
<p style="font-size: 11px; color: #666; margin-top: 28px; border-top: 1px solid #eee; padding-top: 8px;">
Generated <span t-out="data['generated_at']"/> for <span t-out="company.name"/>.
Data source: fusion.plating.* live tables. Run on-demand from Quality Dashboard.
</p>
</div>
</t>
</t>
</t>
</template>
<record id="action_report_fp_quality_monthly" model="ir.actions.report">
<field name="name">Monthly Quality Summary</field>
<field name="model">res.company</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">fusion_plating_reports.report_fp_quality_monthly_doc</field>
<field name="report_file">fusion_plating_reports.report_fp_quality_monthly_doc</field>
<field name="binding_type">action</field>
<field name="print_report_name">'Quality-Monthly-' + (object.name or '').replace(' ', '-')</field>
</record>
</odoo>

View File

@@ -0,0 +1,141 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
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.
-->
<odoo>
<template id="report_fp_rma_authorisation_doc">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="rma">
<t t-call="web.external_layout">
<div class="page" style="font-family: 'Helvetica Neue', Arial, sans-serif; color: #2b2b2b;">
<div style="display: flex; justify-content: space-between; align-items: flex-start; border-bottom: 2px solid #333; padding-bottom: 10px; margin-bottom: 20px;">
<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 &amp; Metal Finishing
</p>
</div>
<div style="text-align: right;">
<div style="font-size: 22px; font-weight: bold;">
<t t-esc="rma.name"/>
</div>
<div style="font-size: 12px; color: #666;">
Issued <span t-out="rma.create_date" t-options='{"widget": "date"}'/>
</div>
</div>
</div>
<table style="width: 100%; border-collapse: collapse; margin-bottom: 18px;">
<tr>
<td style="vertical-align: top; width: 60%;">
<h3 style="margin: 0 0 6px 0; font-size: 14px;">Customer</h3>
<div style="font-weight: bold;"><span t-out="rma.partner_id.name"/></div>
<div t-if="rma.partner_id.street"><span t-out="rma.partner_id.street"/></div>
<div t-if="rma.partner_id.city">
<span t-out="rma.partner_id.city"/><t t-if="rma.partner_id.state_id">, <span t-out="rma.partner_id.state_id.name"/></t>
<t t-if="rma.partner_id.zip"> <span t-out="rma.partner_id.zip"/></t>
</div>
<div t-if="rma.partner_id.country_id"><span t-out="rma.partner_id.country_id.name"/></div>
</td>
<td style="vertical-align: top; width: 40%;">
<h3 style="margin: 0 0 6px 0; font-size: 14px;">Return Details</h3>
<table style="width: 100%; font-size: 12px;">
<tr><td style="color: #666;">Original Order</td><td><span t-out="rma.sale_order_id.name"/></td></tr>
<tr><td style="color: #666;">Trigger</td><td><span t-field="rma.trigger_source"/></td></tr>
<tr><td style="color: #666;">Severity</td><td><span t-field="rma.severity"/></td></tr>
<tr><td style="color: #666;">Qty Returning</td><td><span t-out="rma.qty_returned"/></td></tr>
</table>
</td>
</tr>
</table>
<h3 style="font-size: 14px; margin-bottom: 6px;">Returned Lines</h3>
<table style="width: 100%; border-collapse: collapse; font-size: 12px; margin-bottom: 18px;">
<thead>
<tr style="background: #f3f3f3;">
<th style="text-align: left; padding: 6px; border-bottom: 1px solid #ccc;">Part #</th>
<th style="text-align: left; padding: 6px; border-bottom: 1px solid #ccc;">Description</th>
<th style="text-align: right; padding: 6px; border-bottom: 1px solid #ccc; width: 70px;">Ordered</th>
</tr>
</thead>
<tbody>
<tr t-foreach="rma.sale_order_line_ids" t-as="line">
<td style="padding: 6px; border-bottom: 1px solid #eee;">
<span t-out="line.product_id.default_code or line.product_id.name"/>
</td>
<td style="padding: 6px; border-bottom: 1px solid #eee;">
<span t-out="line.name"/>
</td>
<td style="padding: 6px; border-bottom: 1px solid #eee; text-align: right;">
<span t-out="line.product_uom_qty"/>
</td>
</tr>
</tbody>
</table>
<div t-if="rma.complaint_description" style="margin-bottom: 18px;">
<h3 style="font-size: 14px; margin-bottom: 6px;">Customer Complaint</h3>
<div style="font-size: 12px; padding: 10px; background: #fafafa; border-left: 3px solid #ddd;">
<t t-out="rma.complaint_description"/>
</div>
</div>
<table style="width: 100%; border-collapse: collapse;">
<tr>
<td style="vertical-align: top; width: 65%;">
<h3 style="font-size: 14px; margin-bottom: 6px;">Return Shipping Instructions</h3>
<ol style="font-size: 12px; line-height: 1.5;">
<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>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/>
[shop street address]<br/>
[city, province, postal code]
</p>
</td>
<td style="vertical-align: top; text-align: center; padding-left: 20px;">
<div t-if="rma.qr_code">
<img t-att-src="image_data_uri(rma.qr_code)"
style="width: 140px; height: 140px;"/>
<div style="font-size: 10px; color: #666; margin-top: 4px;">
Scan to track this RMA
</div>
</div>
</td>
</tr>
</table>
<p style="font-size: 11px; color: #666; margin-top: 30px; border-top: 1px solid #eee; padding-top: 8px;">
This authorisation is valid for 60 days from the issue date. Returns received without an RMA number will not be processed. Questions? Reply to the email this PDF was attached to or call your account contact.
</p>
</div>
</t>
</t>
</t>
</template>
<record id="action_report_fp_rma_authorisation" model="ir.actions.report">
<field name="name">RMA Authorisation</field>
<field name="model">fusion.plating.rma</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">fusion_plating_reports.report_fp_rma_authorisation_doc</field>
<field name="report_file">fusion_plating_reports.report_fp_rma_authorisation_doc</field>
<field name="binding_model_id" ref="fusion_plating_quality.model_fusion_plating_rma"/>
<field name="binding_type">report</field>
<field name="print_report_name">'RMA-' + (object.name or '').replace('/', '-')</field>
</record>
</odoo>