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>
146 lines
6.7 KiB
XML
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>
|