feat(reports): BoL PDF uses customer_line_header macro (Sub 2 Task 21)

Rewired portrait + landscape variants of report_fp_bol. The BoL had no
line collection of its own (fusion.plating.delivery only has a soft
`job_ref` Char), so the previous cargo-description block was a single
hardcoded row. Restructured to look up the job's mrp.production via
`job_ref`, iterate its `move_finished_ids` (excluding cancelled), and
render each finished-goods move through the shared
customer_line_header macro using the `move.sale_line_id or move`
adapter pattern.

When no MO is found or there are no finished moves, the template falls
back to the previous single-row "Plated parts — Job X" behavior so
legacy records without a backing MO still print correctly. Per-row QTY
now reflects the individual move's `product_uom_qty` instead of the
MO's aggregate `product_qty`.

Both variants render successfully on entech against a delivery whose
job_ref matches a real MO with one finished move.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-04-21 23:06:07 -04:00
parent ac824c2cfb
commit 79d9e6b3b0

View File

@@ -116,7 +116,10 @@
</tbody>
</table>
<!-- Cargo description — added QTY column to match landscape -->
<!-- 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"/>
<table class="bordered">
<thead>
<tr>
@@ -131,24 +134,50 @@
</tr>
</thead>
<tbody>
<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"/>
<t t-if="doc.notes">
<br/><span t-field="doc.notes"/>
</t>
</td>
<td class="text-center fp-cell-mid">
<t t-set="_mo" t-value="env['mrp.production'].sudo().search([('name', '=', doc.job_ref)], limit=1) if doc.job_ref else False"/>
<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">
<span t-if="doc.tdg_required">TDG</span>
<span t-else="">NON-HAZ</span>
</td>
</tr>
<t t-if="_finished_moves">
<t t-foreach="_finished_moves" t-as="move">
<tr>
<td class="text-center fp-cell-mid">
<t t-if="move_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">
<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"/>
</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>
</td>
</tr>
</t>
</t>
<t t-else="">
<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"/>
<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 '—'"/>
</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>
</td>
</tr>
</t>
</tbody>
</table>
@@ -298,7 +327,10 @@
</tbody>
</table>
<!-- Cargo description -->
<!-- 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"/>
<table class="bordered">
<thead>
<tr>
@@ -314,25 +346,56 @@
</tr>
</thead>
<tbody>
<tr>
<td class="text-center">1</td>
<td>
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">
<span t-if="doc.tdg_required">TDG</span>
<span t-else="">NON-HAZ</span>
</td>
<td class="text-center">
<span t-if="doc.tdg_required" class="status-warning">TDG HANDLING</span>
<span t-else="">Standard</span>
</td>
</tr>
<t t-if="_finished_moves">
<t t-foreach="_finished_moves" t-as="move">
<tr>
<td class="text-center">
<t t-if="move_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">
<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"/>
</td>
<td class="text-center"></td>
<td class="text-center">
<span t-if="doc.tdg_required">TDG</span>
<span t-else="">NON-HAZ</span>
</td>
<td class="text-center">
<span t-if="doc.tdg_required" class="status-warning">TDG HANDLING</span>
<span t-else="">Standard</span>
</td>
</tr>
</t>
</t>
<t t-else="">
<tr>
<td class="text-center">1</td>
<td>
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">
<span t-if="doc.tdg_required">TDG</span>
<span t-else="">NON-HAZ</span>
</td>
<td class="text-center">
<span t-if="doc.tdg_required" class="status-warning">TDG HANDLING</span>
<span t-else="">Standard</span>
</td>
</tr>
</t>
</tbody>
</table>