Three issues from user testing on entech:
1. RPC error: column fp_step_template.triggers_workflow_state_id
does not exist
Root cause: the field was declared in fusion_plating CORE, but
its target model fp.job.workflow.state lives in fusion_plating_jobs.
Odoo loads core BEFORE jobs (jobs depends on core), so when core's
field declaration runs, the comodel doesn't exist yet — and Odoo
silently skips creating the column.
Fix: moved the field to fusion_plating_jobs/models/fp_job.py via
_inherit. Now the column is added when jobs loads (after core),
and the FK target is resolvable.
2. No chatter on the Workflow State form
Added _inherit = ['mail.thread', 'mail.activity.mixin'] to
fp.job.workflow.state. Tracking enabled on name/code/sequence so
admins see who changed the milestone vocabulary. <chatter/> widget
added to the form view.
3. Form layout still showed cramped 2-col help text
The XML file on disk had my new alert-info card, but Odoo's DB
ir_ui_view still held the old arch. The -u didn't refresh it
(likely because the file's mtime didn't change between deploys).
Fix: bump version + the next deploy will run a SQL DELETE on the
ir_ui_view record so Odoo recreates it from XML on -u.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two follow-ups on the workflow state work:
1) Form layout
The "How triggers combine" help text was crammed into a 2-column
group, taking ~25% of the available width. Pulled it out of the
group and rendered as a full-width <div class="alert alert-info">
below the trigger fields. Same fix applied to Notes — uses a
<separator> + bare <field> for full sheet width.
2) Simple Recipe Editor support
The trigger field was only exposed in the Tree Editor. Added it
to the Simple Editor's inline library form too:
* fp.step.template.triggers_workflow_state_id (new Many2one) —
per-template default, snapshot-copied to recipe nodes when
dropped into a recipe (added to _SNAPSHOT_FIELDS).
* /fp/simple_recipe/workflow_states/list — new endpoint to feed
the dropdown. Soft-fails when fusion_plating_jobs isn't
installed (returns []).
* Library editor JS — _fpEnsureWorkflowStatesLoaded helper
caches the catalog on first open (create + edit paths both
warm it). Save vals carry the trigger id.
* Library editor XML — dropdown rendered after the flag
checkboxes. Hidden when the catalog is empty so the form
doesn't show a useless "— None —" pick.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The compute appended '[code]' so admin pages could disambiguate
states at a glance. But display_name is what the status-bar widget
uses to render each pill, so every pill came out as 'Received
[received]', 'In Progress [in_progress]', etc.
Removed the compute. Admin list view already shows code as a
separate column.
Earlier commit parented the new menu directly under menu_fp_config,
making it appear at the top alongside the 7 themed buckets instead
of inside one. Workflow milestones map directly to recipe-step
kinds, so 'Recipes & Steps' is the natural home.
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>
User feedback: operators kept asking why their work order said "Step 10"
for the first row. The 10-spacing was originally there to allow midpoint
inserts (insert sequence 15 between 10 and 20 without renumbering).
Tradeoff is operator confusion, and recipe authors rarely insert in the
middle anyway. Switching to 1-based contiguous sequences.
Files changed (every step-sequence allocation in the codebase):
fusion_plating_jobs/models/fp_job.py
_generate_steps_from_recipe — seq_counter starts at 1, increments by 1.
This is the path that builds fp.job.step records, so new jobs now show
Step 1, 2, 3, ... in the work order.
fusion_plating_bridge_mrp/models/mrp_production.py
Same change for the legacy MRP bridge so customers still on
mrp.production also get 1-based numbering.
fusion_plating/controllers/recipe_controller.py
- create_node: max_seq + 1
- reorder_nodes: idx + 1
- swap renumber: i (was i * 10)
- paste-import renumber: i (was i * 10)
- move_node: max_seq + 1
- _copy_subtree (recipe duplicate/import): i (was i * 10)
fusion_plating/controllers/simple_recipe_controller.py
- _sequence_for_position rewritten — always renumbers siblings to
keep them contiguous. Returns pos + 1 for the inserted node.
Old code used midpoint-with-fallback-to-renumber (10/20/30 spacing).
- step_reorder: i (was i * 10)
- library_input_add + step_add_input: existing_max + 1
What this DOESN'T do
Existing fp.job.step records keep their old sequences (10, 20, ...).
Re-confirm the SO to spawn a fresh job if you want the clean 1-based
numbering on a current test job. No data migration — we're in dev
and the user explicitly said test data is disposable.
What this DOES do
Every NEW job created from this commit forward shows Step 1, 2, 3, ...
Every NEW recipe step inserted via the simple editor / tree editor
also gets sequence 1, 2, 3, ...
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User reproduced on WH/JOB/00342: clicked Start on Incoming Inspection
while Contract Review was still in_progress. Sub 13 should have raised
UserError. It didn't. Both steps ended up in_progress.
Investigation:
$ grep "def button_start" fusion_plating_jobs/models/fp_job_step.py
88: def button_start(self): ← Sub 13 gate code
876: def button_start(self): ← Policy B + Sub 8 (older)
Two definitions of the same method in the same class. Python uses the
SECOND. My Sub 13 gate at line 88 was dead code from the moment it
landed. WH/JOB/00342's Contract Review and Incoming Inspection both
ran in_progress because the live button_start (line 876) only did
Policy B Contract Review auto-open and Sub 8 Racking auto-open — no
predecessor check.
Fix:
* Removed the duplicate button_start at line 88 (left a marker
comment so the next person doesn't redo this footgun)
* Merged the Sub 13 predecessor gate AND the receiving soft check
into the line-876 button_start so all four behaviours run from
one method:
1. Predecessor gate (raise UserError if blocking)
2. Contract Review auto-open (route to QA-005)
3. Racking auto-open (route to inspection)
4. super().button_start() + receiving check + serial promotion
Helpers _fp_should_block_predecessors / can_start / _compute_can_start
preserved (used by view + Move wizard too).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bug: action_finish_current_step (the header-level Finish & Next
button on the job form) called button_start() without capturing its
return value. So when button_start returned an action (e.g. the new
QA-005 redirect for contract_review steps from 21e42e7), the header
method threw it away and returned True. Result: operator clicked
Finish & Next, the step started, but no navigation. They had to
click again — the second click found the in_progress step, called
action_finish_and_advance, which returned the QA-005 action.
Two clicks instead of one to land on QA-005.
Fix: capture button_start's return value. If it's a dict (= an
action), return it. Otherwise return True (the normal case).
User reproduction (WH/JOB/00341):
Header > Finish & Next (1st click) → step starts + QA-005 opens
Sign / dismiss QA-005 → back to job
Header > Finish & Next (2nd click) → step finishes + next starts
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User feedback on WH/JOB/00341 (S00279 retest): clicking Start on
the Contract Review step changed state to in_progress but didn't
take them to QA-005. They had to then click Finish & Next twice
to land on the form — confusing flow.
Better UX: when an operator clicks Start on a step where
recipe_node.default_kind='contract_review', the step starts AND
the QA-005 form opens immediately. Operator signs/dismisses,
navigates back, hits Finish & Next once → step finishes + advances.
Implementation:
fp.job.step.button_start, after super() returns and the
receiving check runs, calls _fp_contract_review_redirect()
(existing helper). If it returns an action, return that
instead of the parent's result. Single-record only — bulk
button_start (job-level start-all) shouldn't navigate.
Helper logic unchanged — same gate matrix:
* recipe_node.default_kind == 'contract_review'
* job has part_catalog_id
* review state NOT in (complete, dismissed)
When review is already complete, the gate clears: button_start
returns the normal True so the operator can advance the step
without bouncing through QA-005 again.
Tests:
test_button_start_routes_cr_step_to_qa005 — start opens QA-005
test_button_start_does_not_route_when_review_complete — start
does NOT redirect once review is signed off
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Scrapped the v2/v3 form-view + list-as-cards CSS approach after
extensive failure to make Odoo's editable list look like cards.
Built a proper OWL Dialog component instead, mirroring the pattern
used by fusion_plating_shopfloor's move_parts_dialog.js.
What changed
============
* New OWL Dialog: fp_record_inputs_dialog.js
- Loads step + prompt definitions via /fp/record_inputs/load
- Renders each prompt as a semantic <div class="o_fp_ri_card">
- Per-row widget chosen by input_type:
numeric/temperature/thickness/time_seconds/ph -> number input
boolean/pass_fail -> custom CSS toggle (clearer than Bootstrap)
date -> datetime-local input
photo -> file picker w/ preview + clear
multi_point_thickness -> 5-cell grid + live average
bath_chemistry_panel -> pH/Conc/Temp/Bath grid
selection -> dropdown sourced from selection_options
text/signature/... -> text input
- Live in-range hint for numeric prompts
("in range" / "below target" / "above target")
- Save validates ad-hoc rows have a Prompt label
- Save dispatches the next_action returned by the wizard model
(e.g. action_finish_and_advance for the Finish & Next flow)
* New XML template: fp_record_inputs_dialog.xml
Full DOM control. No fighting Odoo's list view, no class-stripping
bugs from canUseFormatter, no read-mode-vs-edit-mode CSS dance.
* New SCSS: fp_record_inputs_dialog.scss
- Dark mode aware (compile-time @if $o-webclient-color-scheme==dark)
- Pure semantic selectors (.o_fp_ri_card, .o_fp_ri_input, etc.)
- 14 surface tokens with light/dark hex pairs
- Tablet polish via @media (max-width: 768px)
- Custom toggle widget (no <input type="checkbox"> hidden trick)
* New controller: controllers/record_inputs.py
- /fp/record_inputs/load: returns step + prompts payload
- /fp/record_inputs/commit: creates a wizard, populates lines,
calls action_commit (reuses existing audit-trail / synthetic
move semantics — no commit logic duplicated)
* fp_job_step.py wired to dispatch the new action
- _fp_open_input_wizard returns
{ type: 'ir.actions.client', tag: 'fp_record_inputs_dialog' }
- action_open_input_wizard same
- Contract-review redirect gate preserved (Sub 4 work intact)
* Manifest registers JS/XML/SCSS in BOTH backend + dark bundles
per the dark-mode pattern in CLAUDE.md.
What was kept
=============
* fp.job.step.input.wizard TransientModel — UNCHANGED. The new
controller's commit endpoint creates a wizard record and calls
action_commit() on it, so all the audit-trail / synthetic-move
/ chatter logic stays in Python where it belongs.
* v2 + v3 form views still exist in the XML file. If the OWL
dialog ever fails, switch action_open_input_wizard back to
ir.actions.act_window with view_id=v2 or v3.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>
- fusion_plating: tank field labels (Code → Tank Number, Tank → Tank Name)
+ state-control header buttons (Mark Empty/Filled/In Use/Draining/
Maintenance/Out of Service) with chatter audit logging.
- fusion_plating_configurator: Plating app default landing screen = Sale
Orders, while keeping menu name as 'Plating'.
- fusion_plating_jobs: SO smart-button label 'Plating Jobs' → 'WO'.
Already deployed and verified on entech earlier in the session.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The original mrp.production / mrp.workorder sticker (logo + WO# stack
on the left, big QR on the right, 7-row body with PO/Customer/Process/
Part Number/Due/Qty/Notes — the design ENTECH has been printing for
months) lives in fusion_plating_reports.report_fp_wo_sticker_inner.
The new fp.job sticker had been rebuilt from scratch with a different
look. This wires fp.job into the existing canonical template instead.
What changed:
- report_fp_wo_sticker_inner — every t-set now uses the
"_var or fallback-from-_mo" pattern so callers can pre-resolve
values; mrp.production/mrp.workorder callers still work via the
fallback path.
- report_fp_wo_sticker_defaults — new shared template that initialises
every overridable name to False so the inner's `or` chain doesn't
NameError when an outer hasn't set it.
- report_fp_job_sticker_template — replaces the parallel layout with
a t-call to report_fp_wo_sticker_inner, feeding it from fp.job
fields (name, partner_id, qty, date_deadline, sale_order_id,
sale_order_line_ids, recipe_id, part_catalog_id, coating_config_id).
- report_fp_so_sticker — new outer that iterates sale.order.order_line
and emits one sticker per line that has a part_catalog_id. Bound to
sale.order's print menu via action_report_fp_so_sticker.
Versions: reports 19.0.7.14.0 -> 19.0.7.15.0,
jobs 19.0.5.0.0 -> 19.0.5.1.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three connected operator-workflow features for entech.
A. fp.job smart buttons — count fields and action methods for sale
order, steps, deliveries, invoices, payments, quality holds,
certificates, time logs, and portal job. Each is an oe_stat_button
that drills into the matching records, mirroring the sale.order
pattern. Cross-module models are runtime-detected so the form
stays clean when bridge modules are uninstalled.
B. Reusable QR scanner OWL component (`<QrScanner/>`) wired into the
Manager Desk, Tablet Station, Plant Overview, and Process Tree
headers. Click → modal with rear-camera stream (getUserMedia) +
BarcodeDetector live decode → opens the matching fp.job form via
the action service. Falls back to a manual URL paste box on
browsers without BarcodeDetector. Works on iOS 17+ Safari and
Android Chrome. Width uses `min(420px, 92vw)` wrapped in #{} so
dart-sass passes it through verbatim instead of trying to compute
incompatible units at compile time.
C. /fp/tank/<id> public-but-auth-required tank status page for NFC
taps. Renders the tank's current step (in-progress / paused),
queued ready steps, and most recent bath chemistry log (lines
table) on a mobile-first page. URL-based so it works on iOS Safari
without the Web NFC API — the operator taps the NFC tag, the URL
opens in the default browser, the page auto-renders. New
web.assets_frontend bundle entry pulls in the design tokens +
tank_status.scss.
Manifest version bumps: jobs 19.0.5.0.0, shopfloor 19.0.16.0.0.
Tests: 44 pass (3 new smart-button assertions added).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move 'All Jobs' and 'Steps' under fusion_plating_shopfloor's
existing 'Shop Floor' submenu (between Tablet Station and Bake
Windows). The standalone 'Jobs' parent menu is gone — it was
redundant once the operator UIs (Plant Overview / Tablet /
Manager Desk) consolidated under Shop Floor.
Final structure under Plating:
Sales / *
Configurator / *
Shop Floor
Manager Desk
Plant Overview
Tablet Station
All Jobs ← moved here (seq 15)
Steps ← moved here (seq 17, supervisor+)
Bake Windows
First-Piece Gates
Receiving & Inspection
Operations / *
Configuration → Work Centres (manager)
...rest
Work Centres stays under Configuration (no shopfloor equivalent).
New menu file lives in fusion_plating_jobs/views (jobs depends on
shopfloor; core can't reference shopfloor xmlids).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes the parallel OWL/controller stack I built in
fusion_plating_jobs (job_process_tree, job_plant_overview,
job_manager_dashboard, job_tablet, job_*.scss, plus parallel
controllers and action XML files). Refactors the existing
fusion_plating_shopfloor components in place to bind to
fp.job / fp.job.step instead of mrp.production / mrp.workorder.
End state:
- ONE operator UI module (shopfloor) instead of two parallel ones
- Existing token system (_fp_shopfloor_tokens.scss) reused as
designed - no duplicate jobs tokens
- Existing /fp/shopfloor/* RPC URLs preserved (no integration
breakage); workorder_id kwargs accepted as legacy aliases for
step_id / job_id so older tablet clients keep working
- Existing visual designs preserved - only the data layer
underneath changed
- Process Tree button on fp.job form now points at
fusion_plating_shopfloor's fp_process_tree client action
- Bake Windows / First-Piece Gates / Bake Oven / Operator Queue
models stay where they were
- legacy_menu_hide.xml trimmed: only the bridge_mrp Production
Priorities entry remains; the 3 shopfloor menus (Manager Desk,
Plant Overview, Tablet Station) are now visible (the canonical
native consoles)
Manifests:
- fusion_plating_jobs 19.0.3.1.0 -> 19.0.4.0.0 (consolidation bump,
no more bundled JS/SCSS, only job_scan controller retained)
- fusion_plating_shopfloor 19.0.14.4.0 -> 19.0.15.0.0 (asset bundle
cache-bust + significant controller refactor)
Tests pass on entech: 0 failed, 0 errors of 41 tests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The 4 client-action SCSS files I shipped in Phase 6 ignored the
project's documented design system (CLAUDE.md "Card Styling" + "Dark
Mode" rules) and used hardcoded hex / var(--bs-*) for surfaces.
Result: dark mode rendered white-text-on-white-card.
Companion to "changes" (22573e7) which already landed
_fp_jobs_tokens.scss + the job_plant_overview.scss refactor.
This commit finishes the job:
- Refactored job_process_tree.scss, job_manager_dashboard.scss and
job_tablet.scss to reference the $fp-* tokens — zero hardcoded
hex on theme-sensitive surfaces. Three-layer contrast applied per
CLAUDE.md (page → container → card).
- Process tree keeps the intentional Steelhead-style dark-slate
card fill in BOTH bundles (deliberate visual choice, not a theme
bug); page / header / connectors / empty state are now token-
driven so they look right against light or dark page surfaces.
- Manifest assets list reordered so _fp_jobs_tokens.scss compiles
first in web.assets_backend (CLAUDE.md rule: SCSS variables in
earlier files are visible to later files in the same bundle).
This is what makes the compile-time
$o-webclient-color-scheme branch in the partial actually take
effect for the four consumer files.
Verified on entech: light bundle (web.assets_backend) and dark
bundle (web.assets_web_dark) both compile without SCSS errors and
emit distinct surface hexes (light: #f3f4f6 page / #ffffff card;
dark: #1a1d21 page / #22262d card).
Manifest 19.0.3.0.0 → 19.0.3.1.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three batched changes that close out the original 10-phase
migration plan.
1. Phase 5 — Job Margin report bound to fp.job (replaces the
mrp.production-bound report_wo_margin). Per-step labour cost
table + margin summary using existing fp.job.step.cost_total
from Phase 1.
2. Polish:
- Real implementations for fp.job.step.button_pause,
button_skip, button_cancel (was NotImplementedError stubs).
button_pause closes the open timelog and sums duration_actual,
mirroring button_finish; button_skip/cancel transition state
with UserError guards.
- Explicit ondelete= policies on fp.job's cross-module Many2ones
(part_catalog/coating restrict, customer_spec/portal/delivery
set null) — was implicit set null.
- Standard Nexa Systems author/website/maintainer/support block
on fusion_plating_jobs manifest, suppressing the install
warning.
3. Legacy hide:
- New 'Plating Legacy Menus' group (group_fusion_plating_legacy_menus)
— nobody in it by default.
- Old shopfloor Manager Desk + Plant Overview + Tablet Station
menus restricted to that group, hiding them from operators
now that the native equivalents under 'Plating Jobs (Native)'
exist. (Note: ir.ui.menu uses group_ids in Odoo 19, not the
deprecated groups_id alias.)
Manifest 19.0.2.4.0 → 19.0.3.0.0. fusion_plating_shopfloor added
to depends so the legacy menu xmlid references resolve at install
time.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Operator-facing touchscreen UI. Three modes:
- job_picker: list of active jobs as big touch cards
- job_detail: job header + steps list, click a step to view detail
- step_detail: big Start/Finish buttons depending on state
Backend: 4 JSON-RPC endpoints under /fp/jobs/tablet/* for jobs
list, job detail, start step, finish step. Calls through to
fp.job.step.button_start / button_finish so all the audit
preservation, timelog creation, duration_actual roll-up logic
from Phase 1 still applies.
Menu entry 'Tablet Station (Native)' at sequence 3 (top) of the
Plating Jobs (Native) submenu inside the existing Plating app.
Manifest 19.0.2.3.0 → 19.0.2.4.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two operator-facing client actions for the native job model.
Plant Overview: kanban with columns = fp.work.centre, cards = active
fp.job.step rows (ready/in_progress/paused). Drag a card to a different
column to reassign the step's work_centre_id; click to open the step
form. Backend: /fp/jobs/plant_overview returns columns with cards;
/fp/jobs/plant_overview/move_card reassigns work_centre.
Manager Dashboard: list of in-flight fp.job rows with progress bars,
deadline (overdue highlight), current_step / current_location, and a
priority side-bar (rush=red, high=orange, normal=blue, low=grey). Click
a row to open the job form. State-count pills filter by state. Backend:
/fp/jobs/manager_dashboard returns rows + state counts.
Both menu entries land inside the existing 'Plating Jobs (Native)'
submenu under the Plating app (manager-only). The menu items are
defined in this module rather than in fusion_plating core, because
the action xmlids they reference aren't loaded yet at the time the
core menu file is parsed (fusion_plating_jobs depends on core, not
the other way round).
Manifest 19.0.2.2.0 → 19.0.2.3.0. Three new SCSS, three new JS,
three new XML files registered in web.assets_backend.
Verified on entech: module loaded clean, all 41 fusion_plating_jobs
tests pass, asset bundle regenerates without errors, both menus and
both client actions registered in ir_ui_menu / ir_act_client.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ports fusion_plating_shopfloor's process_tree.js to bind to fp.job
instead of mrp.production. Consumes the /fp/jobs/process_tree
JSON endpoint built in Phase 6 lean.
Renders the recipe tree as cards. Each operation card shows the
step state (pending/ready/in_progress/done/etc.) when there's a
matching fp.job.step. Click an operation card -> open the step
form. Click Back -> return to the job form.
New 'Process Tree' button on the fp.job form (manager-only)
launches the client action with job_id context.
Manifest 19.0.2.1.0 -> 19.0.2.2.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Migration script now sets context fp_jobs_migration=True before
creating fp.job records. action_confirm and button_mark_done check
this flag and skip side-effects (portal job creation, QC check,
racking inspection, delivery, certificate, notification dispatch)
when migrating.
Without this, the migration would double-create portal jobs / QC
checks / racking inspections — once via bridge_mrp's original
create on the source MO, once via jobs module's lifecycle hook on
the new fp.job mirror. With the gate, the migration script
explicitly rebinds the existing dependents via x_fc_job_id.
Manifest 19.0.2.0.0 → 19.0.2.1.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds legacy_mrp_production_id (Integer index) on fp.job and
legacy_mrp_workorder_id on fp.job.step. Used as the idempotency
key during cutover migration.
Three scripts under fusion_plating_jobs/scripts/:
- audit_pre_migration.py — counts and data-quality concerns BEFORE
- migrate_to_fp_jobs.py — copies MO->fp.job, WO->fp.job.step, time
logs, rebinds cross-refs (batches,
holds, certs, readings, portals,
inspections, deliveries). Idempotent.
- audit_post_migration.py — counts and verifies AFTER
Migration is run manually from \`odoo shell\` at cutover (not as
auto post-migration hook, for safety). README explains usage.
Tests verify the legacy id fields exist and the migration script
files are well-formed Python.
Manifest 19.0.1.9.0 -> 19.0.2.0.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 6 originally scoped the full operator UI rewrite (Plant
Overview, Tablet, Manager Dashboard, Process Tree). Tailscale SSH
to entech is currently unavailable, so live in-browser
verification of OWL/JS components isn't possible. Shipping a lean
Phase 6 with the data-layer pieces:
1. /fp/job/<id> scan controller — when a user scans a fp.job
sticker, lands them on the fp.job form (or the process tree
action once that's wired). Mirrors fusion_plating_reports' /fp/wo/
pattern.
2. /fp/jobs/process_tree JSON endpoint — returns the recipe tree
serialized with each node tagged by its fp.job.step state,
ready for an OWL component to render. The component itself is
deferred (see README.md).
The bigger UI deferrals (kanban, tablet, manager dashboard) are
documented in README.md. They get their own focused project after
cutover — the data layer is complete, so they can land
incrementally without touching fp.job/fp.job.step.
Tests verify controller imports + serialization shape (no HTTP
because TransactionCase doesn't easily simulate request context).
Manifest 19.0.1.8.0 → 19.0.1.9.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two parallel report definitions for the native job model:
1. Job Sticker (6x4 inch custom paperformat) bound to fp.job. Prints
WH/JOB/... ID, customer, SO, qty, due date, recipe, step
progress. QR encodes /fp/job/<id> for scan-to-job navigation.
2. Job Traveller bound to fp.job, A4 portrait. Job header + all
fp.job.step rows in sequence order with operator sign-off
column.
Coexists with fusion_plating_reports' MO/WO bindings — both print
menus stay live during migration.
Deferred reports (use existing during migration; rebind at cutover):
- BoL, Packing Slip, Invoice (read from SO, no fp.job change needed)
- WO Margin (cost rollup; rebuild against fp.job.step.cost_total
in phase-end polish)
Adds fusion_plating_reports to fusion_plating_jobs depends.
Tests deferred to post-Tailscale-restore: 3 new tests verify
report actions are registered + sticker template renders without
QWeb errors. Module file content verified locally as
well-formed XML.
Manifest 19.0.1.7.0 → 19.0.1.8.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Adds 'job_confirmed' and 'job_complete' trigger events to
fp.notification.template (legacy 'mo_confirmed' / 'mo_complete'
stay for bridge_mrp).
- fp.job.action_confirm and button_mark_done now fire those
notifications best-effort via fp.notification.template._dispatch
(silent skip if templates absent or notifications module missing).
- Adds x_fc_source ['mrp', 'jobs'] tag to fusion.plating.kpi.value
so Phase 9 dashboards can filter or display both sources.
- Verified aerospace/nuclear/cgp/safety modules don't directly
reference mrp.production or mrp.workorder.
Configurator integration was already covered by Task 2.5's SO
confirm hook (reads x_fc_part_catalog_id and x_fc_coating_config_id
from sale.order.line).
Manifest 19.0.1.6.0 -> 19.0.1.7.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds x_fc_job_id / x_fc_step_id Many2ones via _inherit on:
- fusion.plating.batch (workorder_id stays for legacy MRP-bound batches)
- fusion.plating.quality.hold
- fp.certificate
- fp.thickness.reading
- fusion.plating.delivery (parallel to existing job_ref Char)
- fp.racking.inspection (parallel to existing production_id)
fp.job.action_confirm now also calls a best-effort racking-inspection
auto-create. The current fp.racking.inspection still has a required
production_id, so the helper skips cleanly when this job has no MO link
(pure-native mode). Phase 9 cutover flips the required FK to fp.job.
Strategy: parallel coexistence — bridge_mrp's existing fields stay
populated; this adds NEW fields populated by the native flow. Phase 9
cutover stops populating the old fields.
Adds fusion_plating_batch + fusion_plating_receiving to jobs module
depends.
Note: spec referenced fp.batch / fp.quality.hold; the actual models
in this codebase are fusion.plating.batch / fusion.plating.quality.hold
— used the real model names.
Manifest 19.0.1.5.0 → 19.0.1.6.0. 29 jobs tests pass.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Task 2.6: fp.job.action_confirm auto-creates fusion.plating.portal.job
with x_fc_job_id back-reference. Idempotent (skip if already linked).
- Task 2.7: fp.job.action_confirm checks customer.x_fc_requires_qc
and best-effort creates a fusion.plating.quality.check (the model
lives in bridge_mrp; runtime-detected to avoid dep cycle).
- Task 2.8: fp.job.button_mark_done sets state='done', date_finished,
auto-creates draft fusion.plating.delivery and best-effort triggers
fp.certificate generation.
- Task 2.9: account.move.action_post links invoice -> fp.job via SO
origin lookup, updates portal_job state to complete and stamps
invoice_ref.
5 new tests cover: portal job creation + idempotency, mark_done state
+ delivery, cancel-then-mark-done blocked.
Best-effort patterns (try/except + runtime model detection) used for
QC + cert because their target models are in dependent modules
that this module doesn't depend on by design.
qc_check_id field on fp.job still deferred — adding it here would
require depending on bridge_mrp.
Manifest 19.0.1.4.0 -> 19.0.1.5.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Settings flag controls which SO confirm path runs. Default False
keeps the legacy bridge_mrp / mrp.production flow on entech.
Setting True diverts confirm into fp.job creation.
Both hooks coexist — bridge_mrp's _fp_auto_create_mo and the new
_fp_auto_create_job — but only one creates records per SO confirm
(controlled by the flag).
The new _fp_auto_create_job mirrors bridge_mrp's grouping logic
(x_fc_wo_group_tag), recipe resolution (coating → part), and
traceability fields (origin, sale_order_line_ids).
Settings UI shows the flag in a 'Fusion Plating Jobs' app section
of the standard Configuration menu.
3 new tests cover: flag off no-op, flag on creates job, idempotency.
Manifest 19.0.1.3.0 → 19.0.1.4.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Native port of fusion_plating_bridge_mrp's
_generate_workorders_from_recipe method. Walks the recipe tree,
creates one fp.job.step per 'operation' node, formats child 'step'
nodes as step instructions on chatter, respects opt-in/out
overrides from fp.job.node.override.
Adaptations from the original:
- Creates fp.job.step (not mrp.workorder)
- Maps fusion.plating.work.center to fp.work.centre via forward
link (x_fc_fp_work_centre_id) or code fallback
- Uses native field names (job_id, work_centre_id, etc.)
- Drops work_role_id (not on fp.job.step yet — Task 2.6+)
- Drops _fp_autofill_default_equipment (not yet on step)
5 new tests cover: basic generation, idempotency, no-recipe skip,
opt-in override behaviour, recipe_node_id link.
Manifest 19.0.1.2.0 → 19.0.1.3.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirror of fusion.plating.job.node.override from bridge_mrp, but
bound to fp.job. bridge_mrp's version stays alive for legacy MO
flow during the migration. Both coexist.
Adds override_ids One2many to fp.job via _inherit, plus
unique(job_id, node_id) constraint.
Note: spec-suggested _sql_constraints syntax is deprecated in
Odoo 19 ("Model attribute '_sql_constraints' is no longer
supported, please define model.Constraint on the model"). Used
the new class-attribute form: _unique_job_node = models.Constraint(...).
Verified the UNIQUE index is created on the table.
3 new tests: create, uniqueness, one2many backref.
Manifest 19.0.1.1.1 -> 19.0.1.2.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code review noted this was asymmetric — only part_catalog_id had
explicit index=True among the 5 new fields, and Phase 1 core fp.job
relies on Odoo's implicit FK btree for ALL Many2ones (no explicit
indexes). Removed for consistency.
Important I2 (no explicit ondelete= policies) is deferred to a
phase-end polish task that addresses both Phase 1 core and Phase 2
extension fields uniformly.
Manifest 19.0.1.1.0 → 19.0.1.1.1.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 of the 6 deferred fields from Phase 1 Task 1.4 land here in
fusion_plating_jobs:
- part_catalog_id (fp.part.catalog from configurator)
- coating_config_id (fp.coating.config from configurator)
- customer_spec_id (fusion.plating.customer.spec from quality)
- portal_job_id (fusion.plating.portal.job from portal)
- delivery_id (fusion.plating.delivery from logistics)
qc_check_id deferred to Task 2.7 — its target model
(fusion.plating.quality.check) still lives in
fusion_plating_bridge_mrp and we don't depend on bridge_mrp from
this module. Task 2.7 will address QC sourcing.
6 unit tests (5 field-presence + 1 integration creating linked
records).
Manifest 19.0.1.0.0 → 19.0.1.1.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>