Files
Odoo-Modules/fusion_plating/fusion_plating_receiving/views/fp_box_views.xml
gsinghpal d531faad12 feat(fusion_plating): box-level tracking (fp.box) + thermal job-sticker redesign
Box registry: new fp.box model (fusion_plating_receiving), one record per
received box, auto-created when a receiving is marked Counted (idempotent
_fp_sync_boxes — grows/shrinks with box_count_in, never touches an advanced
box). Status received -> racked -> in_process -> packed -> shipped, per-box
scannable QR (/fp/box/<id> controller). Backfill migration for receivings
counted before tracking shipped. Boxes list/kanban/form + receiving smart
button.

Job stickers redesigned (thermal label, 6x4 in / 152x102mm, mm layout @
paperformat dpi=96 so mm maps 1:1 in wkhtmltopdf — see rule 14):
- Internal Job Sticker = Layout A, ONE per job (shop notes from
  x_fc_internal_description, job QR).
- External Job Sticker = Layout B, ONE per fp.box (BOX n/N, per-box QR,
  factory company logo, customer-facing notes). Dynamic MASK badge
  (x_fc_masking_enabled) + BAKE block (x_fc_bake_instructions), length-tiered
  notes font. Display logic in fp.job._fp_sticker_data().

Also retains the SO/WO box-sticker MemoryError fix in report_fp_wo_sticker.xml
(per-box loop sourced from fp.receiving.box_count_in + 100-label safety cap).

Verified live on entech: 111 boxes backfilled (31 receivings), External renders
one page per box, Internal one per job, scan endpoint 303->login.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 13:21:54 -04:00

146 lines
6.7 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Box-level tracking — fp.box list / form / search / kanban + menu.
-->
<odoo>
<!-- ===== List ===== -->
<record id="fp_box_view_list" model="ir.ui.view">
<field name="name">fp.box.list</field>
<field name="model">fp.box</field>
<field name="arch" type="xml">
<list string="Boxes" decoration-muted="state in ('shipped','cancelled')" decoration-danger="state == 'lost'">
<field name="box_number"/>
<field name="box_count"/>
<field name="name"/>
<field name="sale_order_id"/>
<field name="partner_id"/>
<field name="job_id"/>
<field name="location_note"/>
<field name="state" widget="badge"
decoration-info="state == 'received'"
decoration-warning="state in ('racked','in_process','packed')"
decoration-success="state == 'shipped'"
decoration-danger="state == 'lost'"/>
</list>
</field>
</record>
<!-- ===== Form ===== -->
<record id="fp_box_view_form" model="ir.ui.view">
<field name="name">fp.box.form</field>
<field name="model">fp.box</field>
<field name="arch" type="xml">
<form>
<header>
<button name="action_set_racked" type="object" string="Mark Racked"
class="btn-primary" invisible="state != 'received'"/>
<button name="action_set_in_process" type="object" string="Mark In Process"
class="btn-primary" invisible="state != 'racked'"/>
<button name="action_set_packed" type="object" string="Mark Packed"
class="btn-primary" invisible="state != 'in_process'"/>
<button name="action_set_shipped" type="object" string="Mark Shipped"
class="btn-primary" invisible="state != 'packed'"/>
<button name="action_set_lost" type="object" string="Flag Lost"
invisible="state in ('shipped','lost','cancelled')"/>
<button name="action_reset_received" type="object" string="Reset to Received"
groups="fusion_plating.group_fp_shop_manager_v2"
invisible="state == 'received'"/>
<field name="state" widget="statusbar"
statusbar_visible="received,racked,in_process,packed,shipped"/>
</header>
<sheet>
<div class="oe_title">
<h1><field name="name" readonly="1"/></h1>
</div>
<group>
<group>
<label for="box_number" string="Box"/>
<div>
<field name="box_number" class="oe_inline"/> /
<field name="box_count" class="oe_inline"/>
</div>
<field name="receiving_id"/>
<field name="sale_order_id"/>
<field name="job_id"/>
</group>
<group>
<field name="partner_id"/>
<field name="location_note"/>
<field name="scan_url" widget="url" readonly="1"/>
<field name="company_id" groups="base.group_multi_company"/>
</group>
</group>
</sheet>
<chatter/>
</form>
</field>
</record>
<!-- ===== Search ===== -->
<record id="fp_box_view_search" model="ir.ui.view">
<field name="name">fp.box.search</field>
<field name="model">fp.box</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="sale_order_id"/>
<field name="partner_id"/>
<field name="job_id"/>
<field name="receiving_id"/>
<filter name="open" string="Open (not shipped)" domain="[('state','not in',('shipped','cancelled'))]"/>
<filter name="received" string="Received" domain="[('state','=','received')]"/>
<filter name="in_process" string="In Process" domain="[('state','in',('racked','in_process','packed'))]"/>
<filter name="shipped" string="Shipped" domain="[('state','=','shipped')]"/>
<filter name="lost" string="Lost" domain="[('state','=','lost')]"/>
<group>
<filter name="g_state" string="Status" context="{'group_by':'state'}"/>
<filter name="g_customer" string="Customer" context="{'group_by':'partner_id'}"/>
<filter name="g_receiving" string="Receiving" context="{'group_by':'receiving_id'}"/>
</group>
</search>
</field>
</record>
<!-- ===== Kanban (by status) ===== -->
<record id="fp_box_view_kanban" model="ir.ui.view">
<field name="name">fp.box.kanban</field>
<field name="model">fp.box</field>
<field name="arch" type="xml">
<kanban default_group_by="state" class="o_kanban_small_column">
<field name="state"/>
<templates>
<t t-name="card">
<div class="oe_kanban_content">
<strong><field name="name"/></strong>
<div><field name="partner_id"/></div>
<div t-if="record.location_note.raw_value">
<span class="text-muted">@ </span><field name="location_note"/>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<!-- ===== Action ===== -->
<record id="action_fp_box" model="ir.actions.act_window">
<field name="name">Boxes</field>
<field name="res_model">fp.box</field>
<field name="view_mode">list,kanban,form</field>
<field name="search_view_id" ref="fp_box_view_search"/>
<field name="context">{'search_default_open': 1}</field>
</record>
<!-- ===== Menu ===== -->
<menuitem id="menu_fp_box"
name="Boxes"
parent="menu_fp_receiving_root"
action="action_fp_box"
sequence="35"/>
</odoo>