Per client direction: every order is a thickness RANGE (e.g.
"0.0005-0.0008 mils" or "5-10 mils"), never a single value. The
old picker model (fp.recipe.thickness with a single 'value' Float)
was modelling the wrong concept and overcrowding the order entry
UI. Replaced with one free-text Char field that auto-fills from
last-used or part default.
DELETED entirely:
- fp.recipe.thickness model (file + view + ACL + manifest entry)
- recipe.thickness_option_ids One2many (the picker source)
- "Thickness Options" inline list on the recipe form
- sale.order.line.x_fc_thickness_id (M2O picker)
- account.move.line.x_fc_thickness_id
- fp.delivery.x_fc_thickness_id
- fp.direct.order.line.thickness_id
ADDED:
- sale.order.line.x_fc_thickness_range (Char) — operator types range
- account.move.line.x_fc_thickness_range — for invoice rendering
- fp.delivery.x_fc_thickness_range — for packing slip
- fp.direct.order.line.thickness_range — for the wizard
- fp.part.catalog.x_fc_default_thickness_range — part default
AUTO-FILL CHAIN (sale.order.line + wizard line):
1. Operator already typed → keep
2. Most recent SO line for (this part, this customer) with a
non-empty thickness_range → copy that
3. part.x_fc_default_thickness_range → copy
4. Blank — operator types
Implemented as both an @api.onchange (interactive) AND a
create() override (programmatic — wizard, sale_mrp bridge,
imports). Same logic in both paths.
WIZARD push-to-defaults: when "Save as Default" toggle is ticked
on a wizard line, persist the line's thickness_range to
part.x_fc_default_thickness_range so future first-customer orders
get a sensible starting point.
REPORTS: customer_line_header.xml + report_fp_wo_sticker.xml now
print the Char range as-typed (no display_name lookup needed).
KEPT (admin documentation only — doesn't affect order entry):
- recipe.thickness_min, thickness_max, thickness_uom on the recipe
root: documents the recipe's CAPABILITY range. No UI gate; just
for spec authors to record what the chemistry can produce.
JOB GROUPING: fp.job auto-create groups SO lines by (recipe, part,
spec, thickness, serial). Updated to key on the thickness_range
Char (stripped) instead of the deleted thickness_id integer.
DB cleanup: --update=base ran on the upgrade, dropping the
fp_recipe_thickness table + the four x_fc_thickness_id columns.
Existing data was already nulled in earlier dev work.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After Phase E removed Coating Config + Treatments + Customer Price List
+ Coating Thickness from the Configurator submenu, only 3 admin items
remained — not enough to justify a top-level menu just for an
estimator.
Re-homed:
- Pricing Rules → Configuration → Pricing & Billing
(sequence 40, joins Invoice Strategy
Defaults + Account Holds)
- Materials → Configuration → Materials & Tanks
(sequence 40, joins Bath Parameters,
Replenishment Rules, Chemicals,
Rack Tags, Calibration Equipment)
- Line Description Templates → Configuration → Quality & Documents
(sequence 90, joins Notification
Templates — same "templates" pattern)
All three keep estimator visibility (group_fp_estimator) plus manager
access. Top-level menu count under "Plating" drops from 9 visible to 8.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase E removed the coating-rollup loop but left a stale `has_cost_data`
reference in the percent computation. NameError on every SO list /
form load.
Margin is "not available" until recipe-level cost data exists
(backlog item). Set all three margin fields to 0 / False explicitly
so no stale references remain.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Spec-side picker (x_fc_customer_spec_id / customer_spec_id) added on:
- sale.order.line (via quality inherit — onchange autofill, create()
fallback to part default, _prepare_invoice_line carry)
- account.move.line (via quality inherit — invoice rendering)
- fp.part.catalog (via quality inherit — x_fc_default_customer_spec_id)
- fp.direct.order.line (via quality inherit — wizard picker + autofill)
- fp.direct.order.wizard (action_create_order post-creates spec on SO line)
Thickness picker switched to fp.recipe.thickness (replaces coating-scoped):
- sale.order.line.x_fc_thickness_id comodel + domain rewired to recipe
- account.move.line + fp.delivery same
- fp.direct.order.line.thickness_id same
View inherits in quality add Specification picker next to legacy
Primary Treatment column on:
- SO form line tree
- part catalog Default Treatments block
- direct-order wizard line tree + drawer
Wizard files (fp.contract.review.client.email.wizard) pulled from
entech into the repo — they were ahead of the repo. Quality __init__
now imports wizards/.
Legacy x_fc_coating_config_id + treatment_ids remain visible during
transition; Phase E removes them.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
_clone_subtree() in fp_part_composer_controller built node vals
manually and never copied source.input_ids — so 'Load Template'
copied the recipe tree structure but dropped every custom prompt,
leaving operators with empty data-capture screens. The fix iterates
input_ids and calls .copy({'node_id': new_node.id}) so kind,
target_min/max/unit, compliance_tag, hint, selection_options,
sequence — every field on the input model — flows through.
Verified on entech: ENP-ALUM-BASIC clone now shows all 105 prompts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two compounding issues introduced by the persistence audit work:
1. fields.Text mismatch: sale.order.x_fc_internal_note and
x_fc_external_note are actually fields.Html. I declared the
sale.order.line related mirrors and the fp.job stored copies as
fields.Text, so setup_related raised:
TypeError: Type of related field
sale.order.line.x_fc_internal_note is inconsistent with
sale.order.x_fc_internal_note
Fixed by switching both Note fields on fp.job and sale.order.line
to fields.Html.
2. Module-load-order: Tier 3 fields (x_fc_delivery_method,
x_fc_ship_via, x_fc_invoice_strategy) are defined in
fusion_plating_jobs (related to sale.order via _inherit), but I
referenced them in fusion_plating core's fp_job_views.xml — which
loads BEFORE fusion_plating_jobs registers the fields. View
validator raised "Field x_fc_delivery_method does not exist".
Fixed by removing those 3 fields from the core view group and
adding them via xpath in fusion_plating_jobs's fp_job_form_inherit
(which loads after the fields are registered).
Both fixes deployed and verified — registry loads in 2s, all field
types match, related path resolves correctly. No data loss; the
fp.job rows that already had stored Text content for internal_note /
external_note will carry over into the Html field intact.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tier 3 of the SO->fp.job persistence audit. Three logistics/billing
fields surface on fp.job as related read-only (not stored) mirrors:
- x_fc_delivery_method - Local Delivery / Shipping Partner / Customer
Pickup. Cargo classification used by logistics planning.
- x_fc_ship_via - Carrier name (UPS, FedEx, customer pickup, etc.).
- x_fc_invoice_strategy - Deposit / Progress / Net Terms / COD-Prepay.
Read by the invoicing module's hooks; mirroring on the WO is for
manager visibility only.
These were intentionally chosen as related (not stored persisted)
because the SO is the authoritative source - the existing downstream
code (delivery + invoicing modules) already reads them off SO directly.
A stored copy would risk drift. Related auto-follows SO updates.
Same three fields also mirrored on sale.order.line as stored related
for per-line list visibility.
Closes the SO->fp.job persistence audit. All 10 operational fields
identified now flow through to the WO (7 stored + populated at confirm,
3 related read-only).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tier 2 of the SO->fp.job persistence audit. Four operational metadata
fields mirrored from sale.order:
- x_fc_internal_deadline (Date) - shop's internal target finish date,
ahead of the customer-facing deadline. Kept separate from
date_deadline (which scheduling code may adjust).
- x_fc_planned_start_date (Date) - customer-quoted planned start date.
Kept separate from date_planned_start (Datetime, capacity-adjusted).
- x_fc_internal_note (Text) - shop-internal notes from the order.
- x_fc_external_note (Text) - customer-facing notes, printed on
traveller / BoL / cert.
All four populate at SO confirm via _fp_auto_create_job, and surface
on sale.order.line as stored related fields for per-line visibility.
fp.job form view gets a Notes group alongside the Customer References
group from Tier 1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tier 1 of the SO->fp.job persistence audit. Three customer-reference
fields entered on sale.order's Plating tab were not flowing through
to fp.job (or SO lines), so the shop floor and printed paperwork
(traveller, BoL, cert) had to round-trip via sale_order_id every time.
Changes:
- fp.job: new x_fc_customer_job_number (Char, tracking), x_fc_po_number
(Char, tracking), x_fc_rush_order (Boolean, tracking). All three
populated by _fp_auto_create_job at SO confirm time.
- sale.order.line: x_fc_customer_job_number / x_fc_po_number added as
stored related fields off order_id so per-line list views show the
customer's references without navigating to the order header
(x_fc_rush_order was already on lines).
- fp.job form view: small Customer References group under the title
surfaces the three fields where the user expects them.
Verified end-to-end: SO -> SO line related fields -> fp.job direct
fields all carry the same value.
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>
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>
- 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>
Customer feedback: some customers don't send their PO with the
initial order — they send it days or weeks later. The system was
blocking SO confirmation without a PO, which forced the shop to
either wait on paperwork or ask a manager for a formal override.
New estimator-level path: a "PO Pending" boolean on sale.order +
an optional "PO Expected By" date.
* Confirm without a PO# / PO document when PO Pending is ticked.
* action_confirm skips the hard error if po_pending OR po_override
is set (keeps the existing manager-override path too).
* On confirm with PO Pending, the system schedules a chase
activity for po_expected_date (or +3 days if blank), assigned
via mail.activity so it shows up in the sales user's activity
list. Chatter note logged so audit is obvious.
* Direct-order wizard: po_number and po_attachment_file become
optional. Ticking "PO Pending" in the wizard is the trade-in;
a help note under the toggle explains the chase behaviour.
* Once the PO arrives, user fills in the PO# / uploads the doc,
and turns PO Pending off — existing downstream flow resumes.
Difference from x_fc_po_override (kept):
* PO Override = manager waiver, permanent ("handshake deal").
* PO Pending = estimator flag, time-boxed ("customer will send it
by Friday").
fusion_plating_configurator → 19.0.14.0.0
fusion_plating_invoicing → 19.0.3.0.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Second attempt at widening the wizard modal after the :has() CSS
approach broke layout. This time using Odoo's built-in
dialog_size mechanism — just drop 'dialog_size': 'extra-large'
into the action's context and Odoo applies its own modal-xl
class, sized for wide content. About 30% wider than the default,
which gives the 10+ line columns breathing room without any
custom CSS.
fusion_plating_configurator → 19.0.13.6.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reverting the previous wizard-width attempt. The
`.modal-dialog:has(.o_fp_direct_order_wizard)` selector combined
with the fixed width override produced broken layout (modal no
longer rendering correctly).
Better path for widening this wizard is still TBD — the built-in
maximize icon in the modal header already lets users go full-
screen as a workaround until a safe width override lands.
fusion_plating_configurator → 19.0.13.5.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User complaint: the direct-order wizard's line table was crammed —
"Primary Treatment" showed as "Primary T..." and "Serial Number"
as "Serial Nu..." at the default Odoo modal width (~992px).
Added a dedicated stylesheet that targets the wizard via a new
o_fp_direct_order_wizard class on the form element. The modal
dialog containing the wizard now opens at min(1600px, 95vw) —
wide enough for the 10+ columns (Part, Primary Treatment, Process,
Thickness, Serial Number, Qty, Unit Price, Subtotal, Part Deadline,
WO Group) to fit without truncation on typical desktop displays,
while still adapting to narrower screens.
Also nudged per-cell horizontal padding from Bootstrap's default
to 10px left/right so the columns have a bit more breathing room
inside the wider table.
fusion_plating_configurator → 19.0.13.4.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two small fixes on the direct-order wizard:
1. User reported the Process column from the previous commit didn't
appear on the line list. Root cause: module view definitions
only reload on -u, not on systemctl restart. The earlier ship
bumped the version + ran -u, but the Process column inside the
list view required a fresh view load. Re-running -u now resolves
it (no code change needed for that field).
2. The "+ Add From Prior SO" and "+ Add From Quotes" buttons were
rendering edge-to-edge because their container div used the
default Bootstrap flow (margin-right: 0). Swapped the wrapping
div class from `mb-2` to `mb-2 d-flex gap-2` so Bootstrap lays
the two buttons side-by-side with a consistent 0.5rem gap.
fusion_plating_configurator → 19.0.13.3.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User asked why the direct-order wizard only showed "Primary
Treatment" and not Process — aren't they the same? They're not,
but the distinction was invisible on the order line until now.
Mental model (preserved here to keep future decisions aligned with
the user's question):
* Primary Treatment (fp.coating.config) = "WHAT coating" (process
type, thickness range, spec reference — the contractual
deliverable).
* Process (fusion.plating.process.node tree) = "HOW we make it"
(the floor-level sequence of operations and steps that WO
generation turns into work).
Each coating carries a default process (recipe_id). Parts can
override that via the Process Composer, storing a part-scoped
clone (default_process_id on fp.part.catalog). Resolution:
part's composed process wins; coating default is the fallback.
Added a computed read-only `effective_process_id` field on
fp.direct.order.line that displays exactly what process will drive
WO generation for the line, plus a one-line `effective_process_source`
showing whether it came from the part ("customised") or the
coating ("default"). Both surfaced on the wizard list and form so
the estimator can verify before confirming the order.
No behaviour change — this is pure visibility. WO generation still
uses the same resolution chain it did before.
fusion_plating_configurator → 19.0.13.2.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two user-reported gaps:
1. The Process Recipes list was about to be flooded by part-scoped
clones as soon as parts started carrying customised processes —
thousands of clones would bury the handful of real shared
templates (General Processing, Anodize, etc.).
Fix: the main Process Recipes action now narrows to
part_catalog_id = False so shared templates stay alone in that
view. A sibling menu "Part Processes" (Plating → Operations →
Part Processes) shows the inverse list — every part-cloned
process, grouped by part — so admins can audit clones without
cluttering the templates list.
Search view gains two toggle filters (Shared Templates /
Part-Scoped) and a "Group by Part" option. The node list gains
an optional "Part" column.
Split across modules: core owns the base search / tree / action
(unchanged); configurator owns all the part_catalog_id-dependent
pieces (filter extensions, list column, narrower domain,
"Part Processes" action + menu). Keeps the dependency direction
clean — configurator always depends on core, never the other way.
2. Added a "Process" smart button to the part form's button box.
Shows either a green check (composed) or "None" (not yet
composed) and opens the part-scoped Composer on click. Gives
users one-tap access from any part form without hunting through
the Process tab.
fusion_plating → 19.0.8.0.0
fusion_plating_configurator → 19.0.13.1.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User feedback: the Process tab on fp.part.catalog was displaying a
bare template name ("General Processing"), making it impossible to
tell at a glance that the clone belonged to this specific part.
Root clone now inherits the template name with a part-identifier
suffix appended:
"General Processing — 1234567 Rev 2"
Only the ROOT gets the suffix — child nodes keep their clean source
names so the tree-editor canvas doesn't get cluttered.
Suffix logic avoids doubling "Rev": if part.revision is already
prefixed with "Rev " (e.g. "Rev 2"), we don't prepend another one.
Post-upgrade hook backfills existing part-cloned roots that
pre-date this change so users see the new format without having
to re-compose (which would otherwise wipe their customisations).
fusion_plating_configurator → 19.0.13.0.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two separate issues were stacking up on the breadcrumb trail:
1. The composer was launching the tree editor with the name
"Process Composer — …" — identical to its own label. The
breadcrumb trail ended up showing "Process Composer / Process
Composer" because both pages claimed the same name. Renamed the
tree-editor instance to "Process Editor — …" so the two pages
read distinctly.
2. Every "Back to part" click pushed a new entry onto the
breadcrumb stack instead of resetting. After one round-trip the
trail looked like "… / Composer / Editor / Part"; after two it
was "… / Composer / Editor / Part / Composer / Editor / Part".
Added clearBreadcrumbs: true to both back paths (composer's
backToPart and tree-editor's onBackToList) so a RETURN action
actually resets the stack.
fusion_plating → 19.0.6.2.0
fusion_plating_configurator → 19.0.12.4.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Icon + label now share an inline-flex row with align-items: center,
so both sit on the button's vertical midline regardless of line-
height inheritance.
- Dedicated gap (10px) replaces the me-2 utility so the _hint block's
.fa { margin-bottom: 16px } can't bleed in and push the icon off.
- Icon colour forced to #ffffff to match the button's white label —
contrast against btn-primary green is now clean.
fusion_plating_configurator → 19.0.12.3.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Icon: fa-edit → fa-sitemap (pencil suggests inline editing; the
button actually navigates to a separate tree editor — sitemap
matches the intent).
- Icon spacing: swap the fragile leading-space-in-span for the
Bootstrap me-2 utility so the icon-to-text gap is consistent.
- Label: "Open Tree Editor" → "Open Process Editor" to match the
page title ("Process Composer") and the menu vocabulary the rest
of the product uses.
fusion_plating_configurator → 19.0.12.2.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Process Composer was rendering a white card background on dark-mode
tenants because the SCSS relied on var(--bs-body-bg, #ffffff) — the
Bootstrap CSS variable doesn't flip reliably in Odoo 19's backend,
and the hardcoded fallback kicked in.
Rewrote with the shopfloor token pattern from CLAUDE.md: branch on
$o-webclient-color-scheme == dark at compile time, expose via CSS
custom properties (--fp-composer-card / -border / -panel / -text /
-muted), drop every --bs-* fallback for layout colours.
Cleared the ir_attachment asset cache on entech so both the backend
and web_dark bundles recompile cleanly.
fusion_plating_configurator → 19.0.12.1.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four new fields on every sale.order.line, propagated through to MO,
Delivery, and Invoice for end-to-end traceability:
- fp.serial registry (new model in configurator) with smart-button
traceability to Sale Order, MO, Delivery, Invoice, Part. M2O on SO
line; optional; user types a customer serial or clicks Generate
Serial for a sequence-backed one. Reverse O2M links split across
configurator (invoice) / bridge_mrp (MO) / logistics (delivery) so
module load order is respected.
- x_fc_job_number on SO line, auto-sequenced FP-JOB-NNNNN on SO
confirm. Editable — shops can override for customer/legacy schemes.
- fp.coating.thickness (new child of fp.coating.config) with per-
config discrete thickness options; x_fc_thickness_id on SO line
domain-filtered to the line's coating. Auto-clears when coating
changes.
- x_fc_revision_snapshot Char on SO line, frozen from
x_fc_part_catalog_id.revision at save. Protects historical SOs from
later catalog edits. Secondary "Revision" picker on the tree view
lets users switch between prior revisions of the same part number;
the Part M2O still surfaces only is_latest_revision rows.
Reports (CoC, packing slip, invoice, BoL) pick up all four via the
Sub 2 customer_line_header macro — one macro edit, four reports.
Smoke on entech: 11 assertions pass including revision snapshot,
generate-serial button, typed-serial create-on-fly, coating→thickness
domain reset, SO confirm auto job#, and MO traceability carry.
Module version bumps:
fusion_plating_configurator → 19.0.12.0.0
fusion_plating_bridge_mrp → 19.0.11.0.0
fusion_plating_logistics → 19.0.2.0.0 (+depends configurator)
fusion_plating_reports → 19.0.5.1.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add fp_part_composer_controller with 3 JSON-RPC endpoints:
/fp/part/composer/state, /fp/part/composer/templates,
/fp/part/composer/load_template (deep-clones a shared template
into a part-owned tree inside a cr.savepoint, sets
fp.part.catalog.default_process_id atomically)
- _clone_subtree copies name/sequence/opt_in_out/treatment_uom plus
description/notes/icon/color/timing/behaviour/work_center/process_type
and stamps part_catalog_id + cloned_from_id on every node
- Add fp_part_process_composer OWL client action (JS + XML + SCSS):
picks template from dropdown, clones, hands off to existing
fp_recipe_tree_editor via context={recipe_id, part_id}
- Add Process tab on part form with readonly default_process_id
field and Compose button calling action_open_part_composer
- Register new assets in web.assets_backend, bump configurator
version to 19.0.11.0.0
The wizard was calling so.action_confirm() immediately after creating the
sale order, which flipped it from draft to sale state and triggered the
fusion_plating_notifications hook that auto-emails the customer.
Client wants a review step: keep the SO in quotation (draft) so the
user can adjust before the customer sees anything. They manually click
Send (to email the quotation) or Confirm (to convert to sale order,
which intentionally fires the confirmation email).
Changes:
- Remove so.action_confirm() call in action_create_order
- Update docstring + inline comment to reflect manual-confirm flow
- Update the chatter message on the created SO
CLAUDE.md updated to mark Sub 1 + Sub 2 as Shipped.
Verified:
- Static check: wizard.action_create_order contains no action_confirm
- Dynamic check: SO created programmatically stays in draft
- Manual action_confirm() flow still works as designed
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When Sub 2 Task 26 flipped x_fc_internal_description to required=True,
any programmatic sale.order.line creation that doesn't set the field
fails at the Postgres NOT NULL constraint. Callers include:
- sale_mrp stock-move line creation (doesn't set name either)
- demo seeders
- external integrations
- test scripts
The UI-side onchange populates the field when the user picks a
description template; this hook mirrors that for programmatic callers.
Fallback chain: explicit vals['x_fc_internal_description'] → vals['name']
→ product_id.display_name → '—'. Matches the migration's backfill rule.
Also adds Sub 2 end-to-end smoke test (6 cases, all green):
1. Required-field rejection on part creation
2. Required-field rejection on template creation
3. Template picker populates both SO-line descriptions
4. Cert resolver: part-level override wins over partner
5. display_name renders part_number + revision + name
6. certificate_requirement defaults to 'inherit'
QC Phase 1-3 regression suite remains green after the fix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes the `description` field from `fp.sale.description.template` now
that all readers (reports, wizard, sale line) consume the new
`internal_description` + `customer_facing_description` pair.
- Model: drop `description = fields.Text(...)` declaration
- Migration 19.0.9.0.0 Step 6: `ALTER TABLE ... DROP COLUMN IF EXISTS description`
- Template form/search views: swap `description` for the two new fields
- Seed data: write new fields instead of legacy column (dupes old text into both)
- Direct-order wizard: remove `tpl.description` fallback in both onchange handlers
Entech column dropped via Odoo's auto-schema-sync during module upgrade
(migration step is for fresh installs).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Invoice PDF (portrait + landscape) now collapses SKU + Description into
a single Part column rendered via fusion_plating_reports.customer_line_header,
so customer-facing invoices print the customer's part number (with
revision) instead of the internal service SKU.
To feed the macro on invoice lines, add x_fc_part_catalog_id to
account.move.line and override sale.order.line._prepare_invoice_line so
the part reference propagates automatically when an SO is invoiced.
Adds an `internal_description` text field to the direct-order wizard
line so the shop-floor copy is captured at order entry alongside the
customer-facing text. Picking a template now fires both sides of the
onchange: `line_description` gets `customer_facing_description` (with
fallback to the legacy `description` field for backward compat) and
`internal_description` gets the template's internal text. The
auto-suggest onchange was refactored around a tiny `_apply` helper so
all three fallback paths populate both fields consistently.
The template picker is surfaced as an optional column on the wizard
list (hidden until a part is chosen, domain-scoped to that part) and
as a dedicated labeled row in the per-line form. The internal text
field is also surfaced in the form under "Line Description" so the
estimator can review / edit it before confirm. On create_order, both
`x_fc_description_template_id` and `x_fc_internal_description` are
written through to the generated sale.order.line so the audit trail
and WO printout stay linked.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Surfaces the per-part description template on the SO line list alongside
a hidden-by-default internal description column. Picking a template
fires an onchange that copies `customer_facing_description` into Odoo's
standard `name` (customer-visible) and `internal_description` into
x_fc_internal_description (shop-floor / WO only). Estimator can edit
either field after the template is applied.
The template picker's domain filters by the line's part, and the field
stays hidden until a part is chosen — avoids showing every global
template when the line is blank.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>