I was wrong about the DOM. Verified from Odoo 19 source on entech:
web/static/src/views/fields/float/float_field.xml
web/static/src/views/fields/char/char_field.xml
web/static/src/views/list/list_renderer.xml
Float/Char fields render as a BARE <span> (read mode) or BARE
<input class="o_input"> (edit mode) directly inside the <td>.
There is NO .o_field_widget wrapper. So all my prior CSS targeting
.o_field_widget matched nothing.
Also discovered: Odoo's getCellClass() in list_renderer.js calls
canUseFormatter() which strips custom <field> classes when the
column has widget="..." set:
canUseFormatter(column, record) {
if (column.widget) {
return false; // ← class stripped here
}
...
}
So o_fp_iw_value class doesn't even land on cells with
widget="boolean_toggle"/"image". Those cells render natively;
boolean toggle and image styling now targets the widgets directly
wherever they appear (.o_boolean_toggle, .o_field_image).
Fix: put visible chrome (border, bg, padding, min-height) on the
<td> itself for prompt/meta/value/extras cells. Make inner span
and input transparent + inherit. Focus ring travels up via
:focus-within on the td.
Cells now look like obvious input boxes from first paint, regardless
of whether the user has clicked into edit mode.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Root cause user kept seeing inputs as bare/borderless text:
Odoo's <list editable="bottom"> renders each cell as a read-mode
<span> inside .o_field_widget UNTIL the user clicks the cell.
Only then does an <input> swap in. My CSS was targeting
`td.o_fp_iw_value input { ... }` so the chrome only appeared on
focus. Every other (unclicked) cell looked like dead text.
Fix:
Move all input chrome (border, bg, padding, min-height) to the
.o_field_widget wrapper which is ALWAYS in the DOM. Then make
the inner <input> / <span> transparent so they inherit. Effect:
the cell looks like an input box from first paint, regardless
of focus state. Focus ring travels up via :focus-within.
Special widgets (boolean toggle, photo upload, multi-point,
bath panel) opt OUT of the wrapper chrome via :has() so they
keep their own visual treatment.
Same fix applied to .o_fp_iw_extra cells (composite types).
User reproduction: WH/JOB/00339 → Record on Masking step. After
hard-refresh + this build, every value cell should read as an
obvious input box even before the operator clicks.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four visible bugs reported by user after deploy:
1. Type + Unit pills overlapped at top-right of every card.
Root cause: both <field>s carried the same .o_fp_iw_meta class
AND both mapped to grid-area: meta. CSS Grid stacked them on
top of each other so the labels rendered as overlap garbage
(e.g. "eachnber", "Time(secs)Time(seconds)").
Fix: distinct classes (.o_fp_iw_meta_type / .o_fp_iw_meta_unit)
each in its own grid column. Grid is now 4 columns wide:
"prompt | type | unit | trash"
2. Input borders barely visible in dark mode (#343942 on #22262d).
Operators couldn't tell where to click.
Fix: brighter border using $fp-iw-ink-faint instead of $fp-iw-border.
Hover bumps to $fp-iw-ink-mute. Focus uses brand purple. Also
added a slight surface tint ($fp-iw-page) so empty inputs read
as obviously-interactive instead of blending into the card.
3. Photo widget rendered enormous (full card width).
Root cause: max-width applied only to the preview image, not
to the .o_field_image container itself.
Fix: max-width 240px on .o_field_image AND its inner controls.
4. Numeric values floated centered in empty space.
Root cause: input width wasn't stretching to its grid cell;
default Odoo numeric-cell text-align: right plus our missing
width: 100% left tiny inputs centered in the value area.
Fix: explicit width: 100%, text-align: left, and 420px
max-width on the .o_field_widget container.
Bonus polish:
* Trash icon hidden by default (opacity: 0), reveals to 0.6 on
row hover, full opacity on direct hover. Reduces visual noise
for the common case where operator just types and saves.
* Boolean toggle scale bumped from 1.4 to 1.5 + adds left margin
so the switch sits properly inside the value cell.
* Mobile (<900px) grid collapses to: prompt|trash / type|unit /
value / extras — keeps the type+unit pair on one row but lets
them flow naturally below the prompt.
No model changes. SCSS + XML view only. v2 view still in place
for instant rollback.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two coherent feature drops shipping together because their fp_job_step
edits overlap. Both target operator workflow correctness.
## Sub 13 — Sequential step enforcement (recipe + per-step)
Background:
Investigation on WH/JOB/00339 showed operators starting Incoming
Inspection while Contract Review was still in_progress. Audit:
98.7% of recipe operations system-wide had requires_predecessor_done
= false (the legacy per-step opt-in defaults off, recipe authors
rarely tick the box).
Architecture:
Recipe-level toggle + per-step opt-out (Option A from /investigate).
* fusion.plating.process.node.enforce_sequential — Boolean on the
recipe root. Default True. When True, every operation under this
recipe waits for earlier-sequence steps to finish before it can
start.
* fusion.plating.process.node.parallel_start — Boolean on operation
nodes. When True, this step bypasses the sequential gate (e.g.
paperwork or QA review that runs alongside production).
* Mirrored on fp.step.template (parallel_start) so library steps
carry the flag into snapshots.
* fp.job.enforce_sequential — related from recipe_id. Snapshotted
at job creation so a recipe author flipping the recipe's flag
AFTER job generation does NOT change behaviour mid-run.
* fp.job.step.parallel_start — related from recipe_node_id.
* Decision matrix (encapsulated in
fp.job.step._fp_should_block_predecessors):
recipe.enforce_sequential | step.parallel_start | step.req_pred_done | block?
--------------------------|---------------------|--------------------|------
True | False | any | YES
True | True | any | no
False | any | True | YES
False | any | False | no
* Manager bypass via context fp_skip_predecessor_check=True (existing).
Runtime gates:
* fp.job.step.button_start — calls _fp_should_block_predecessors;
raises UserError naming the blocking earlier step(s).
* fp.job.step.can_start — computed Boolean for view-side disable.
* Move wizard predecessor check
(fusion_plating_shopfloor/controllers/move_controller.py) — uses
the same helper so tablet + backend behave identically.
UI surface:
* Recipe form (fp_process_node_views.xml) — enforce_sequential
toggle on recipe root, parallel_start checkbox on operations.
* Step template form — parallel_start checkbox.
* Simple Recipe Editor (inline library form) — Parallel Start
checkbox + legacy flag demoted with muted styling + supervisor
group gate.
* Recipe Tree Editor (properties panel) — both flags exposed,
only-show on the right node_type.
* Controllers updated to allowlist + payload the new fields.
Migration:
fusion_plating/migrations/19.0.18.12.0/post-migrate.py — sets
enforce_sequential = TRUE on every existing recipe-root node.
Idempotent. User confirmed dev-stage data, so retroactive flip
is safe (no production jobs to disrupt).
Tests:
TestSequentialEnforcement (10 tests) covering:
* sequential mode blocks out-of-order start
* first step always startable
* predecessor finish/skip unlocks next
* parallel_start opts out of gate
* free-flow mode bypasses gate
* legacy requires_predecessor_done still honoured in free-flow
* manager bypass via context
* can_start compute reflects state correctly
* library template parallel_start snapshots into recipe-node
## Sub 12e — Record Inputs Wizard v3 (card layout, dark-mode aware)
Background:
v2 wizard was a 17-column wide editable table. Operators got lost
finding which value column applied to their row's type, horizontal
scroll required on tablets, composite types crammed into one row.
New layout:
* Each measurement renders as a stacked card (CSS Grid + display
transformation on the existing list widget — preserves inline
editing, no JS rewrite).
* Card header: prompt name (large, bold) + type/unit pills.
* Card body: ONLY the value widget for this row's type
(number / boolean / date / text / photo / multi-point / panel).
* Composite types (multi-point thickness 5x reading + avg, bath
panel 4 fields) get inline sub-grid inside the card.
* Empty state ("no measurement prompts") with friendly CTA.
Dark mode:
* SCSS branches at compile time on $o-webclient-color-scheme
(per fusion-plating/CLAUDE.md note).
* Tokens: 7 surface colours + 4 ink levels with light/dark hex
pairs, all behind var(--fp-*) custom properties for per-deploy
override.
* Registered in BOTH web.assets_backend AND web.assets_web_dark
so each bundle compiles its own palette.
Tablet polish:
@media (max-width: 900px) — collapse meta below prompt + bump
numeric input min-height to 56px.
Defensive:
* v2 view kept in the XML file (instant rollback by changing one
view_id ref).
* `:has(.o_invisible_modifier)` rule drops empty cells out of the
grid so Odoo's invisible="..." doesn't punch holes in layout.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two bugs fixed in one drop, both targeting the contract review (QA-005)
enforcement gap reported on entech.
## Bug 1 — WO step routed to wrong wizard
Symptom: clicking Finish & Next or Record on a Contract Review step in
WH/JOB/00339 opened the generic measurement wizard with three fake
prompts (Reviewer Initials / Date Reviewed / QA-005 Approved). No path
to the actual QA-005 form from the work order.
Root cause: action_finish_and_advance + action_open_input_wizard had no
branch for recipe_node.default_kind == 'contract_review'. The step.kind
mapping collapses contract_review -> 'other' so kind-based detection
wouldn't have worked either; gate has to live at the recipe-node layer.
Fix in fusion_plating_jobs/models/fp_job_step.py (v19.0.8.14.6):
- action_finish_and_advance:329 calls _fp_contract_review_redirect
before the input-wizard branch
- action_open_input_wizard:844 same gate, keeps Record button consistent
- _fp_contract_review_redirect:866 (new) returns the part's
action_start_contract_review() unless review.state in
(complete, dismissed) — gate clears so the step can finish after
the operator signs QA-005.
## Bug 2 — Part create did not enforce contract review
Symptom: spec called for a banner-only UX. User wanted true automatic
enforcement on first part creation under an enforced customer.
Fix in fusion_plating_quality/models/fp_part_catalog.py (v19.0.4.10.0):
- @api.model_create_multi def create() override
- _fp_enforce_contract_review_on_create() helper auto-stages the
fp.contract.review record AND surfaces three prominent reminders:
1. Sticky bus.bus warning toast (top-right, doesn't auto-dismiss)
2. mail.activity (To Do) on the part for the current user
3. Smart button on the part form lights up (review now exists)
- Idempotent: skips parts that already carry a review id
- Soft-fails: bus or activity outage doesn't block part creation
- create()-only — write/update flows never re-trigger
Sub 4's existing info banner stays as a fourth surface.
## Tests
- fusion_plating_jobs/tests/test_fp_job_extensions.py:
+TestContractReviewStepRouting (5 tests covering both routing methods,
the complete/dismissed gate-clear, and non-CR step regression)
- fusion_plating_quality/tests/test_part_catalog_contract_review_enforcement.py
(NEW): 9 tests covering auto-create, batch create, idempotency,
activity surface, bus surface, write-must-not-retrigger, soft-fail.
- docs/superpowers/tests/2026-04-22-sub4-smoke.py: flipped the
"no review yet" assertion to "review auto-created" to match new
behavior. Sign-flow assertions unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bucket 1 — Generation bug fix
- post-migrate.py for 19.0.18.8.0 promotes flat 'step' children of
recipes to 'operation' so fp.job._generate_steps() picks them up.
Filter is narrow: only direct children of node_type='recipe' get
flipped, tree-editor sub-steps (parent.node_type='operation') are
untouched. Idempotent. Posts an audit chatter note on each affected
recipe.
- Simple Editor controller hardcodes node_type='operation' on insert
+ snapshot-import path so future recipes start correct.
Bucket 2 — Inline library authoring
- 6 new JSONRPC routes (/fp/simple_recipe/library/load + save +
seed_defaults + input/{add,write,remove}, /fp/simple_recipe/tank/list).
- + New Step button in the right pane opens an inline form with name /
kind / icon / instructions / stations / flags / prompts table.
- Pencil icon on each library row reopens the same form prefilled.
- Step Kind picker leads with 'Generic — no automatic behaviour'.
- 'Seed defaults from kind' calls action_seed_default_inputs server-side
for kinds that have curated default prompts.
Bucket 3 — Back nav
- '← Recipes' button in the header (or '← Part' when opened from
Process Composer) mirrors recipe_tree_editor.js, with
clearBreadcrumbs:true to avoid stack pollution.
Verified on entech: LGPS1104's 19 'step' children now show as
'operation', migration chatter note posted on the recipe, asset cache
busted.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Spec for the upcoming Simple Recipe Editor refinement:
- Fix node_type bug so Simple-Editor recipes generate job steps
- Inline + New Step / pencil-edit library authoring with prompts
- Back button + breadcrumb-aware navigation (mirrors tree editor)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Plating Jobs list was sorted priority-desc, deadline-asc, id-desc — which
mixed Done (closed) jobs in with Confirmed (open) jobs and made the list look
chaotic to managers. Done jobs from weeks ago surfaced above active work.
Two changes:
1. New stored compute fp.job.state_priority (Integer, indexed) ranks states
by managerial relevance: in_progress=0, confirmed=1, draft=2, on_hold=3,
done=4, cancelled=5. _order now leads with state_priority asc, then
priority desc, then date_deadline asc, then id desc. Active work bubbles
to the top automatically.
2. Plating Jobs action defaults to a new 'Open' filter
(state not in done, cancelled). Managers see only active work by default;
they untick the filter to see history. Added On Hold + Cancelled filters
too for full state coverage.
Verified on entech: top 10 jobs are now all in_progress, sorted by deadline
ascending. Existing 26-row list goes from chaotic to focused.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Operator Instructions panel had a hardcoded inline style
(background: #f8f9fa) which became a white-on-dark unreadable blob
in dark mode. Replaced with a CSS class backed by an SCSS file that
branches at compile-time via $o-webclient-color-scheme — registered
in both web.assets_backend (light) and web.assets_web_dark (dark)
bundles per the CLAUDE.md pattern.
Tokens: panel bg #f8f9fa light / #22262d dark; border #d8dadd /
#3a3f47; text #212529 / #e8eaed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Click a step's name in the embedded job-form list → opens a read-only
modal with everything a manager wants in one scroll: equipment,
schedule, master collect-measurements banner, operator instructions
(rich-text from recipe_node.description), measurement prompts list,
and values recorded so far.
Implementation: separate read-only form view bound to the embedded
field via context={'form_view_ref': '...'}. The standalone editable
form view stays registered for the Job Steps menu, so direct
navigation still loads the editable variant.
Three new computed/related fields on fp.job.step:
- quick_look_instructions (Html, related from recipe_node_id.description)
- quick_look_prompt_ids (filtered+sorted recipe_node.input_ids, step_input only)
- quick_look_recorded_value_ids (search across moves: input_value rows
whose move.from_step_id == self.id)
Plus a small action_open_full_form method that escapes from the modal
to the editable form when the manager actually needs to edit.
Edge cases:
- No recipe_node_id → instructions panel shows empty-state hint
- collect_measurements=False → amber banner: "Master switch off — no
values will be collected at runtime"
- Multiple moves on same step → values list shows all, newest first
Spec: docs/superpowers/specs/2026-04-30-step-details-modal-design.md.
Verified on entech: step "11. Hard Anodize Type III" populates with
516 chars instructions + 7 prompts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Critical UX gap discovered in production-environment battle test: when
operator hits "Mark Done" and the input wizard fires, they only saw the
measurement prompts list. The rich-text instructions written by the
office (recipe_node.description) never reached the operator at the
exact moment they need them.
Fixed: wizard model gains instructions (Html, computed from
step.recipe_node_id.description) + has_instructions flag.
Form view renders the instructions in a prominent blue alert at the
top of the wizard, above the Measurements list. Hidden when blank
so operators on instruction-less steps don't see noise.
Also: extend default_kind Selection on fusion.plating.process.node to
match fp.step.template — both models now have the same 24 kinds. Without
this, recipe authors could pick a kind in the library template form
that the recipe-node Selection rejected with a ValueError.
Battle test artifact:
- Recipe "Hard Anodize Type III + Dye + Seal" (id=1863) — 23 steps,
105 measurement prompts, rich-text operator instructions per step
- SO S00278 for ABC Manufactoring confirmed → fp.job 1236 / WH/JOB/00337
with all 23 steps materialized, 105 prompts visible to operators
- Wizard test: step "11. Hard Anodize Type III" → 516 chars of
instructions render + 7 input prompts in the form
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Battle-tested complete workflow on entech: ABC Manufacturing + Anodize
recipe (id=136) cloned to part-variant (id=1775) → SO S00276 confirmed →
fp.job 1234 with 17 steps → recorded 56 measurement values exercising all
13 input types (incl. all 4 new types) → CoC chronological report renders
69KB with all values incl. photo thumbnails.
Bugs found and fixed:
1. fp.process.node.input_ids missing copy=True — when a master recipe
was cloned per-part (the standard variant pattern), the operator
prompts on each step did NOT get copied to the variant. Result: jobs
built from variants ran with zero prompts even though the master had
them. Fixed: input_ids now copy=True so cloning auto-duplicates.
2. CoC chronological template read dest.input_ids where dest is
fp.job.step. Steps don't carry input_ids — that field lives on the
recipe node. Result: AttributeError aborted the entire CoC render.
Fixed: walk via dest.recipe_node_id.input_ids; preserves the existing
collect=True filter.
3. CoC chronological template used hasattr() in a t-value expression.
QWeb's expression engine doesn't expose Python builtins, raised
KeyError: 'hasattr'. Fixed: use 'collect' in i._fields instead.
Also enhanced photo rendering in CoC: was just "[Attachment]" placeholder;
now renders an actual <img> thumbnail (max 80px tall) plus the filename.
Battle-test script saved to fusion_plating/scripts/bt_e2e_anodize_v2.py
for re-runs / regression testing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implements 2026-04-29-step-library-audit-design.md. Bumps fusion_plating
to 19.0.18.7.0, fusion_plating_jobs to 19.0.8.12.0, fusion_plating_reports
to 19.0.10.2.0.
LIBRARY EXPANSION
- 8 new Step Kinds: Receiving, Electroclean, Strike, Salt Spray,
Adhesion Test, Hardness Test, Packaging, Tank Replenishment
- 4 new input types: photo, multi_point_thickness, bath_chemistry_panel, ph
- DEFAULT_INPUTS_BY_KIND rewritten to seed audit-grade prompts on every
kind (bath IDs, photos, multi-point thickness, signatures, etc.)
- + Common Audit Fields one-click button on the library template form
- Default Operator Instructions relabel + alert callout
PER-RECIPE CONFIGURABILITY
- collect (Boolean) per recipe-step input prompt — opt out without delete
- collect_measurements (Boolean) master switch on recipe step — when off,
wizard skips entirely
- template_input_id (Many2one) traceability link from recipe to library
- Recipe-step backend form view exposes the new fields with handle drag,
toggle, target range, and library-source column
RUNTIME WIRING
- Step input wizard filters node.input_ids to step_input AND collect=True;
short-circuits on collect_measurements=False
- New input types: photo (image widget + ir.attachment), multi-point
thickness (5 readings + auto avg, skips empty cells), bath chemistry
panel (pH/conc/temp/bath bundle), pH (0-14 numeric)
- Composite values JSON-serialized into value_text; photo via attachment
CoC REPORT
- Filters captured prompts to collect=True only
- Renders new input types with appropriate format
MIGRATION (post-migrate.py for 19.0.18.7.0)
- Backfills collect=True on recipe-step inputs
- Backfills collect_measurements=True on recipe steps
- Re-runs action_seed_default_inputs on every existing template
(idempotent, preserves user edits)
- Backfills template_input_id by name-matching against source library
template (handles JSONB vs varchar name columns)
SEED DATA
- 8 example templates (one per new kind) in fp_step_template_data.xml
with noupdate=1
BATTLE TEST
- bt_step_library_audit.py: 29 assertions all PASS on entech
OWL EDITOR EXTENSION DEFERRED
- The simple recipe editor's per-step Instructions/Measurements
expansions were not implemented in this pass; users configure via the
backend recipe-step form. Track follow-up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds 8 new Step Kinds (Receiving, Electroclean, Strike, Salt Spray,
Adhesion Test, Hardness Test, Packaging, Replenishment) with industry-
standard default measurements. Adds 4 new input types (photo,
multi_point_thickness, bath_chemistry_panel, ph). Beefs up existing
kinds (cleaning, etch, plate, bake, ship, etc.) with bath ID, photos,
multi-point thickness, signatures.
Per-recipe configurability: each recipe step can disable, rename,
retarget, reorder prompts; add custom prompts; toggle entire-step
data collection. Library is the smart default; recipe is final say.
Office-to-operator instructions: relabel as Default Operator
Instructions in the library; per-recipe override surfaced in the
simple recipe editor; falls back to library default at runtime when
recipe override is empty.
Battle test plan covers 18 assertions end-to-end on entech.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolution chain: explicit override → days offset → part lead time → order
commitment. Adds x_fc_default_lead_time_days on part catalog; per-line
effective_part_deadline + effective_internal_deadline computes; order-level
completion_date rollup + is_late_forecast warning.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Records full session work for future Claude Code sessions:
Sub 12a — Simple Recipe Editor + Step Library
3 new models, additive fields on process.node + node.input,
fp_simple_recipe_editor OWL action with drop-position simulator,
11 JSONRPC endpoints, snapshot semantics, post_init seeding.
Sub 12b — Move Parts / Move Rack / Rack Parts / Stop Timer dialogs
fp.rack.tag, fp.job.step.move + .input.value, racking_state on
fusion.plating.rack (orthogonal to wear state), state machine on
existing fp.job.step.timelog (no parallel labor model), 12 tablet
endpoints, 4 OWL dialogs with fp-resolve-rack custom event,
manager-bypass flags, plant overview Racks pane.
Sub 12c — Reports + Labor History
Operator Traveller v2 (A4 landscape, paper-style), chronological
CoC body via fp.certificate.body_style + coc_body_router,
Labor History views, gap-fix bundle (rack travel ticket PDF,
per-customer cert statement 3-tier resolution, captured Actual
values from move.input.value).
Phase 1/2/3 — Menu reorganization
Top-level: 17 → 6 (operator-visible). Industry verticals nested
under Compliance hub. Move Log/Labor History/Maintenance under
Operations. Certificates under Quality.
Configuration: 36 flat → 7 themed folders + Settings sibling.
Group-gating: KPIs/Move Log/Replenishment Suggestions →
supervisor+. Operator now sees ~5 top-levels instead of ~10.
Landing page resolver
action_fp_resolve_plating_landing server action, user override →
company default → Sale Orders fallback. x_fc_pickable_landing
Boolean tag for curated picklist.
Production Line / Routing Station rename
fusion.plating.work.center → 'Production Line' (shop-layout, owns
tanks). fp.work.centre → 'Routing Station' (per-step routing,
cost-per-hour, mrp.workcenter replacement). Model IDs unchanged.
Other ergonomics
Tank field labels (Code → Tank Number, Tank → Tank Name) +
state-control header buttons. SO smart-button 'Plating Jobs' → 'WO'.
Default landing screen = Sale Orders.
Drop-position simulator in Simple Recipe Editor.
Updated 'Menu Structure' section near top of CLAUDE.md to reflect
new 6-top-level layout + 7-folder Configuration grouping.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two distinct entities were both labelled 'Work Centre' (US/UK spelling
the only differentiator — confusing). Renamed by purpose, model IDs
unchanged so all 12+9 existing cross-refs keep working:
fusion.plating.work.center → 'Production Line'
Physical shop-layout grouping that owns tanks. Examples: 'Line 1 —
EN', 'Anodize Line', 'Prep Bay'. Has tank_ids (O2M),
supported_process_ids (M2M), capacity_per_day. The thing tanks live
in.
fp.work.centre → 'Routing Station'
Per-job-step routing entity (post-Sub-11 mrp.workcenter replacement).
Has 'kind' selection (wet_line / bake / mask / rack / inspect),
cost_per_hour for fp.job.step rollup, default_bath_id +
default_tank_id for release-ready validation. The thing a job step
routes through.
Conceptually a Production Line CONTAINS many Routing Stations (e.g.
'EN Line' production line has wet-line, bake, inspect routing
stations on it).
Updated:
- _description on both models
- string= on the name fields
- list/form/search view strings
- act_window names ('Production Lines' / 'Routing Stations')
- menu items in fp_menu.xml + fp_jobs_menu.xml
- doc comments in both model files explaining the distinction
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three targeted gates so operators no longer see admin/audit views:
- KPIs (menu_fp_dashboard) → supervisor+. Operators don't need
dashboards; their tablet shows what they need to do next.
- Move Log (menu_fp_job_step_move) → supervisor+. Operators see
their own moves on the tablet; this top-level menu is the
audit-of-everyone-else view.
- Replenishment Suggestions (menu_fp_replenishment_suggestions) →
supervisor+. Purchasing decision, not operator concern.
Other top-levels were already correctly gated:
- Sales / Configurator → estimator
- Shipping & Receiving → group_fp_receiving
- Compliance hub → supervisor+
- Configuration → manager
- Shop Floor / Quality → operator (correctly visible to floor staff)
- Operations parent stays open; child menus enforce per-action gates
Net effect: a fresh operator now sees ~5 top-level menus instead of
the previous ~10. Supervisors see ~8. Managers see all.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Within fp_menu.xml itself, the menu_fp_replenishment_rules entry
referenced menu_fp_config_materials_tanks which was defined later in
the same file. Odoo's data loader is strictly top-down within a file.
Reorganized by section: 1) root, 2) Configuration + 7 buckets,
3) Compliance hub, 4) Operations parent, 5) all child menus (referencing
parents already defined above).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fp_rack_tag_views.xml (and several other view files) reference the
new Phase-2 Configuration sub-folder menus (menu_fp_config_*) defined
in fp_menu.xml. Odoo's data loader is strictly sequential within a
module, so fp_menu.xml must come before any file that references
its bucket xmlids.
Caught by entech upgrade (ParseError on rack-tag menuitem).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 1 collapses the Plating app's 17 top-level menus down to 6
domains (Sales, Operations, Receiving & Shipping, Quality,
Compliance, Configuration) so users no longer scroll a 17-item
sidebar to find one thing.
Re-parented (no XML id changes — bookmarks still work):
- fusion_plating_compliance.menu_fp_compliance_root
→ menu_fp_compliance_hub (renamed 'General')
- fusion_plating_safety.menu_fp_safety_root
→ menu_fp_compliance_hub (renamed 'Safety / WHMIS')
- fusion_plating_aerospace.menu_fp_aerospace
→ menu_fp_compliance_hub (renamed 'Aerospace (AS9100 / Nadcap)')
- fusion_plating_nuclear.menu_fp_nuclear
→ menu_fp_compliance_hub (renamed 'Nuclear (CSA N299 / CNSC)')
- fusion_plating_cgp.menu_fp_cgp
→ menu_fp_compliance_hub (renamed 'Controlled Goods (CGP)')
- fusion_plating_certificates.menu_fp_certificates
→ menu_fp_quality (Certs are a Quality output, not a separate
top-level concern)
- fusion_plating_bridge_maintenance.menu_fp_maintenance
→ menu_fp_operations
- fusion_plating.menu_fp_job_step_move (Move Log)
→ menu_fp_operations
- fusion_plating.menu_fp_job_step_timelog (Labor History)
→ menu_fp_operations
The new menu_fp_compliance_hub is supervisor-gated; underlying
verticals retain their own group locks (CGP officer, etc.).
Settings menu remains manager-gated through inheritance from
menu_fp_config (already in place).
NEW — Plating landing-page resolver:
- ir.actions.act_window.x_fc_pickable_landing (Boolean tag for
curated picklist; flagged on Sale Orders, Quotations, Process
Recipes for Phase 1; more in Phase 2)
- res.company.x_fc_default_landing_action_id (admin sets fallback)
- res.users.x_fc_plating_landing_action_id (per-user override)
- ir.actions.server action_fp_resolve_plating_landing — picks
user → company → Sale Orders fallback at click time
- menu_fp_root rewired to call the resolver
- User profile + Settings tabs surface the dropdowns
Configurator's earlier menu_fp_root override (action_fp_sale_orders
direct) removed — core's resolver now owns the routing.
Versions bumped: fusion_plating 19.0.11.0.0, configurator
19.0.17.16.0, plus 7 dependent modules patched.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the per-row 'highlight whole row' feedback (operator
couldn't tell whether the new step would land before or after the
hovered row) with a precise insertion-point indicator.
How it works:
- Each row's onDragOver computes ev.clientY vs row midpoint.
Above midpoint → insertion index = rowIndex (BEFORE).
Below midpoint → insertion index = rowIndex + 1 (AFTER).
- An <o_fp_drop_indicator> div lives BEFORE the first row and
AFTER every row. When state.dragOverIndex matches that slot's
index, the div expands from height:0 to a 2.25rem dashed-green
reservation strip with a ghost-preview chip ('↓ insert here →
<icon> <step name>').
- onDragStart captures the dragged step/template's name + icon
into state.dragPreviewLabel/Icon for the chip text.
- Smooth 80ms height/margin transition so the line glides between
slots as the cursor moves rather than blinking.
- Trailing dropzone retains its existing 'Drop here to add at end'
styling. Empty list shows 'Drag a library step here to start'.
- onDrop reads from state.dragOverIndex (set by the most-recent
onDragOver) so we drop at the simulated position exactly.
- onDragLeave guards against child-element flicker via
relatedTarget contains() check.
- onDragEnd clears state.dragPreviewLabel/Icon so a half-completed
drag (cancelled by Esc) doesn't leave the chip stuck on screen.
fusion_plating → 19.0.10.4.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Gap 1 — Rack Travel Ticket PDF (Sub 12b's Save+Print 404):
+ report_fp_rack_travel.xml in fusion_plating_reports — A5 landscape
single page, big rack name, Code 128 of FP-RACK:<name>, tag chips,
contained part-batches table.
+ ir.actions.report bound to fusion.plating.rack so it appears in
the rack form's Print menu too.
+ Sub 12b's rack_parts_dialog.js Save+Print URL fixed to use the
standard /report/pdf/<xmlid>/<id> route.
Gap 2 — Per-customer cert statement:
+ res.company.x_fc_default_cert_statement (company-level fallback).
+ res.partner.x_fc_cert_statement (per-customer override).
+ Surfaced on the partner form under the existing Cert + Document
Routing block.
+ Chronological CoC body resolves: customer override → company
default → hardcoded AS9100/ISO 9001 boilerplate. Three-tier
fallback so existing certs without overrides keep working.
Gap 3 — Chronological CoC 'Actual' column:
+ Build a captured_values_by_input dict from the move's
transition_input_value_ids (Sub 12b captures these on every
Move Parts commit).
+ Render typed Actual: text → as-is, number → with target unit,
boolean → PASS/FAIL, date → formatted, attachment → '[Attachment]'
placeholder.
+ Falls back to prompts from the destination step's step_input list
when no values were captured (still useful as audit-of-what-was-
asked even if blank).
Version bumps:
fusion_plating → 19.0.10.3.0
fusion_plating_reports → 19.0.10.1.0
fusion_plating_certificates → 19.0.5.3.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Plating → Labor History (sequence 64, between Move Log 62 and
Aerospace 65). List view colour-coded by state (info/warning/
success/muted), with billed_pct progressbar and rich field optionals.
Search filters: My Timers (default), Today, Running, Paused, Pending
Reconciliation, Reconciled. Group-by: Operator, Job, Date.
Form view (read-only header with statusbar): identity fields readonly,
billed_hrs/min/sec editable for supervisors+ until state=reconciled.
Notes group at bottom. create=false (timers are runtime-produced;
manual creation goes through the tablet flow).
ACL rows for fp.job.step.timelog already shipped in Sub 12b's CSV
(operator/supervisor/manager) — no security changes needed here.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New template: fusion_plating_reports.coc_body_chronological.
Walks fp.job.step.move records in time order (chain-of-custody).
Per-move heading 'Step Name (Tank Code)' with 'Moved By / Time / Qty'
meta line + a 5-column measurement sub-table (Name / Description /
Target / Actual / Recorded By) when the destination step has captured
inputs. Heading-only when there are no inputs (gating moves).
New router template: coc_body_router. Picks chronological vs classic
based on fp.certificate.body_style. Existing certs default to 'classic'
so no regressions. Both English + French CoC templates rerouted.
fp.certificate.body_style ('classic' | 'chronological') exposed on
the cert form alongside certified_by_id. Operator picks per cert.
Sign-off block reuses the existing owner_user_id signature pattern +
x_fc_coc_signature_override fallback. Cert statement boilerplate is
inline (Sub 12d will move it to a configurable per-customer field).
The Actual column in the measurement sub-table is rendered blank
because Sub 12a/12b runtime captures step_input values via the
operator's per-step input form which lives in a model not yet wired
into this template — Sub 12d follow-up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the minimal portrait template with the Amphenol-style paper
sheet (screens 16-18):
- Header: company logo + barcode (Code 128) + WO# + Date In + Due
Date + Type + Order# + PO# + WO-Generated-By + customer block.
- Item Information panel: Part# / Rev / Mat / Catg / S/N + Item-Name +
Qty Rec / VIS INSP / Rework / Special Requirements / Stamp-Date.
- Process-Sheet header: recipe name + category + spec/info.
- Routing table (11 cols): Step / Tank / Operation+Actuals (recipe
inputs render as 'Actual <name>: ____ unit' lines) / Instruction /
Unit / Material / Voltage / Time(min) / Temp / Stamp / Date.
Targets pulled from recipe-node fields when present (Sub 12a authored),
'N/A' otherwise. Heavily defensive QWeb — every cross-module field
access ('part_catalog_id' / 'coating_config_id' / 'qty_received' /
'special_requirements' / 'serial_number' / 'base_material' /
'customer_facing_description' / 'time_min_target' / etc.) guarded with
'X in record._fields' checks so the report renders cleanly even when
some Sub 12a/12b fields aren't yet populated.
New paperformat: A4 landscape narrow margins, 90 dpi.
Action ID + report_name unchanged so existing form-button bindings
keep working (binding_model_id still points at fp.job).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fusion_plating → 19.0.10.2.0 (Labor History views)
fusion_plating_jobs → 19.0.7.0.0 (Operator Traveller v2)
fusion_plating_reports → 19.0.10.0.0 (Chronological CoC body)
Adds data entries for the 2 new XML files (timelog views + coc
chronological).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tightened from the original 18-task plan after inspecting existing
templates:
- report_coc_en / report_coc_fr already exist with Nadcap/AS9100/CGP
logos, signature, certified_by — solid. Add a chronological body
alongside, don't rebuild.
- company.x_fc_nadcap_logo etc already exist on res.company. Skip.
- The native fp.job traveller is minimal (post-Sub-11) and needs the
paper-style upgrade. Replace its body, not the action.
- fp.job.step.timelog state machine landed in Sub 12b — Sub 12c just
ships views + menu.
5-task breakdown:
1. Bump versions + manifest scaffolding
2. Operator Traveller v2 (A4 landscape, paper-style, target columns)
3. Chronological CoC body + body_style opt-in router
4. Labor History list/form/search + Plating menu
5. Deploy to entech + smoke test
Out of scope: rack travel ticket PDF (Sub 12b's Save+Print 404 stays
flagged), per-customer cert statement (boilerplate inline for now).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Odoo 19 disallows ondelete='set null' on a required M2O. Switched to
restrict — destination steps can't be unlinked while move-log rows
reference them, which is the right audit-safety behavior anyway.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Controller: extend /fp/shopfloor/plant_overview return payload to
include 'racks' array (filtered to loaded/in_use/awaiting_unrack
states). Each entry has tag chips, part count, current node
breadcrumb, current step + tank code, and a precomputed
next_step_id (next sequence in the job's recipe — operator
overrides at runtime in the Move Rack dialog).
JS: state.racks populated from payload. New openMoveRackDialog()
method spawns FpMoveRackDialog. Notification when rack has no
successor (last step of job).
XML: top section above the existing work-centre columns. Renders
rack rows with tags, part count, breadcrumb, and primary MOVE RACK
button per row. Visible only when state.racks.length > 0.
SCSS: minimal styling for the racks pane (extends move_dialogs.scss
to keep all Sub 12b styles in one file).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ShopfloorTablet component:
- Imports the 3 new OWL dialogs.
- useService('dialog') for spawning.
- Listens for 'fp-resolve-rack' window CustomEvent fired from inside
FpMovePartsDialog → spawns FpRackPartsDialog inline.
- New methods: openMovePartsDialog(from, to) + openStopTimerDialog(id).
Refresh tablet after commit/reconcile so the UI reflects new state.
Listener cleanup on unmount.
Note: the actual buttons that call these methods are added to the
existing tablet XML in a follow-up step — for now they are wired but
not surfaced. Operators get them after Task 16 + smoke test.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3-column grid layout for field rows (label / input / hint).
Compliance prompts + Blockers blocks have their own backgrounds.
Soft blockers amber + left border, hard blockers red + left border —
matches the spec's protocol.
Token pattern + dark-mode @if branch (CLAUDE.md rule: Odoo 19 doesn't
flip dark mode via runtime DOM class; we branch at SCSS compile time
on $o-webclient-color-scheme).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move Rack: rack name in title via getter, tag chips, batches list
(read-only), Type + To Node + To Station picker. Atomic Save commits
all batches via /fp/tablet/move_rack/commit.
Stop Timer: opens with state already at 'stopped' (server flipped on
load via /labor_timer/stop), pre-fills billed_* from accrued.
Operator edits → Save (state → reconciled).
Save & Start New Timer chains into a fresh timer for the same step
via the start_new=True flag — mirrors screen 10's right-most button.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors screens 7-8. Searchable empty-rack picker with debounced
typeahead via /fp/tablet/rack/list_empty. QR Scan button prompts
operator for FP-RACK:<name> token, resolves via /fp/tablet/rack/
scan_qr.
Save commits the racking via /fp/tablet/rack_parts/commit. Save+Print
opens /web/report/pdf/fp.rack.travel/<id> in a new tab — that report
ships in Sub 12c, returns 404 until then. Plain Save works today.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors Steelhead screens 1-3, 14-15. Loads preview on mount,
re-checks hard-blockers on commit. MOVE (n) button disabled when
hard-blocked OR required prompt blank — improvement over Steelhead's
silent disabled state (we show a tooltip listing reasons).
Inline 'Resolve' button next to each blocker. For rack-required,
fires a window CustomEvent ('fp-resolve-rack') the parent tablet
catches to open the Rack Parts sub-dialog.
Typed input rendering by input_type — text/number/checkbox/select/
datetime, plus support for time_hms and signature/photo (text input
for now; full upload widget in Sub 12c).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
11 routes (consolidated from plan Tasks 8/9/10/17):
Move Parts:
/fp/tablet/move_parts/preview
/fp/tablet/move_parts/commit
Move Rack:
/fp/tablet/move_rack/preview
/fp/tablet/move_rack/commit
Rack Parts:
/fp/tablet/rack_parts/commit
/fp/tablet/rack/list_empty
/fp/tablet/rack/scan_qr
Persistent labor timer:
/fp/tablet/labor_timer/start
/fp/tablet/labor_timer/pause
/fp/tablet/labor_timer/resume
/fp/tablet/labor_timer/stop
/fp/tablet/labor_timer/reconcile
Manager-bypass context flags (Task 17 wired in here for cohesion):
fp_skip_predecessor_check → bypasses S14 lock
fp_skip_rack_assignment → bypasses requires_rack_assignment
fp_skip_transition_form → bypasses required transition prompts
All bypass uses post to chatter on the move record naming the user
+ which flags fired. Group check enforced (manager-only).
_safe() wrapper: UserError → JSONRPC-friendly {ok: False, error: msg}
so the OWL components can show a flash without crashing.
Field naming follows existing fp.job.step.timelog convention
(date_started / date_finished, NOT started_at / stopped_at).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends the existing timelog (used by S1/S2 battle tests) with:
- state: running / paused / stopped / reconciled (default running)
- last_paused_at + total_paused_seconds (drives accrued compute)
- accrued_seconds (compute, depends date_started/_finished/paused)
- billed_hrs/min/sec + billed_total_seconds + billed_pct (compute)
- product_id (split-by-product reconciliation per screen 10)
- notes
- job_id (related, indexed — for fp.job.active_timer_ids O2M)
Field naming follows the existing date_started / date_finished
convention (NOT started_at / stopped_at as my plan said — adjusted
inline to match what's already in the file).
The existing battle tests use the timelog without state — default
'running' so they're unaffected. State only flips when Sub 12b's
Stop Timer dialog commits.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fp.job.step:
+ requires_rack_assignment (related from recipe_node_id)
+ requires_transition_form (related)
+ move_ids (O2M from_step_id), incoming_move_ids (O2M to_step_id)
+ is_racked (compute, stored, depends rack_id) — drives tablet
rack-vs-parts greyed-button guard
+ qty_at_step_start, qty_at_step_finish (advanced by move commits)
NOTE: existing 'rack_id' field is reused as the 'current rack' pointer
(already there on line 95). Adding requires_rack_assignment as a
related from recipe_node_id for runtime gate evaluation.
fp.job:
+ qty_received, qty_visual_inspection_rejects, qty_rework
+ special_requirements (Text — paper traveller header)
+ active_timer_ids (filtered O2M, depends on Task 7's state field)
+ move_ids (O2M to fp.job.step.move)
All additive. No removed fields. Existing battle tests unaffected.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Plating → Move Log (sequence 62, between Logistics 60 and Aerospace 65).
Form is read-only (create=false) since moves are produced by the
tablet flow, not the desktop UI.
Search filters: Today, Scrap/Rework, Racked. Group-by: Job, Operator,
Transfer Type.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Chain-of-custody log: one row per Move Parts / Move Rack commit.
FP/MOVE/YYYY/NNNN sequence (5-digit). Carries from/to step + tank,
transfer type, qty, location, photo, rack, operator, datetime.
Child model captures recorded transition-input values (Sub 12a's
fp.step.template.transition.input snapshots → fp.job.step.move.
input.value rows). Each row carries 5 typed value columns; the
controller (Task 8) picks the right one based on input_type.
Operators get read+write+create — they generate moves at runtime —
but no unlink. Manager-only deletes for audit safety.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The existing 'state' field tracks wear (active/needs_strip/stripping/
retired). Sub 12b adds an orthogonal 'racking_state' (empty/loading/
loaded/in_use/awaiting_unrack/out_of_service) for the load lifecycle
— a rack can be wear-active AND racking-loaded simultaneously.
New fields:
racking_state — operational lifecycle
tag_ids (M2M) — fp.rack.tag chips on plant overview
capacity_count — soft warn (distinct from existing 'capacity')
current_job_step_id — compute, derived from latest fp.job.step.move
current_tank_id — compute
current_part_count — compute
Form view picks up the new fields under Sub-12b group + a 'Current
Use' panel that hides when racking_state is empty / out_of_service.
The compute references fp.job.step.move which lands in Task 4 — the
module won't load cleanly on entech until Tasks 4-5 ship; that's
expected for batch deployment at the end.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>