feat(jobs): Sub 14 — configurable workflow state bar (Path B)

Replaces the generic Draft/Confirmed/In Progress/Done statusbar with
a shop-configurable list of plating-specific milestones. Bar advances
automatically as recipe steps complete; no manual button clicks.

What ships
==========

* New model: fp.job.workflow.state
  Catalog of milestones (name, code, sequence, color, triggers).
  Triggers can be:
    - trigger_default_kinds: "receiving,inspect" matches by step.default_kind
    - trigger_first_step_started: any wet/bake/mask/rack step started
    - trigger_all_steps_done: every non-cancelled step in done/skipped
    - block_when_quality_hold: held back while NCR/hold open
  Plus per-recipe-node override (see below).

* Default 7-state seed (data/fp_workflow_state_data.xml):
    Draft → Confirmed → Received → In Progress → Inspected → Shipped → Done
  noupdate=1 so per-shop edits survive module upgrade.

* Recipe-side trigger field on fusion.plating.process.node:
    triggers_workflow_state_id (Many2one, optional)
  Wins over default_kind matching. Lets the recipe author pin a
  specific step as a milestone trigger even when default_kind isn't
  set or doesn't match. Exposed in the Recipe Tree Editor properties
  panel (dropdown sourced from the catalog).

* fp.job.workflow_state_id (computed, stored)
  Iterates the catalog in sequence order; lands at the highest passed
  milestone. Recomputes on step state / kind / recipe_node / quality
  hold changes. Replaces fp.job.state on the form's statusbar.

* Settings UI: Configuration > Workflow States
  Standard list+form pages so admins can add / edit / deactivate
  states. Manager-group write permission, supervisor read.

What this does NOT do
=====================
  * Doesn't drop fp.job.state — that field still drives the internal
    state machine (button_confirm, action_cancel, etc.). Only the
    UI statusbar is reassigned.
  * No migration for existing jobs — they auto-recompute on next read
    because workflow_state_id is a stored compute with the right
    api.depends. Existing WH/JOB/00342 will display its current
    workflow state on next page load.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-05-03 23:39:38 -04:00
parent 4c6bad04c5
commit 4e0b74d7ae
12 changed files with 564 additions and 2 deletions

View File

@@ -48,6 +48,16 @@
invisible="state in ('draft', 'cancelled')"/>
</xpath>
<!-- Sub 14 — Replace the generic Draft/Confirmed/In Progress/Done
statusbar with the configurable workflow_state_id bar.
Operators see meaningful plating milestones (Received,
Inspected, Shipped, etc.) instead of generic Odoo states. -->
<xpath expr="//header/field[@name='state']" position="replace">
<field name="workflow_state_id"
widget="statusbar"
options="{'clickable': '0'}"/>
</xpath>
<!-- Surface part / coating / recipe on the header so the
floor knows WHAT they're plating without diving into
Source. The "Reference Product" line in core is just

View File

@@ -0,0 +1,109 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Sub 14 — Workflow state catalog UI (admin / Settings).
-->
<odoo>
<!-- ====================== List view ====================== -->
<record id="view_fp_workflow_state_list" model="ir.ui.view">
<field name="name">fp.job.workflow.state.list</field>
<field name="model">fp.job.workflow.state</field>
<field name="arch" type="xml">
<list string="Workflow States" default_order="sequence, id">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="code"/>
<field name="color" widget="badge"
decoration-info="color == 'blue'"
decoration-success="color == 'success' or color == 'green'"
decoration-warning="color == 'yellow' or color == 'orange'"
decoration-danger="color == 'danger'"
decoration-muted="color == 'grey'"/>
<field name="trigger_default_kinds"/>
<field name="trigger_first_step_started"/>
<field name="trigger_all_steps_done"/>
<field name="block_when_quality_hold"/>
<field name="is_initial"/>
<field name="is_terminal"/>
<field name="active" widget="boolean_toggle"/>
</list>
</field>
</record>
<!-- ====================== Form view ====================== -->
<record id="view_fp_workflow_state_form" model="ir.ui.view">
<field name="name">fp.job.workflow.state.form</field>
<field name="model">fp.job.workflow.state</field>
<field name="arch" type="xml">
<form string="Workflow State">
<sheet>
<div class="oe_title">
<label for="name"/>
<h1><field name="name" placeholder="Received"/></h1>
</div>
<group>
<group string="Identity">
<field name="code" placeholder="received"/>
<field name="sequence"/>
<field name="color" widget="badge"/>
<field name="active"/>
</group>
<group string="Lifecycle role">
<field name="is_initial"/>
<field name="is_terminal"/>
<field name="block_when_quality_hold"/>
</group>
</group>
<group string="Trigger conditions">
<field name="trigger_default_kinds"
placeholder="receiving, inspect"/>
<field name="trigger_first_step_started"/>
<field name="trigger_all_steps_done"/>
<p class="text-muted oe_grey mt-2">
<strong>How triggers combine:</strong> a state is "passed"
when EITHER the special trigger is true, OR every
recipe step matching the listed default_kinds (or
tagged via the per-node override on the recipe) is
in done/skipped/cancelled state.
<br/>
<em>block_when_quality_hold</em>: holds back the
advance even if the trigger conditions are met,
until all open quality holds on the job are closed.
</p>
</group>
<group string="Notes">
<field name="description" nolabel="1"
placeholder="What this milestone represents and when it should fire..."/>
</group>
</sheet>
</form>
</field>
</record>
<!-- ====================== Action + Menu ====================== -->
<record id="action_fp_workflow_state" model="ir.actions.act_window">
<field name="name">Workflow States</field>
<field name="res_model">fp.job.workflow.state</field>
<field name="view_mode">list,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create your first workflow state.
</p>
<p>
Workflow states are the milestones that appear on the
status bar of every plating job. Each state passes
automatically when its trigger conditions are met
(recipe step kind finishes, all steps done, etc.).
</p>
</field>
</record>
<menuitem id="menu_fp_workflow_state"
name="Workflow States"
parent="fusion_plating.menu_fp_config"
action="action_fp_workflow_state"
sequence="80"/>
</odoo>