feat(reports): WO box sticker + QR-scan-to-WO endpoint
Client is migrating from Steelhead and needs to keep the small
parts-box sticker format the warehouse crew already knows. Two
pieces shipped together so scanning is seamless from day one:
1. report_fp_wo_sticker — 4x3" QWeb label bound to mrp.workorder.
Layout mirrors the Steelhead sticker:
* ENTECH logo top-left (via env.company.logo)
* QR code top-right encoding /fp/wo/<id>
* Grid: PO (RO) / Customer / Process / Part Number / Due
Date / Qty / Notes
Dedicated paperformat_fp_wo_sticker at 102x76mm, 300 DPI,
landscape, 3mm margins — sized for thermal / inkjet label
printers without shrink-to-fit.
Binding added so "Print → WO Box Sticker" appears on every
mrp.workorder record.
2. FpWoScanController — GET /fp/wo/<int:wo_id> redirects the
scanner straight to the work-order form
(/odoo/action-mrp.action_mrp_workorder/<id>). auth='user' so
logged-in scanners land on the WO immediately; others bounce
through Odoo's login and return to the same URL. No custom
client work needed — any phone camera, handheld barcode
scanner, or tablet browser opens the URL on scan.
Process row resolution chain: part.default_process_id →
coating.recipe_id → fallback. So the sticker prints whichever
process is actually going to drive WO generation for this line,
matching the direct-order wizard's Effective Process column.
fusion_plating_reports → 19.0.7.0.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -315,6 +315,39 @@
|
||||
<field name="paperformat_id" ref="paperformat_fp_a4_landscape"/>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================= -->
|
||||
<!-- 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. -->
|
||||
<!-- ============================================================= -->
|
||||
<record id="paperformat_fp_wo_sticker" model="report.paperformat">
|
||||
<field name="name">FP WO Sticker (4x3")</field>
|
||||
<field name="format">custom</field>
|
||||
<field name="page_width">102</field>
|
||||
<field name="page_height">76</field>
|
||||
<field name="orientation">Landscape</field>
|
||||
<field name="margin_top">3</field>
|
||||
<field name="margin_bottom">3</field>
|
||||
<field name="margin_left">3</field>
|
||||
<field name="margin_right">3</field>
|
||||
<field name="header_line" eval="False"/>
|
||||
<field name="header_spacing">0</field>
|
||||
<field name="disable_shrinking" eval="True"/>
|
||||
<field name="dpi">300</field>
|
||||
</record>
|
||||
|
||||
<record id="action_report_fp_wo_sticker" model="ir.actions.report">
|
||||
<field name="name">WO Box Sticker</field>
|
||||
<field name="model">mrp.workorder</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">fusion_plating_reports.report_fp_wo_sticker</field>
|
||||
<field name="report_file">fusion_plating_reports.report_fp_wo_sticker</field>
|
||||
<field name="print_report_name">'WO Sticker - %s' % object.name</field>
|
||||
<field name="binding_model_id" ref="mrp.model_mrp_workorder"/>
|
||||
<field name="binding_type">report</field>
|
||||
<field name="paperformat_id" ref="paperformat_fp_wo_sticker"/>
|
||||
</record>
|
||||
|
||||
<!-- ============================================================= -->
|
||||
<!-- 15. Packing Slip (Portrait + Landscape) -->
|
||||
<!-- ============================================================= -->
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright 2026 Nexa Systems Inc.
|
||||
License OPL-1 (Odoo Proprietary License v1.0)
|
||||
|
||||
WO Box Sticker — 4x3" parts-box identification label.
|
||||
|
||||
Layout mirrors the Steelhead sticker format the client is
|
||||
migrating from:
|
||||
* ENTECH logo top-left.
|
||||
* QR code top-right — encodes the WO's scan URL
|
||||
(/fp/wo/<id>) so warehouse staff can scan with any
|
||||
phone/tablet and land on the WO form instantly.
|
||||
* Grid of PO / Customer / Process / Part Number / Due Date
|
||||
/ Qty / Notes rows.
|
||||
|
||||
Printed on a dedicated 4x3" paperformat; no header / footer.
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
<template id="report_fp_wo_sticker">
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="docs" t-as="doc">
|
||||
<t t-set="_mo" t-value="doc.production_id"/>
|
||||
<t t-set="_so" t-value="_mo and env['sale.order'].sudo().search(
|
||||
[('name', '=', _mo.origin)], limit=1) or False"/>
|
||||
<t t-set="_line" t-value="_so and _so.order_line[:1] or False"/>
|
||||
<t t-set="_part" t-value="
|
||||
(_line and _line.x_fc_part_catalog_id)
|
||||
or (doc.production_id and 'x_fc_part_catalog_ids' in _mo._fields
|
||||
and _mo.x_fc_sale_order_line_ids[:1].x_fc_part_catalog_id)
|
||||
or False"/>
|
||||
<t t-set="_coating" t-value="_line and _line.x_fc_coating_config_id or False"/>
|
||||
<t t-set="_process" t-value="(_part and _part.default_process_id)
|
||||
or (_coating and _coating.recipe_id)
|
||||
or False"/>
|
||||
<t t-set="_base_url" t-value="env['ir.config_parameter'].sudo().get_param('web.base.url', '')"/>
|
||||
<t t-set="_scan_url" t-value="_base_url + '/fp/wo/' + str(doc.id)"/>
|
||||
|
||||
<div class="page" style="font-family: Arial, sans-serif;
|
||||
color: #000; width: 100%; height: 100%; padding: 4mm;
|
||||
box-sizing: border-box;">
|
||||
|
||||
<style>
|
||||
.fp-stk-row { display: table-row; }
|
||||
.fp-stk-lbl {
|
||||
display: table-cell;
|
||||
vertical-align: top;
|
||||
font-weight: bold;
|
||||
text-decoration: underline;
|
||||
padding: 1mm 2mm 1mm 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.fp-stk-val {
|
||||
display: table-cell;
|
||||
vertical-align: top;
|
||||
padding: 1mm 0 1mm 2mm;
|
||||
border-left: 0.4mm solid #000;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Header: logo + WO # + QR -->
|
||||
<div style="display: table; width: 100%;">
|
||||
<!-- Logo + WO number -->
|
||||
<div style="display: table-cell; vertical-align: top;
|
||||
width: 65%; padding-right: 2mm;">
|
||||
<img t-if="env.company.logo"
|
||||
t-att-src="image_data_uri(env.company.logo)"
|
||||
style="max-height: 14mm; max-width: 100%;"/>
|
||||
<div style="font-size: 14pt; font-weight: bold;
|
||||
margin-top: 2mm;">
|
||||
WO #<span t-esc="doc.id"/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- QR code — linked to the scan redirect endpoint -->
|
||||
<div style="display: table-cell; vertical-align: top;
|
||||
width: 35%; text-align: right;">
|
||||
<img t-att-src="'/report/barcode/?barcode_type=QR&value=%s&width=200&height=200' % quote(_scan_url)"
|
||||
style="width: 26mm; height: 26mm;"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Data grid -->
|
||||
<div style="display: table; width: 100%;
|
||||
margin-top: 3mm; font-size: 9pt;">
|
||||
<div class="fp-stk-row">
|
||||
<div class="fp-stk-lbl">PO (RO):</div>
|
||||
<div class="fp-stk-val">
|
||||
<t t-esc="(_so and _so.x_fc_po_number) or '—'"/>
|
||||
<t t-if="_mo"> (<span t-esc="_mo.id"/>)</t>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fp-stk-row">
|
||||
<div class="fp-stk-lbl">Customer:</div>
|
||||
<div class="fp-stk-val">
|
||||
<t t-esc="(_so and _so.partner_id.name) or '—'"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fp-stk-row">
|
||||
<div class="fp-stk-lbl">Process:</div>
|
||||
<div class="fp-stk-val">
|
||||
<t t-if="_process">
|
||||
<span t-esc="_process.name"/>
|
||||
</t>
|
||||
<t t-elif="_coating">
|
||||
<span t-esc="_coating.name"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fp-stk-row">
|
||||
<div class="fp-stk-lbl">Part Number:</div>
|
||||
<div class="fp-stk-val">
|
||||
<t t-if="_part">
|
||||
<span t-esc="_part.part_number"/>
|
||||
<t t-if="_part.revision"> Rev <span t-esc="_part.revision"/></t>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fp-stk-row">
|
||||
<div class="fp-stk-lbl">Due Date:</div>
|
||||
<div class="fp-stk-val">
|
||||
<t t-set="_due" t-value="_mo and (_mo.date_deadline or _mo.date_finished) or False"/>
|
||||
<t t-if="_due">
|
||||
<span t-esc="_due.strftime('%m/%d/%Y')"/>
|
||||
</t>
|
||||
<t t-else="">—</t>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fp-stk-row">
|
||||
<div class="fp-stk-lbl">Qty:</div>
|
||||
<div class="fp-stk-val">
|
||||
<span t-esc="int(doc.qty_production) if doc.qty_production == int(doc.qty_production) else doc.qty_production"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fp-stk-row">
|
||||
<div class="fp-stk-lbl">Notes:</div>
|
||||
<div class="fp-stk-val">
|
||||
<t t-esc="(_so and _so.x_fc_internal_note
|
||||
and _so.x_fc_internal_note.striptags()[:120]) or ''"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user