52 KiB
Fusion Plating — Claude Code Instructions
Project
Fusion Plating is a multi-module Odoo 19 ERP for electroless nickel plating and metal finishing shops. Built by Nexa Systems for EN Technologies (the client). Replaces Steelhead Software.
Module Structure (30 modules)
fusion_plating/ — Core: facilities, process types, tanks, baths, chemistry, recipes
fusion_plating_batch/ — Rack/barrel batch tracking (FpBatch, FpBatchChemistry)
fusion_plating_kpi/ — KPI definitions, daily auto-compute, dashboard views
fusion_plating_configurator/ — Quotation configurator, pricing engine, part catalog, 3D viewer
fusion_plating_receiving/ — Parts receiving, inspection, damage logging
fusion_plating_invoicing/ — Invoice strategies (deposit/progress/net/COD), account holds
fusion_plating_certificates/ — Certificate registry (CoC, thickness reports), Fischerscope data
fusion_plating_notifications/ — Auto-email engine, notification templates, audit log
fusion_plating_shopfloor/ — Tablet UI, plant overview kanban, process tree visualization
fusion_plating_portal/ — Customer portal + self-service configurator wizard
fusion_plating_reports/ — PDF reports (WO margin, discharge sample, CoC, etc.)
fusion_plating_compliance/ — Compliance framework, jurisdictions
fusion_plating_compliance_on/ — Ontario compliance reference data (data-only, no menus)
fusion_plating_compliance_tor/ — Toronto bylaw discharge limits (data-only, no menus)
fusion_plating_aerospace/ — AS9100 / Nadcap
fusion_plating_nuclear/ — CSA N299 / CNSC
fusion_plating_cgp/ — Controlled Goods Program
fusion_plating_safety/ — SDS, WHMIS, JHSC
fusion_plating_quality/ — QMS (NCR, CAPA, calibration)
fusion_plating_logistics/ — Pickup & delivery, chain of custody
fusion_plating_culture/ — Values / fundamentals (⚠️ RETIRED — do NOT auto-install)
fusion_plating_bridge_mrp/ — MRP integration (recipe→WO, portal job, work order priorities)
fusion_plating_bridge_sign/ — Digital signatures
fusion_plating_bridge_quality/ — Quality bridge
fusion_plating_bridge_documents/ — Odoo Documents integration (NCR, CAPA, FAIR, Doc Control)
fusion_plating_process_en/ — Electroless nickel process pack
fusion_plating_process_chrome/ — Chrome process pack
fusion_plating_process_anodize/ — Anodizing process pack
fusion_plating_process_black_oxide/ — Black oxide process pack
fusion_tasks/ — Local delivery dispatch (GPS, maps, driver scheduling)
Menu Structure (Plating App)
The Plating app (menu_fp_root, seq 46) has these top-level menus:
| Seq | Menu | Module | Children |
|---|---|---|---|
| 3 | KPIs | fusion_plating_kpi | KPIs, KPI History, Production/Quality/Finance dashboards |
| 5 | Sales | fusion_plating_configurator + portal | Quotations, Sale Orders, Customers, Part Catalog, Quote Requests, Portal Jobs |
| 8 | Configurator | fusion_plating_configurator | New Quote, Coating Configs, Pricing Rules, Treatments |
| 12 | Shop Floor | fusion_plating_shopfloor | Plant Overview, Tablet Station, Bake Windows, First-Piece Gates |
| 15 | Receiving | fusion_plating_receiving | All Receiving, Pending Inspection, Discrepancies |
| 18 | Operations | fusion_plating (core) | Process Recipes, Production Priorities (bridge_mrp), Batches (batch), Baths, Chemistry Logs, Tanks |
| 25 | Certificates | fusion_plating_certificates | All, CoC, Thickness Reports |
| 30 | Quality | fusion_plating_quality | Holds, NCRs, CAPAs, FAIR, Audits, Doc Control |
| 40 | Compliance | fusion_plating_compliance | Permits, Discharge, Waste, Calendar, Spills, Config |
| 45 | Safety | fusion_plating_safety | SDS, Training, Exposure, JHSC, Incidents, PPE |
| 50 | Logistics | fusion_plating_logistics + fusion_tasks | Pickups, Deliveries, Routes, CoC, POD, Field Tasks, Task Map, Task Calendar |
| 60 | Aerospace | fusion_plating_aerospace | AS9100, Nadcap, Counterfeit, Config Items, Risk |
| 65 | Nuclear | fusion_plating_nuclear | Program, ITP, 10CFR21, Pedigree, CNSC |
| 70 | CGP | fusion_plating_cgp | Registration, AI, PSA, Visitors, Goods, Shipments, Security, Access Log |
| 90 | Configuration | fusion_plating (core) + many | Facilities, Work Centres, Process Categories/Types, Bath Params, Stations, Ovens, Invoice Strategy, Account Holds, Training Types, Chemicals, Notification Templates/Log, Calibration, Specs, AVL, Value Sets/Rotations, N299 Levels, Vehicles |
Field Service (fusion_tasks) also has its own standalone root app (seq 45) with Map View, Tasks, Calendar, Configuration. The same task actions are also accessible under Plating > Logistics.
Key rule: Sales menu is unified in fusion_plating_configurator. Portal module adds Quote Requests + Portal Jobs as children (referencing fusion_plating_configurator.menu_fp_sales). Do NOT create a separate Sales menu in portal.
Retired / Do-Not-Install Modules
These modules have source code in this repo but are intentionally NOT installed on entech (the client's live Odoo). Do not:
- Include them in any
-ior-usequence that installs modules automatically. - Add them as a
dependstarget from any other Fusion Plating module. - Re-sync their folders to
/mnt/extra-addons/custom/on entech. - Recommend installing them to the user without explicit confirmation.
| Module | State on entech | Retired because | What to do if revisiting |
|---|---|---|---|
fusion_plating_culture |
state=uninstalled, dir removed from entech disk |
Soft people-ops feature (peer kudos / "Fundamental of the Week"); zero data entered; not a client priority. Top-level "Culture" menu confused operators. | Ask the client whether they want it before reinstalling. If yes: re-sync folder + -i fusion_plating_culture + seed a value set. |
fusion_plating_sensors |
deleted entirely (not in repo anymore) | Duplicated fusion_plating_iot's scope but with no working alerting logic. Its valuables (sensor_type taxonomy, dashboard, location flexibility) were ported into fusion_iot/fusion_plating_iot/. |
N/A — gone. Any new sensor work goes in fusion_iot/fusion_plating_iot/. |
Critical Rules — Odoo 19
- NEVER code from memory — Read reference files from the server first.
- Backend OWL:
static template,static props = ["*"], standalonerpc()from@web/core/network/rpc. NOTuseService("rpc"). - HTTP routes:
type="jsonrpc"— NOTtype="json"(deprecated in Odoo 19). - Search views: NO
group expand="0", NOstringattribute on<search>, NO<group string="...">wrapper for group-by filters. Use bare<group>for group-by. - res.config.settings: Only boolean/integer/float/char/selection/many2one/datetime. NO Date fields.
- res.groups: Use
privilege_id(NOTcategory_id).user_idsis OK but the deprecatedusersalias is NOT. Always includesequencefield. - Field params:
parent_pathdoes NOT acceptunaccentparameter in Odoo 19. - SCSS borders: Use
$border-color(SCSS variable) for card borders, NOTcolor-mix()in border shorthand — the SCSS compiler drops it.color-mix()works fine inbackground-color,box-shadow, etc. - Theme awareness: All colours must use CSS custom properties (
var(--bs-body-bg),var(--bs-body-color),var(--bs-border-color),var(--bs-secondary-color),var(--o-action)). NO hardcoded hex. Seefusion_plating_shopfloor.scssas the gold standard. - XML comments: No double-hyphens (
--) inside<!-- -->comments — invalid XML, causes lxml parse error. - XML data ordering: Window actions must be defined BEFORE
<menuitem>elements that reference them in the same file. - Module install on new modules: Use
--update=basealongside-i MODULEto ensure Odoo rescans the addons path and finds the new module directory. - Implied group cascade:
implied_idsonres.groupsdoes NOT reliably propagate to users on module install. Always includeuser_idsto explicitly assign admin, or fix via SQL post-install.
Naming
- New custom models (post-2026-04):
fp.*prefix (e.g.fp.part.catalog,fp.certificate) - Existing custom models: Keep
fusion.plating.*(e.g.fusion.plating.portal.job,fusion.plating.delivery) - New fields on standard Odoo models:
x_fc_*prefix - Legacy fields:
x_studio_* - Canadian English for all user-facing text
- SCSS class prefix:
o_fp_*(shopfloor:o_fp_po_*,o_fp_pt_*; recipes:o_fp_recipe_*) - Monetary fields: always pair with
currency_idfield on the same model
Process Recipe System (NEW — v19.0.2.x)
Model: fusion.plating.process.node (in fusion_plating core)
- Hierarchical tree with
_parent_store = True - Node types:
recipe,sub_process,operation,step - Companion model:
fusion.plating.process.node.input(operator inputs) iconis a Selection field (24 curated plating icons), NOT a Char- Auto-icon: JS
guessIcon(name)maps keywords → icons when adding nodes - OWL tree editor: registered as
fp_recipe_tree_editorclient action - Controller:
fusion_plating/controllers/recipe_controller.py(7 endpoints) - SCSS:
fusion_plating/static/src/scss/recipe_tree_editor.scss
Recipe Endpoints
POST /fp/recipe/tree — full nested tree for OWL editor
POST /fp/recipe/node/create — add child node
POST /fp/recipe/node/write — update fields
POST /fp/recipe/node/unlink — delete + cascade
POST /fp/recipe/node/reorder — bulk sequence update
POST /fp/recipe/node/move — change parent_id
POST /fp/recipe/duplicate — deep-copy recipe
Steelhead Features Status
| Feature | Status |
|---|---|
| Hierarchical process tree | Done |
| Node types (recipe/sub/op/step) | Done |
| Auto-complete flag | Done |
| Customer visible flag | Done |
| Manual/automated flag | Done |
| Requires sign-off | Done |
| Opt In/Out (disabled/opt-in/opt-out) | Done |
| Icon picker | Done |
| Time tracking (created/updated with seconds) | Done |
| Operator inputs | Done |
| Description (rich text) | Done |
| File attachments (via mail.thread) | Done |
| OWL tree editor with drag-drop | Done |
| Tags | Not yet |
| Dashboard Transitions | Not yet |
| Treatment Groups / Choices | Not yet |
| Go To Node Options | Not yet |
| Spec Fields | Not yet |
Client Recipes Created
ENP-ALUM-BASIC— Electroless Nickel Plating Aluminium Basic (9 operations, 15 steps). Data file:fusion_plating/data/fp_recipe_enp_alum_basic.xml
Plant Overview Dashboard
- OWL client action:
fp_plant_overviewinfusion_plating_shopfloor - Kanban columns = work centres, cards = active
mrp.workorderrecords - Drag & drop between columns (writes
workcenter_idon the work order) - Endpoint:
POST /fp/shopfloor/plant_overview - Move endpoint:
POST /fp/shopfloor/plant_overview/move_card - Auto-refreshes every 30s
Deployment
odoo-entech (LXC 111 on pve-worker5)
- Type: Native Odoo (apt package, NOT Docker)
- IP: 10.200.1.26
- DB:
admin(PostgreSQL local, userodoo) - Config:
/etc/odoo/odoo.conf - Addons:
/mnt/extra-addons/custom/(fusion_plating modules live here) - Service:
systemctl {start|stop|restart} odoo - Update command:
ssh pve-worker5 "pct exec 111 -- bash -c 'systemctl stop odoo && su - odoo -s /bin/bash -c \"/usr/bin/odoo -c /etc/odoo/odoo.conf -d admin -u MODULE_NAME --stop-after-init\" && systemctl start odoo'" - Copy files:
cat LOCAL_FILE | ssh pve-worker5 "pct exec 111 -- bash -c 'cat > /mnt/extra-addons/custom/REMOTE_PATH'" - IMPORTANT: Must pass
-c /etc/odoo/odoo.confor Odoo won't find the repackaged enterprise addons
odoo-trial (VM 316 on pve-worker1)
- Type: Docker (container
odoo-trial-app, dbodoo-trial-db) - DB:
trial(userodoo) - Host addons path:
/opt/odoo/custom-addons/→ mounts as/mnt/extra-addons/in Docker - Docker network:
odoo_odoo-network - Copy files (base64 pipe through qm guest exec):
B64=$(base64 -w0 "LOCAL_FILE") ssh pve-worker1 "qm guest exec 316 -- bash -c 'echo $B64 | base64 -d > /opt/odoo/custom-addons/REMOTE_PATH'" - Clear asset cache (required after SCSS/JS changes):
ssh pve-worker1 "qm guest exec 316 -- bash -c \"docker exec odoo-trial-db psql -U odoo -d trial -c \\\"DELETE FROM ir_attachment WHERE url LIKE '%/web/assets/%';\\\"\"" - Update command:
ssh pve-worker1 "qm guest exec 316 -- bash -c 'docker stop odoo-trial-app && docker run --rm --network odoo_odoo-network -v odoo_odoo-data:/var/lib/odoo -v /opt/odoo/custom-addons:/mnt/extra-addons -v /opt/odoo/enterprise-addons:/mnt/enterprise-addons -v /opt/odoo/odoo.conf:/etc/odoo/odoo.conf odoo:19 odoo -d trial -u MODULE_NAME --stop-after-init && docker start odoo-trial-app'"
Git Push
cd K:/Github/Odoo-Modules/fusion-plating && git push origin main
Pushes to both GitHub and Gitea (nexasystems.ca) via multiple remotes.
Supabase Knowledge Base
Project: nexasystems (id: ikvdlqkbqsitabxidvnq)
fusionapps.decisions— past architecture decisionsfusionapps.issues— known issues and fixesfusionapps.code_snippets— reference codefusionapps.quick_commands— deployment and admin commandsfusionapps.vm_registry— VM inventoryfusionapps.proxmox_nodes— cluster node specs
End-to-End Business Workflow
Full Lifecycle (What Exists Today)
┌─ QUOTATION ──────────────────────────────────────────────────────┐
│ 1. Customer submits RFQ on portal [DONE] │
│ → FpQuoteRequest (state: new → under_review → quoted) │
│ → Model: fusion_plating_portal/models/fp_quote_request.py │
│ │
│ 2. Customer accepts → "Create Sale Order" button [DONE] │
│ → action_create_sale_order() creates SO with lines │
│ → Links SO origin back to RFQ ref │
│ │
│ 3. SO confirmed → MRP creates Manufacturing Order [DONE] │
│ → Standard Odoo sale_mrp flow │
└──────────────────────────────────────────────────────────────────┘
┌─ MANUFACTURING ──────────────────────────────────────────────────┐
│ 4. MO confirmed → Portal Job auto-created [DONE] │
│ → MrpProduction.action_confirm() override │
│ → Creates FpPortalJob (state: in_progress) │
│ → Links via x_fc_portal_job_id │
│ │
│ 5. Planner assigns recipe + configures steps [DONE] │
│ → x_fc_recipe_id set on MO │
│ → Opens fp.recipe.config.wizard for opt-in/out │
│ → Creates fusion.plating.job.node.override records │
│ │
│ 6. Work orders generated from recipe [DONE] │
│ → _generate_workorders_from_recipe() in bridge_mrp │
│ → One WO per operation node, steps become WO instructions │
│ → Respects opt-in/out overrides from job.node.override │
│ │
│ 7. Operators execute WOs on shopfloor [DONE] │
│ → Plant Overview kanban (drag between work centres) │
│ → Batch chemistry tracking (FpBatch + FpBatchChemistry) │
│ → Quality holds (FpQualityHold → FpNcr → FpCapa) │
│ │
│ 8. MO marked done → Portal job ready_to_ship [DONE] │
│ → MrpProduction.button_mark_done() override │
│ → Auto-creates FpDelivery (draft) │
└──────────────────────────────────────────────────────────────────┘
┌─ SHIPPING & INVOICING ───────────────────────────────────────────┐
│ 9. CoC report generated [DONE] │
│ → report_coc.xml (PDF with job info, certification, sig) │
│ → Attached to delivery + portal job │
│ │
│ 10. Delivery scheduled & executed [DONE] │
│ → FpDelivery: draft → scheduled → en_route → delivered │
│ → Chain of custody auto-logged (FpChainOfCustody) │
│ → Proof of delivery captured (FpProofOfDelivery) │
│ → Routes with stops (FpRoute + FpRouteStop) │
│ │
│ 11. Delivery marked → Portal job shipped [DONE] │
│ → FpDelivery.action_mark_delivered() override │
│ → Sets actual_ship_date + tracking_ref on portal job │
│ │
│ 12. Account hold check before invoicing [DONE] │
│ → x_fc_account_hold on res.partner (fusion_plating_invoicing)│
│ → Blocks SO confirm, invoice post, shipping for non-managers │
│ │
│ 13. Invoice posted → Portal job complete [DONE] │
│ → AccountMove.action_post() override │
│ → Sets invoice_ref on portal job, state → complete │
│ │
│ 14. Auto-email with CoC + Invoice + Tracking [DONE] │
│ → fusion_plating_notifications module │
│ → fp.notification.template (configurable per trigger event) │
│ → fp.notification.log (audit trail) │
└──────────────────────────────────────────────────────────────────┘
┌─ CUSTOMER PORTAL ────────────────────────────────────────────────┐
│ 15. Customer sees on portal [DONE] │
│ → Job progress bar (received → complete) │
│ → CoC download, invoice access, tracking ref │
│ → Quote request history │
└──────────────────────────────────────────────────────────────────┘
Per-Job Recipe Overrides (v19.0.2.0.0 bridge_mrp)
x_fc_recipe_idonmrp.production→ links MO to recipefusion.plating.job.node.override→ per-job opt-in/out decisionsfp.recipe.config.wizard→ checklist wizard for planner- "Overrides" stat button on MO form
- Located in
fusion_plating_bridge_mrp
All Gaps Resolved (2026-04-12/13)
| Gap | Resolution | Module |
|---|---|---|
| Recipe → Work Orders | _generate_workorders_from_recipe() — one WO per operation, steps become instructions |
fusion_plating_bridge_mrp v2.1.0 |
| Account Hold Check | x_fc_account_hold on res.partner, blocks SO/invoice/shipping for non-managers |
fusion_plating_invoicing |
| Auto-Email Package | fp.notification.template + fp.notification.log with hooks on SO confirm, receiving, invoice |
fusion_plating_notifications |
| Quotation Configurator | Part catalog, coating configs, pricing engine, 3D STL viewer, portal wizard | fusion_plating_configurator |
| Parts Receiving | Receiving records, inspection, damage logging, SO auto-create, MRP soft gate | fusion_plating_receiving |
| Certificate Registry | Unified fp.certificate with thickness readings, CoC/thickness/Nadcap types | fusion_plating_certificates |
| Local Delivery | Forked fusion_tasks with GPS/maps, stripped of claims/sync, delivery-specific fields | fusion_tasks |
Architectural Decisions Made
- Recipe → WO: One WO per
operationnode, childstepnodes become numbered instructions in WO description - Account hold: Manual flag on
res.partner(auto from aging is roadmap) - Email triggers: SO confirmed, parts received, invoice posted (configurable per trigger)
- Configurator: Custom build with formula-based pricing, estimator override, portal self-service wizard
- Model naming: New models use
fp.*prefix, existing keepfusion.plating.* - Security groups: Role-based (Estimator, Receiving, Accounting, Shop Manager) layered on existing privilege hierarchy (Operator→Supervisor→Manager→Admin)
Key Models Quick Reference
| Model | Module | Purpose |
|---|---|---|
fusion.plating.process.node |
fusion_plating |
Recipe tree (template) |
fusion.plating.process.node.input |
fusion_plating |
Operator input definitions |
fusion.plating.job.node.override |
fusion_plating_bridge_mrp |
Per-job opt-in/out |
fp.part.catalog |
fusion_plating_configurator |
Customer part library (geometry, material) |
fp.coating.config |
fusion_plating_configurator |
Coating configuration templates |
fp.treatment |
fusion_plating_configurator |
Pre/post treatment steps |
fp.pricing.rule |
fusion_plating_configurator |
Formula-based pricing engine |
fp.pricing.complexity.surcharge |
fusion_plating_configurator |
Complexity surcharge lines |
fp.quote.configurator |
fusion_plating_configurator |
Configurator session + price calc |
fp.receiving |
fusion_plating_receiving |
Parts receiving record |
fp.receiving.line |
fusion_plating_receiving |
Per-part receiving detail |
fp.receiving.damage |
fusion_plating_receiving |
Damage log entry |
fp.invoice.strategy.default |
fusion_plating_invoicing |
Customer-level invoice strategy |
fp.certificate |
fusion_plating_certificates |
Certificate registry (CoC, thickness, etc.) |
fp.thickness.reading |
fusion_plating_certificates |
Fischerscope measurement data |
fp.notification.template |
fusion_plating_notifications |
Configurable email notification |
fp.notification.log |
fusion_plating_notifications |
Email audit trail |
fusion.plating.quote.request |
fusion_plating_portal |
Customer RFQ |
fusion.plating.portal.job |
fusion_plating_portal |
Portal-facing job tracker |
fusion.plating.customer.spec |
fusion_plating_quality |
Spec library |
fusion.plating.quality.hold |
fusion_plating_quality |
Parts on hold |
fusion.plating.ncr |
fusion_plating_quality |
Non-conformance reports |
fusion.plating.capa |
fusion_plating_quality |
Corrective actions |
fusion.plating.batch |
fusion_plating_batch |
Rack/barrel batch tracking |
fusion.plating.kpi |
fusion_plating_kpi |
KPI definition (OTD, yield, throughput, etc.) |
fusion.plating.kpi.value |
fusion_plating_kpi |
KPI daily value (auto-computed or manual) |
fusion.plating.delivery |
fusion_plating_logistics |
Delivery with chain of custody |
fusion.plating.pickup.request |
fusion_plating_logistics |
Customer pickup requests |
fusion.plating.route |
fusion_plating_logistics |
Driver routes with stops |
fusion.technician.task |
fusion_tasks |
Local delivery task (GPS, maps) |
fusion.technician.location |
fusion_tasks |
Driver GPS tracking |
Repackaged Enterprise Modules
See K:\Github\RePackaged-Odoo\CLAUDE.md for full details. Key points:
- Odoo 19 enterprise modules repackaged for community edition
- All OEEL-1 licenses changed to LGPL-3
- Phone-home/telemetry gutted
web_enterpriseandmail_enterpriseare installed on odoo-entech- Addons path includes:
_dependencies,accounting,inventory_manufacturing,hr,sales,ai,fusion_backend,custom,website
Fine-Tuning Initiative (Started 2026-04-21)
System-wide UX gap closure. Running PLAN → SPEC → IMPLEMENT per sub-project so we don't
rewrite code as new requirements surface. Each sub-project has its own design doc in
docs/superpowers/specs/ and its own implementation plan before any code lands.
Sub-Project Roadmap
| # | Sub-project | Status | Gaps |
|---|---|---|---|
| 1 | Direct Order Wizard fix (no auto-confirm/auto-email) | Shipped 2026-04-22 (commit afd8bae+) | Gap 1 |
| 2 | Part Data Model Overhaul (part#/rev required, dual descriptions, per-part cert requirement, SKU→Part Number on customer docs) | Shipped 2026-04-22 (commits 868b418..afd8bae) | 2b, 2c, 2d, 4 |
| 3 | Default Process + Composer per part (reuse recipe tree) | Shipped 2026-04-22 (commits ce07daa..f059348) | 2e, 2f |
| 4 | Contract Review (optional, per-part, settings-driven QA roster, QA-005 1:1 PDF) | Shipped 2026-04-22 | 2i |
| 5 | Order-line fields (fp.serial registry, auto job#, coating-scoped thickness dropdown, revision picker) | Shipped 2026-04-22 | 5, 6, Q2 |
| 6 | Contact Profiles & Communication Routing (per-contact flags + per-location routing + global contact; single resolver helper) | Shipped 2026-04-22 | client transcript A/B/C |
| 7 | IoT tuning (per-sensor polling interval + ingest rate-limit; entech seeded with 25 tanks / 50 sensors) | Shipped 2026-04-22 | client transcript D |
| 8 | Receiving / Inspection / QC flow restructure (fp.receiving = box count only; new fp.racking.inspection per MO; WO soft gate; delivery box-parity warning) | Shipped 2026-04-22 | client transcript E |
| 9 | Process variants per part + persistent draft order wizard + tax per line + payment terms wired + chatter + nicer breadcrumbs across plating models | Shipped 2026-04-26 | various wizard/UX |
| 10 | Quote → Direct Order promotion (won quotes consolidate onto a single PO instead of spawning standalone 1-line SOs) | Shipped 2026-04-26 | redundancy concern |
| 11 | MRP cutout — bridge_mrp deletion + MRP module uninstall (7-phase migration: relocate models, swap inherits, drop legacy FK columns, uninstall mrp + 10 cascade modules) | Shipped 2026-04-26 | bridge_mrp removal |
| 12 | Native Quality — full Odoo quality_control replacement + RMA + integration polish |
In flight (planned) | quality dependency removal |
| ∞ | First-off / last-off QC | Deferred | client transcript F |
| ∞ | VEC machine auto-ingest (Word-format thickness report from network-connected XRF; different machine from Fischerscope) | Deferred | client transcript G |
| ∞ | RMA customer portal submission | Deferred (Sub 12 phase 2) | follow-on to Sub 12 |
Sub 2 Locked Decisions (2026-04-21)
| Q | Decision |
|---|---|
| Q1 — Cert requirement precedence | Part wins; partner is fallback. New selection certificate_requirement on fp.part.catalog: inherit / none / coc / coc_thickness. Default inherit preserves current behaviour for existing records. |
| Q2 — Revision handling | Keep existing chain (parent_part_id, is_latest_revision, revision_ids). Out-of-scope for Sub 2. The "revision picker at order entry" moves to Sub 5. |
| Q3 — Required-field flip | Strict + backfill. On upgrade: part_number = name if empty; revision = 'A' if empty. Then required=True for both. name becomes optional. |
| Q4 — Descriptions shape | Split fp.sale.description.template.description into internal_description + customer_facing_description. Repeater on the part's Descriptions tab gains two columns. Old description column dropped in migration. |
| Q5 — SKU vs Part Number | Use fp.part.catalog.part_number directly as the source of truth. Don't sync to default_code. Customer-facing reports print part_number; internal reports keep showing default_code (service code). Odoo-native screens untouched. |
| Q6 — Description required at order entry | Both required. SO line carries name (customer-facing, already Odoo standard) + new x_fc_internal_description (ops workflow). Both required before save. |
Sub 2 Defensive Measures (Prevent Rework When Later Subs Land)
- Single-source cert resolution function —
mrp.production._fp_resolve_cert_requirement(self)returns(want_coc, want_thickness). Every caller (cert cascade, QC gate, notification routing) goes through this. When Sub 6 restructures partner-level flags into location / contact permissions, one function updates — no call-site hunt. - Shared QWeb line-header macro —
fusion_plating_reports.customer_line_headerrenderspart_number + revision + customer-facing descriptionwith fallback to product name for non-part lines. All 4 customer-facing reports (SO, invoice, packing slip, BoL) call the macro. Sub 5's revision picker updates the macro once, all reports follow. - Isolated migration — Sub 2's
post_init_hookis idempotent (NULL/empty checks). Safe to re-run. Doesn't fight Sub 3/4/5/6 migrations. - Additive SO line fields —
x_fc_internal_description,x_fc_description_template_idsit alongside future Sub 5 fields (x_fc_serial_number,x_fc_job_number,x_fc_thickness,x_fc_revision_snapshot) with zero touchpoints. - Clean removal of old
descriptioncolumn — migrated then dropped. Not kept as deprecated. One clean break now beats two migrations later.
Sub 6 Preview — Contact Profiles & Communication Routing (client transcript A/B/C)
- Sub-contacts under
res.partnerwith per-contact permissions: certs / QC / quotes+SO / invoices. - Multiple delivery locations per customer; each location has its own notification list.
- Global contact (company-level + location-level) gets all communications.
- Will restructure or augment the partner-level
x_fc_send_coc/x_fc_send_thickness_reportflags that Sub 2 currently falls back to. Sub 2's_fp_resolve_cert_requirementis the update point.
Sub 7 Preview — IoT Tuning (client transcript D)
- 6–10 active tanks (of ~20–25 total) need continuous monitoring.
- Polling interval: 30 minutes acceptable, 15 minutes ideal. Configurable per tank.
- Temperature, pH, nickel concentration — all on automated controller (existing
fusion_plating_iotmodule). - Work scope: ensure per-sensor interval field exists + defaults + seed 6–10 tank.sensor records.
Sub 8 Preview — Receiving / Inspection / QC Restructure (client transcript E)
Current flow (wrong): Direct order → receiving entry → receiver inspects on arrival. Correct flow:
- Customer ships parts in boxes. Receiver counts boxes (does NOT inspect individual parts).
- Boxes sit in staging until racking.
- Racking crew opens boxes, inspects each part as they load racks (inspection ≠ receiving).
- Parts go through plating process.
- Post-plate QC on machine (thickness / depth / coating thickness) — existing QC gate (Phase 1–3 work).
- Pack back into the SAME boxes they arrived in. Same qty out as in.
Implication: The current fusion_plating_receiving module conflates receiving + inspection. Sub 8 splits them. Racking-time inspection becomes its own record, linked to WOs not to receiving.
Deferred Items (Future)
- First-off / last-off QC — first and last part of each batch get full QC inspection; middle parts sampled. Not priority.
- VEC machine auto-ingest — different from Fischerscope. Exports a Word doc (picture + data) named
workorder_PO.docxto a network share. Plan: auto-scan the share, parse, attach to QC as thickness_report. Defer until core flow is solid.
Client-Confirmed Operational Thresholds
- Tank polling: 15–30 min, half-hour acceptable
- Active tanks: 6–10 (not all 20–25)
- Boxes round-trip: parts ship back in the same boxes they arrived in, same quantity per box
How to Resume This Work in a Fresh Session
- Read this section (Fine-Tuning Initiative).
- Check the sub-project status table — which sub is in flight.
- Read the corresponding spec in
docs/superpowers/specs/YYYY-MM-DD-sub<N>-*-design.md. - Read the implementation plan if one exists.
- Continue from the next un-checked step.
Sub 11 — MRP Cutout (shipped 2026-04-26)
The Odoo mrp module + 10 cascade dependents have been uninstalled. fusion_plating_bridge_mrp is gone. The plating shop runs entirely on fp.job / fp.job.step. Document this so a fresh session doesn't try to re-add MRP refs.
Final state
- 0 rows in
mrp_production,mrp_workorder,mrp_workcenter - 205+
fp.jobrows, 1,800+fp.job.steprows in production - 0 custom-table FKs to MRP
- Modules uninstalled:
mrp,mrp_workorder,mrp_account,sale_mrp,purchase_mrp,quality_mrp,quality_mrp_workorder,project_mrp*,fusion_manufacturing,fusion_plating_bridge_mrp
Where things ended up after Sub 11
| Model / asset | Old home | New home |
|---|---|---|
fp.work.role, fp.operator.proficiency, hr.employee shop-roles, fusion.plating.process.node.x_fc_work_role_id |
fusion_plating_bridge_mrp |
fusion_plating (core) |
fp.qc.checklist.template (+line) |
fusion_plating_bridge_mrp |
fusion_plating_quality |
fusion.plating.quality.check (+line) |
fusion_plating_bridge_mrp |
fusion_plating_quality |
fp.thickness.reading.quality_check_id link + auto_extracted |
fusion_plating_bridge_mrp |
fusion_plating_quality |
res.partner.x_fc_requires_qc + x_fc_qc_template_id |
fusion_plating_bridge_mrp |
fusion_plating_quality |
fp.job.consumption |
fusion_plating_bridge_mrp |
fusion_plating_jobs |
sale.order.x_fc_workflow_stage + x_fc_assigned_manager_id + workflow buttons |
fusion_plating_bridge_mrp |
fusion_plating_jobs |
QC tablet OWL (fp_qc_checklist.js/.xml/.scss) + /fp/qc/* controller |
fusion_plating_bridge_mrp |
fusion_plating_quality |
| Production Priorities kanban | fusion_plating_bridge_mrp (mrp.workorder) |
fusion_plating_jobs (fp.job.step) |
Hard rules going forward
- Never re-introduce
'mrp'as a manifest dep. Usefp.jobfor jobs,fp.job.stepfor operations. x_fc_job_idis the canonical job link, notproduction_id. Drop legacy MO refs as you find them.fusion_plating_qualitydepends onfusion_plating_shopfloorfor SCSS tokens ($fp-page,$fp-card,$fp-accent). Don't strip that dep — the QC tablet bundle breaks without it.- The QC tablet OWL template namespace is
fusion_plating_quality.FpQcChecklist(wasfusion_plating_bridge_mrp.FpQcChecklist). Don't rename back.
Sub 12 — Native Quality Module (in flight, ~4 working days)
Goal: Build a complete native quality stack matching Odoo quality_control functionality plus plating-specific extensions (RMA, CAPA effectiveness, holds, 8D reports), with zero dependency on Odoo's quality / quality_control. After Sub 12 lands, those modules + fusion_plating_bridge_quality get uninstalled.
Module choice
Enrich fusion_plating_quality — no new modules. Existing module already owns NCR / CAPA / Hold / Check / Calibration / AVL / FAIR / Audit / Doc Control / Customer Spec / Contract Review.
Locked decisions (don't re-ask in fresh session)
| Q | Decision |
|---|---|
| RMA portal submission | Deferred to phase 2. Internal-only RMA in Sub 12. |
| 8D format | Full 8D (D1–D8 sections in the combined NCR + CAPA PDF). |
| Quality Dashboard | 5 tabs (Holds / Checks / NCRs / CAPAs / RMAs) in one client action with a summary header that totals open + overdue across all five. |
| Auto-NCR + auto-Hold on RMA receive | Automatic, with a manager-only "skip this RMA's auto-spawn" toggle on the RMA record. |
| Auto-CAPA on NCR closure | Automatic when severity in (high, critical), with a manager-only override on the NCR. |
| Quality team model | Build a dedicated fp.quality.team rather than reusing res.groups. Teams need their own kanban grouping + per-team escalation chains, which groups don't model well. |
| Stage model vs. state field on NCR | Both. Keep the existing state Selection (used by code paths). Add a parallel stage_id Many2one to fp.quality.alert.stage for the kanban draggable view. Computed bidirectional sync (stage ↔ state). |
| Trigger-based quality.point | Build a new fp.quality.point model. Trigger types: manual, receiving_done, job_step_done, job_done. Existing fp.qc.checklist.template STAYS — it's the template a point fires; the point is the trigger rule. |
| RMA back-link to original SO line | Required field. Always carry the original SO line so cert / part / coating context follows the return. |
| Module choice (one or many) | Single module — enrich fusion_plating_quality. |
Phase A — RMA model (~1 day)
File: fusion_plating_quality/models/fp_rma.py
Model: fusion.plating.rma
| Field | Type | Notes |
|---|---|---|
name |
Char | Sequence RMA/YYYY/NNNN |
partner_id |
M2O res.partner |
Required |
sale_order_id |
M2O sale.order |
The original order being returned |
sale_order_line_ids |
M2M sale.order.line |
Specific lines being returned (subset of the SO) |
original_job_ids |
M2O fp.job (compute from SO lines) |
For navigation only |
state |
Selection | draft / authorised / shipped_to_us / received / triaged / resolving / resolved / closed / cancelled |
trigger_source |
Selection | customer_complaint / qc_fail_post_ship / inspection_post_delivery / other |
severity |
Selection | low / medium / high / critical |
complaint_description |
Html | What the customer reported |
triage_findings |
Html | What we found on inspection |
resolution_type |
Selection | replace / rework / refund / scrap |
resolution_notes |
Html | Free-form notes on the chosen path |
replacement_job_id |
M2O fp.job |
When replace/rework — the new job created |
refund_invoice_id |
M2O account.move |
When refund — the credit note |
inbound_receiving_id |
M2O fp.receiving |
The receiving record auto-created when carrier delivers |
inbound_picking_id |
M2O stock.picking |
Optional — if a stock.picking is also created |
linked_ncr_ids |
O2M fusion.plating.ncr (inverse rma_id) |
NCRs spawned from this RMA |
linked_capa_ids |
O2M fusion.plating.capa (related via NCRs) |
Read-only roll-up |
linked_hold_ids |
O2M fusion.plating.quality.hold (inverse rma_id) |
Holds placed on returned parts |
qty_returned |
Integer | Total units customer is returning |
qty_received |
Integer | Counted on receipt |
customer_tracking |
Char | Customer's outbound tracking # |
our_tracking |
Char | Our return-to-shop tracking # |
carrier_id |
M2O delivery.carrier |
Optional |
qr_code |
Binary (compute) | QR encoding /fp/rma/<id> for the authorisation PDF |
auto_spawn_ncr |
Boolean | Default True. Manager can toggle off before saving. |
auto_spawn_hold |
Boolean | Default True. |
tag_ids |
M2M fp.quality.tag |
(Sub 12 Phase B) |
reason_id |
M2O fp.quality.reason |
(Sub 12 Phase B) |
team_id |
M2O fp.quality.team |
(Sub 12 Phase B) |
chatter |
mail.thread | mandatory |
Lifecycle hooks
action_authorise: statedraft → authorised. Generate the RMA authorisation PDF + email link/QR to customer (usingfp.notification.templateif installed; falls back to standard mail.template).action_mark_shipped_to_us: customer-driven; updates state when carrier scan logged.- On
fp.receivingcreate withrma_idset: state→ received. Ifauto_spawn_ncr, create anfusion.plating.ncrpre-filled (description, severity, customer, parent SO line). Ifauto_spawn_hold, createfusion.plating.quality.holdfor the returned qty. action_triage_complete: state→ triaged. Requiresresolution_typeset.action_resolve: state→ resolved. Triggers resolution-specific actions:replace→ spawn newfp.jobcloned from originalrework→ spawn newfp.jobreferencing the returned units (linked to inboundfp.receiving)refund→ openaccount.move.refundwizard, link result torefund_invoice_idscrap→ createfp.job.consumptionrow tagged 'rma_scrap' + post chatter
action_close: state→ closed. Locks editing.action_cancel: any state →cancelled(manager only).
Smart buttons
RMA form gets buttons to: original SO, original Jobs, inbound Receiving, replacement Job, refund Invoice, NCRs (count), CAPAs (count), Holds (count). Per-target button visibility based on resolution_type / state.
Sequence
Create ir.sequence fp.rma with prefix RMA/%(year)s/, padding 4. Data file fp_rma_sequence.xml.
Reports
fusion_plating_reports/report/report_fp_rma_authorisation.xml — single-page customer-facing PDF with QR code. Branded "EN Technologies".
Phase B — Categorisation & kanban infra (~half day)
Files: fusion_plating_quality/models/fp_quality_tag.py, fp_quality_reason.py, fp_quality_team.py, fp_quality_alert_stage.py
fp.quality.tag
name(Char, required, translate)color(Integer, kanban color)active(Boolean)- Reused by NCR / CAPA / Hold / RMA / Check via
M2M tag_ids
fp.quality.reason
name,description,category(selection:process / supplier / equipment / human / material / other)- Curated reason library so root-cause classification is consistent
fp.quality.team
name,lead_user_id(M2O res.users),member_ids(M2M res.users)escalation_user_id(manager who gets notified on missed deadlines)- Used by NCR / RMA — primary owner team
fp.quality.alert.stage
name,sequence,fold(Boolean — collapsed-by-default in kanban)- Default stages seeded: New / Investigating / Containment / Disposition / Awaiting Sign-off / Closed / Cancelled
- Add
stage_id = fields.Many2one('fp.quality.alert.stage')tofusion.plating.ncrANDfusion.plating.rma. Map state ↔ stage_id via_inverse_*so legacy code paths keep working.
Apply tag/reason/team M2M/M2O fields to: NCR, CAPA, Hold, Check, RMA
Each model gets tag_ids, reason_id, team_id. NCR + RMA additionally get stage_id.
Phase C — Trigger-based quality points (~half day)
File: fusion_plating_quality/models/fp_quality_point.py
fp.quality.point
name,active,descriptiontrigger_type(Selection):manual / receiving_done / job_confirmed / job_step_done / job_done / so_confirmed- Filters (any combination):
partner_ids(M2M),part_catalog_ids(M2M),coating_config_ids(M2M),step_kind(Selection — wet/bake/inspect/etc.) template_id(M2Ofp.qc.checklist.template) — required, the checks to spawnassignee_user_id(M2Ores.users) — optional default inspector- Fires
_spawn_check_for(<source_record>)which creates afusion.plating.quality.checkfrom the template + binds it to the source viajob_idorstep_id.
Hooks (already partly in place — extend)
fp.receiving.writehook (existing): when state flips toclosed, walk allfp.quality.pointwith triggerreceiving_donematching the receiving's partner/parts → spawn checks.fp.job.action_confirmhook (existing — currently calls_fp_create_qc_check_if_needed): replace with quality.point lookup. Keep the existing partner-template fallback as a default point seeded byfp_qc_data.xml.fp.job.button_mark_done: triggerjob_donepoints.fp.job.step.button_finish: triggerjob_step_donepoints.
Phase D — Integration polish (~1 day)
fp.jobform smart-button row: addHolds,Checks,NCRs,CAPAs,RMAsbuttons with badge counts. Always-visible (zero is OK).sale.orderform smart-button row: same five, rolled up across all linked jobs.res.partnerform: customer-level "Quality History" smart button that opens a kanban filtered to that partner across all 5 record types.- One-click cross-creation:
- Hold form →
Open NCRbutton — pre-fills NCR with hold's part / customer / quantity / linked job. - NCR form →
Spawn CAPAbutton — visible when state ∈ {disposition, closed} and severity ≥ medium. - CAPA form →
Verify Effectivenessbutton — schedules a follow-up check on the originating NCR.
- Hold form →
- Unified Quality Dashboard (
fp_quality_dashboardclient action):- 5 tabs: Holds / Checks / NCRs / CAPAs / RMAs
- Each tab is a kanban grouped by
stage_id(NCRs/RMAs) orstate(Holds/Checks/CAPAs) - Header summary card: open count + overdue count across all 5 types
- Filters: my team / my customer / overdue / high-severity
- Menu: Plating → Quality → Dashboard
- CAPA closure-loop linkage: when CAPA effectiveness verification fails, auto-spawn a new NCR linked back to the original. Closes the loop "we said we fixed it but it happened again."
Phase E — Reports (~half day)
Files: fusion_plating_reports/report/report_fp_rma_authorisation.xml, report_fp_8d.xml, report_fp_quality_monthly.xml
- RMA Authorisation PDF: single-page customer-facing. Header with our logo + customer info, RMA number, parts listed (table), return-to address, QR code linking to
/fp/rma/<id>for status tracking, carrier instructions. - 8D Report (NCR + CAPA combined):
- D1: Team (from
team_id+ member_ids) - D2: Problem description (NCR description + scope)
- D3: Containment (NCR containment narrative)
- D4: Root cause analysis (CAPA root_cause + reason_id)
- D5: Permanent corrective action (CAPA action_plan)
- D6: Implement & verify (CAPA implementation_date + verification_evidence)
- D7: Prevent recurrence (CAPA preventive_actions)
- D8: Congratulate the team (CAPA closure notes + team sign-offs)
- Auto-renders when both NCR and CAPA exist; degraded mode if CAPA missing.
- D1: Team (from
- Monthly Quality Summary (
fp_quality_monthlyreport):- Counts by record type / severity / customer / month
- Overdue ageing buckets
- CAPA effectiveness rate (verified / total closed)
- Repeat-customer-issue flag (>2 NCRs same customer in 90 days)
- Run via cron monthly + on-demand from dashboard.
Phase F — Test + verify (~half day)
End-to-end smoke flow on a fresh DB:
- Customer reports issue → create RMA → authorise → email PDF
- Customer ships → carrier delivers →
fp.receivingauto-created → RMA receives → NCR + Hold auto-spawn - QA triages NCR → finds root cause → spawns CAPA (auto via severity rule)
- CAPA assigned to engineering → action plan written → implemented → effectiveness check scheduled
- Effectiveness verified → CAPA closes → NCR closes → RMA resolves (rework path) → replacement job created from original → ships → CoC issued → invoice
- Run 8D report on the closed NCR/CAPA pair
- Verify dashboard counts update at every state transition
- Confirm legacy NCR/CAPA/Hold/Check forms still work (no regressions)
- ACL drilldown: operator sees what they should, supervisor more, manager all
Phase G — Drop Odoo quality cascade (~30 min)
Pre-conditions: Phases A–F all merged + smoke-tested.
- Strip the three custom fields from
fusion.plating.ncr(x_fc_quality_alert_id,x_fc_quality_alert_synced,x_fc_auto_sync— added by bridge_quality) - Remove
fusion_plating_bridge_qualityfrom/mnt/extra-addons/custom/ - SQL:
UPDATE ir_module_module SET state='to remove' WHERE name IN ('fusion_plating_bridge_quality', 'quality_control', 'quality') AND state='installed'; - Restart odoo → cascade uninstall fires
- ALTER TABLE drop the three NCR columns
- (Optional) move
/mnt/extra-addons/inventory_manufacturing/quality{,_control}/out of the path so they can't auto-reinstall
Server / deployment notes (entech)
- LXC 111 on pve-worker5, native odoo (apt), DB
admin, addons path/mnt/extra-addons/custom/ - Update flow:
ssh pve-worker5 "pct exec 111 -- bash -c 'systemctl stop odoo && \ su - odoo -s /bin/bash -c \"/usr/bin/odoo -c /etc/odoo/odoo.conf -d admin \ -u fusion_plating_quality --stop-after-init\" && systemctl start odoo'" - File copy:
cat LOCAL | ssh pve-worker5 "pct exec 111 -- bash -c 'cat > REMOTE'" - Asset cache bust:
DELETE FROM ir_attachment WHERE url LIKE '/web/assets/%'; - Always bump module version in
__manifest__.pyfor migrations to fire (currentfusion_plating_quality:19.0.3.0.0; bump to19.0.4.0.0for Sub 12).
Build order (executable checklist for fresh session)
- Read this Sub 12 section in full + the Sub 11 section above (for context on what's already native).
- Bump
fusion_plating_quality/__manifest__.pyversion to19.0.4.0.0. - Phase A — RMA: create
fp_rma.pymodel +fp_rma_views.xml+fp_rma_sequence.xml+ ACL rows + add to__manifest__.pydata list. - Phase A migration: not needed (new model, fresh table).
- Phase B — categorisation: create the 4 small models + their views + ACL. Add
tag_ids / reason_id / team_idM2M/M2O to NCR, CAPA, Hold, Check, RMA. Addstage_idto NCR + RMA. - Phase B data: seed default stages + a few starter tags/reasons/teams in
fp_quality_categorisation_data.xml. - Phase C —
fp.quality.pointmodel + view + ACL + the 4 trigger hooks (infp.receiving,fp.job.action_confirm,fp.job.button_mark_done,fp.job.step.button_finish). - Phase D — smart buttons on
fp.job,sale.order,res.partner. Cross-creation buttons. Dashboard client action. - Phase E — three QWeb reports.
- Phase F — manual smoke test + ACL drilldown + screenshot the dashboard.
- Deploy each phase as it lands (don't batch — easier to roll back). Bump version each time.
- Phase G runs LAST, only after confirmation that A–F work end-to-end.
Things to NOT do
- Don't add
'quality'or'quality_control'to any manifest dep. They will be uninstalled by Phase G. - Don't import from
odoo.addons.quality.*. Use only native models. - Don't put RMA in a new module. It belongs in
fusion_plating_quality. - Don't break the existing QC tablet OWL. Its template namespace is
fusion_plating_quality.FpQcChecklist, endpoints are/fp/qc/*, andfusion_plating_qualitydepends onfusion_plating_shopfloorfor SCSS tokens. - Don't re-introduce
production_idreferences anywhere. Usejob_id/x_fc_job_id. MRP is gone. - Don't forget
rma_idinverse field on NCR + Hold — those One2many fields on RMA need an inverse Many2one on the linked model.
Status check before starting (run this first in the fresh session)
-- Should show 4: NCR, CAPA, Hold, Check (Sub 12 adds RMA = 5)
SELECT model FROM ir_model WHERE model LIKE 'fusion.plating.%' AND model SIMILAR TO '%(ncr|capa|hold|check|rma)%';
-- Should show 'fusion_plating_quality_bridge_quality_control' state — likely 'installed' until Phase G
SELECT name, state FROM ir_module_module WHERE name LIKE 'quality%' OR name LIKE 'fusion_plating_bridge_quality';
-- Confirm MRP is gone (Sub 11)
SELECT name, state FROM ir_module_module WHERE name = 'mrp'; -- expect 'uninstalled'
-- Live row counts so you know what survives
SELECT 'ncr' AS m, count(*) FROM fusion_plating_ncr
UNION ALL SELECT 'capa', count(*) FROM fusion_plating_capa
UNION ALL SELECT 'hold', count(*) FROM fusion_plating_quality_hold
UNION ALL SELECT 'check', count(*) FROM fusion_plating_quality_check;