Files
Odoo-Modules/fusion_plating/fusion_plating_receiving/views/fp_receiving_views.xml
gsinghpal 3d1b6e7ec5 fix(receiving): drop dead staged state — Option B (draft→counted→closed)
Reported 2026-05-20: the receiving state machine had four states
(draft → counted → staged → closed) where the middle pair was pure
ceremony. Real-usage data on entech:

  state distribution: 14 draft, 4 closed (zero `staged` records)
  median dwell counted → staged: 11 seconds
  median dwell staged  → closed: 4 minutes

`staged` captured no fields, fired no gates, mapped to the same SO
`x_fc_receiving_status='partial'` as `counted`. Pure click-through.

Cleanup:
- State Selection retains `staged` as `Staged (legacy)` so historical
  records remain readable; new transitions never write it.
- statusbar_visible drops it from the chevron header.
- action_mark_staged becomes a thin shim that advances counted →
  closed directly (any old button binding still works).
- action_close now accepts `counted` as a valid source state (was
  previously only `staged` / legacy `accepted` / `resolved`).
- View: "Stage for Racking" button removed. "Close" button renamed
  to "Close — Racking Confirmed" so the racking-crew confirmation
  meaning stays obvious.
- _update_so_receiving_status mapping unchanged for legacy `staged`
  (still maps to partial) — only the comment block updated to
  describe the new canonical flow.

Migration 19.0.3.20.0 advances any `staged` records to `closed`
and syncs the linked SO's x_fc_receiving_status to `received` so
downstream gates (job step start, mark_done qty check, cert
creation) don't see a stale "partial" status.

Module: fusion_plating_receiving 19.0.3.19.0 → 19.0.3.20.0.

Tests: TestQtyReceivedPropagation updated — 5 tests dropped the
action_mark_staged() call, walk draft → counted → closed directly.
All 11 tests green (carrier 6 + propagation 5).

Verified on entech: existing 14 draft + 4 closed records untouched.
Direct draft → counted → closed transition works end-to-end on
RCV-30041 (was the test target).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 08:40:43 -04:00

308 lines
16 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Part of the Fusion Plating product family.
-->
<odoo>
<!-- ===== Receiving List View ===== -->
<record id="view_fp_receiving_list" model="ir.ui.view">
<field name="name">fp.receiving.list</field>
<field name="model">fp.receiving</field>
<field name="arch" type="xml">
<list string="Receiving"
decoration-warning="state == 'discrepancy'"
decoration-success="state == 'accepted'"
decoration-muted="state == 'resolved'"
default_order="received_date desc">
<field name="received_date"/>
<field name="name"/>
<field name="sale_order_id"/>
<field name="partner_id"/>
<field name="po_number"/>
<field name="expected_qty"/>
<field name="received_qty"/>
<field name="qty_match" widget="boolean"/>
<field name="state" widget="badge"
decoration-info="state == 'draft'"
decoration-warning="state in ('inspecting', 'discrepancy')"
decoration-success="state == 'accepted'"
decoration-muted="state == 'resolved'"/>
</list>
</field>
</record>
<!-- ===== Receiving Form View ===== -->
<record id="view_fp_receiving_form" model="ir.ui.view">
<field name="name">fp.receiving.form</field>
<field name="model">fp.receiving</field>
<field name="arch" type="xml">
<form string="Receiving">
<header>
<!-- Sub 8 + 2026-05-20: draft → counted → closed.
`Stage for Racking` removed (was pure ceremony
between Counted and Closed, median dwell 11 sec,
no captured data). Receiver counts boxes, then
closes once the racking crew confirms. -->
<button name="action_mark_counted"
string="Mark Counted"
type="object"
class="btn-primary"
invisible="state not in ('draft', 'inspecting')"/>
<button name="action_close"
string="Close — Racking Confirmed"
type="object"
class="btn-primary"
invisible="state not in ('counted', 'staged', 'accepted', 'resolved')"/>
<!-- Legacy actions (hidden by default; surfaces for old records) -->
<button name="action_accept"
string="Accept (legacy)"
type="object"
invisible="state != 'inspecting'"
groups="fusion_plating.group_fusion_plating_manager"/>
<button name="action_flag_discrepancy"
string="Flag Discrepancy (legacy)"
type="object"
class="btn-danger"
invisible="state != 'inspecting'"
groups="fusion_plating.group_fusion_plating_manager"/>
<button name="action_resolve"
string="Resolve (legacy)"
type="object"
invisible="state != 'discrepancy'"
groups="fusion_plating.group_fusion_plating_manager"/>
<button name="action_generate_outbound_label"
type="object"
string="Generate Outbound Label"
class="btn-primary"
icon="fa-print"
invisible="not x_fc_carrier_id or not x_fc_weight"/>
<button name="action_print_label"
type="object"
string="Print Label"
class="btn-secondary"
icon="fa-file-pdf-o"
invisible="not x_fc_outbound_shipment_id"/>
<field name="state" widget="statusbar"
statusbar_visible="draft,counted,closed"/>
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button name="action_create_outbound_shipment"
type="object"
class="oe_stat_button"
icon="fa-truck">
<field name="x_fc_outbound_shipment_count"
widget="statinfo"
string="Outbound Shipment"/>
</button>
<button name="action_print_label"
type="object"
class="oe_stat_button"
icon="fa-print"
invisible="not x_fc_has_label">
<div class="o_stat_info">
<span class="o_stat_value">PDF</span>
<span class="o_stat_text">Print Label</span>
</div>
<field name="x_fc_has_label" invisible="1"/>
</button>
</div>
<div class="alert alert-info" role="alert">
<i class="fa fa-info-circle me-2"/>
<strong>Receiving = box count only.</strong>
Count the boxes the truck dropped off, set the number
below, and stage them for racking. The racking crew
opens the boxes and inspects each part — click
<strong>Racking Inspections</strong> above to jump
straight to the open inspection for this SO.
</div>
<div class="oe_title">
<h1>
<field name="name" readonly="1"/>
</h1>
</div>
<group>
<group>
<field name="sale_order_id"/>
<field name="partner_id"/>
<field name="po_number"/>
</group>
<group string="Box Count">
<field name="box_count_in"/>
</group>
</group>
<group>
<group string="Reception">
<field name="received_by_id"/>
<field name="received_date"/>
<field name="x_fc_carrier_id"
options="{'no_create': True}"/>
<field name="carrier_tracking"/>
<!--
Legacy carrier_name (Char) is retained
in the schema for audit but hidden from
the form. Migration 19.0.3.10.0 already
matched legacy text against
delivery.carrier.name; any unmatched
value still lives on the record. View
duplication confused operators (one
field labelled "Outbound Carrier", a
second readonly one labelled "Legacy
Carrier") so we drop the display.
-->
<field name="carrier_name" invisible="1"/>
</group>
</group>
<group string="Outbound Packaging"
invisible="not x_fc_carrier_id">
<group>
<label for="x_fc_weight"/>
<div class="o_row">
<field name="x_fc_weight"/>
<field name="x_fc_weight_uom" nolabel="1"/>
</div>
<label for="x_fc_length" string="Dimensions (L×W×H)"/>
<div class="o_row">
<field name="x_fc_length"
placeholder="L"/>
<span class="mx-1">×</span>
<field name="x_fc_width"
placeholder="W"/>
<span class="mx-1">×</span>
<field name="x_fc_height"
placeholder="H"/>
<field name="x_fc_dim_uom" nolabel="1"/>
</div>
</group>
<group>
<p class="text-muted" colspan="2">
Enter the weight and dimensions of the
packaging you'll use to ship the finished
parts back. The system reuses the same
boxes for the return shipment. Click
<strong>Generate Outbound Label</strong>
in the header once carrier + weight are
set.
</p>
</group>
<group string="Quantities (populated by racking crew)">
<field name="expected_qty" readonly="1"/>
<field name="received_qty" readonly="1"/>
<field name="qty_match" widget="boolean_toggle" readonly="1"/>
</group>
</group>
<notebook>
<page string="Multi-Piece Packages"
name="outbound_packages"
invisible="not x_fc_carrier_id">
<p class="text-muted">
For multi-box shipments, add one row per
box with its weight + dimensions. The
carrier API will return one tracking
number + one label per row.
<strong>Single-box flow:</strong> leave
this empty and the top-level weight/dim
fields above are used.
</p>
<field name="x_fc_outbound_package_ids">
<list editable="bottom">
<field name="sequence" widget="handle"/>
<field name="weight"/>
<field name="length"/>
<field name="width"/>
<field name="height"/>
<field name="tracking_number"
readonly="1"/>
<field name="label_attachment_id"
readonly="1"
widget="many2one_binary"/>
</list>
</field>
</page>
<page string="Receiving Lines" name="lines">
<field name="line_ids">
<list editable="bottom">
<field name="part_number"/>
<field name="description"/>
<field name="expected_qty"/>
<field name="received_qty"/>
<field name="condition"/>
<field name="notes"/>
</list>
</field>
</page>
<page string="Damage Log" name="damage">
<field name="damage_ids">
<list editable="bottom">
<field name="description"/>
<field name="severity" widget="badge"
decoration-info="severity == 'cosmetic'"
decoration-warning="severity == 'functional'"
decoration-danger="severity == 'rejected'"/>
<field name="action_required"/>
<field name="customer_notified"/>
<field name="resolved"/>
</list>
</field>
</page>
<page string="Photos" name="photos">
<field name="attachment_ids" widget="many2many_binary"/>
</page>
<page string="Notes" name="notes">
<field name="notes"
placeholder="Internal notes about this receiving..."/>
</page>
</notebook>
</sheet>
<chatter/>
</form>
</field>
</record>
<!-- ===== Receiving Search View ===== -->
<record id="view_fp_receiving_search" model="ir.ui.view">
<field name="name">fp.receiving.search</field>
<field name="model">fp.receiving</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="sale_order_id"/>
<field name="partner_id"/>
<field name="po_number"/>
<separator/>
<filter string="Awaiting Parts" name="draft" domain="[('state', '=', 'draft')]"/>
<filter string="Inspecting" name="inspecting" domain="[('state', '=', 'inspecting')]"/>
<filter string="Discrepancy" name="discrepancy" domain="[('state', '=', 'discrepancy')]"/>
<filter string="Accepted" name="accepted" domain="[('state', '=', 'accepted')]"/>
<group>
<filter string="Customer" name="group_customer" context="{'group_by': 'partner_id'}"/>
<filter string="Status" name="group_state" context="{'group_by': 'state'}"/>
<filter string="Received Date" name="group_received_date" context="{'group_by': 'received_date:month'}"/>
</group>
</search>
</field>
</record>
<!-- ===== Window Action ===== -->
<record id="action_fp_receiving" model="ir.actions.act_window">
<field name="name">Receiving</field>
<field name="res_model">fp.receiving</field>
<field name="view_mode">list,form</field>
<field name="search_view_id" ref="view_fp_receiving_search"/>
<field name="context">{'search_default_draft': 1}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No receiving records yet
</p>
<p>
Receiving records are created automatically when a sale order is
confirmed. They track quantity verification, condition inspection,
and damage logging for customer parts arriving at the shop.
</p>
</field>
</record>
</odoo>