# 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 | | ~~80~~ | ~~Culture~~ | ~~fusion_plating_culture~~ | ~~Values, Recognitions~~ **— RETIRED, uninstalled on entech, code kept in repo only** | | 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 `-i` or `-u` sequence that installs modules automatically. - Add them as a `depends` target 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 1. **NEVER code from memory** — Read reference files from the server first. 2. **Backend OWL**: `static template`, `static props = ["*"]`, standalone `rpc()` from `@web/core/network/rpc`. NOT `useService("rpc")`. 3. **HTTP routes**: `type="jsonrpc"` — NOT `type="json"` (deprecated in Odoo 19). 4. **Search views**: NO `group expand="0"`, NO `string` attribute on ``, NO `` wrapper for group-by filters. Use bare `` for group-by. 5. **res.config.settings**: Only boolean/integer/float/char/selection/many2one/datetime. NO Date fields. 6. **res.groups**: Use `privilege_id` (NOT `category_id`). `user_ids` is OK but the deprecated `users` alias is NOT. Always include `sequence` field. 7. **Field params**: `parent_path` does NOT accept `unaccent` parameter in Odoo 19. 8. **SCSS borders**: Use `$border-color` (SCSS variable) for card borders, NOT `color-mix()` in border shorthand — the SCSS compiler drops it. `color-mix()` works fine in `background-color`, `box-shadow`, etc. 9. **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. See `fusion_plating_shopfloor.scss` as the gold standard. 10. **XML comments**: No double-hyphens (`--`) inside `` comments — invalid XML, causes lxml parse error. 11. **XML data ordering**: Window actions must be defined BEFORE `` elements that reference them in the same file. 12. **Module install on new modules**: Use `--update=base` alongside `-i MODULE` to ensure Odoo rescans the addons path and finds the new module directory. 13. **Implied group cascade**: `implied_ids` on `res.groups` does NOT reliably propagate to users on module install. Always include `user_ids` to 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_id` field 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) - `icon` is 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_editor` client 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_overview` in `fusion_plating_shopfloor` - Kanban columns = work centres, cards = active `mrp.workorder` records - Drag & drop between columns (writes `workcenter_id` on 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, user `odoo`) - **Config**: `/etc/odoo/odoo.conf` - **Addons**: `/mnt/extra-addons/custom/` (fusion_plating modules live here) - **Service**: `systemctl {start|stop|restart} odoo` - **Update command**: ```bash 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.conf` or Odoo won't find the repackaged enterprise addons ### odoo-trial (VM 316 on pve-worker1) - **Type**: Docker (container `odoo-trial-app`, db `odoo-trial-db`) - **DB**: `trial` (user `odoo`) - **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): ```bash 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): ```bash 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**: ```bash 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 ```bash 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 decisions - `fusionapps.issues` — known issues and fixes - `fusionapps.code_snippets` — reference code - `fusionapps.quick_commands` — deployment and admin commands - `fusionapps.vm_registry` — VM inventory - `fusionapps.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_id` on `mrp.production` → links MO to recipe - `fusion.plating.job.node.override` → per-job opt-in/out decisions - `fp.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 1. **Recipe → WO**: One WO per `operation` node, child `step` nodes become numbered instructions in WO description 2. **Account hold**: Manual flag on `res.partner` (auto from aging is roadmap) 3. **Email triggers**: SO confirmed, parts received, invoice posted (configurable per trigger) 4. **Configurator**: Custom build with formula-based pricing, estimator override, portal self-service wizard 5. **Model naming**: New models use `fp.*` prefix, existing keep `fusion.plating.*` 6. **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_enterprise` and `mail_enterprise` are 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) 1. **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. 2. **Shared QWeb line-header macro** — `fusion_plating_reports.customer_line_header` renders `part_number + revision + customer-facing description` with 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. 3. **Isolated migration** — Sub 2's `post_init_hook` is idempotent (NULL/empty checks). Safe to re-run. Doesn't fight Sub 3/4/5/6 migrations. 4. **Additive SO line fields** — `x_fc_internal_description`, `x_fc_description_template_id` sit alongside future Sub 5 fields (`x_fc_serial_number`, `x_fc_job_number`, `x_fc_thickness`, `x_fc_revision_snapshot`) with zero touchpoints. 5. **Clean removal of old `description` column** — 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.partner` with 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_report` flags that Sub 2 currently falls back to. Sub 2's `_fp_resolve_cert_requirement` is 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_iot` module). - 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:** 1. Customer ships parts in boxes. Receiver counts boxes (does NOT inspect individual parts). 2. Boxes sit in staging until racking. 3. Racking crew opens boxes, inspects each part as they load racks (inspection ≠ receiving). 4. Parts go through plating process. 5. Post-plate QC on machine (thickness / depth / coating thickness) — existing QC gate (Phase 1–3 work). 6. 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.docx` to 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 1. Read this section (Fine-Tuning Initiative). 2. Check the sub-project status table — which sub is in flight. 3. Read the corresponding spec in `docs/superpowers/specs/YYYY-MM-DD-sub-*-design.md`. 4. Read the implementation plan if one exists. 5. 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.job` rows, **1,800+** `fp.job.step` rows 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 1. **Never re-introduce `'mrp'` as a manifest dep.** Use `fp.job` for jobs, `fp.job.step` for operations. 2. **`x_fc_job_id` is the canonical job link**, not `production_id`. Drop legacy MO refs as you find them. 3. **`fusion_plating_quality` depends on `fusion_plating_shopfloor`** for SCSS tokens (`$fp-page`, `$fp-card`, `$fp-accent`). Don't strip that dep — the QC tablet bundle breaks without it. 4. **The QC tablet OWL template namespace is `fusion_plating_quality.FpQcChecklist`** (was `fusion_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/` 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`**: state `draft → authorised`. Generate the RMA authorisation PDF + email link/QR to customer (using `fp.notification.template` if installed; falls back to standard mail.template). - **`action_mark_shipped_to_us`**: customer-driven; updates state when carrier scan logged. - **On `fp.receiving` create with `rma_id` set**: state `→ received`. If `auto_spawn_ncr`, create an `fusion.plating.ncr` pre-filled (description, severity, customer, parent SO line). If `auto_spawn_hold`, create `fusion.plating.quality.hold` for the returned qty. - **`action_triage_complete`**: state `→ triaged`. Requires `resolution_type` set. - **`action_resolve`**: state `→ resolved`. Triggers resolution-specific actions: - `replace` → spawn new `fp.job` cloned from original - `rework` → spawn new `fp.job` referencing the returned units (linked to inbound `fp.receiving`) - `refund` → open `account.move.refund` wizard, link result to `refund_invoice_id` - `scrap` → create `fp.job.consumption` row 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')` to `fusion.plating.ncr` AND `fusion.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`, `description` - `trigger_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` (M2O `fp.qc.checklist.template`) — required, the checks to spawn - `assignee_user_id` (M2O `res.users`) — optional default inspector - Fires `_spawn_check_for()` which creates a `fusion.plating.quality.check` from the template + binds it to the source via `job_id` or `step_id`. #### Hooks (already partly in place — extend) - `fp.receiving.write` hook (existing): when state flips to `closed`, walk all `fp.quality.point` with trigger `receiving_done` matching the receiving's partner/parts → spawn checks. - `fp.job.action_confirm` hook (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 by `fp_qc_data.xml`. - `fp.job.button_mark_done`: trigger `job_done` points. - `fp.job.step.button_finish`: trigger `job_step_done` points. ### Phase D — Integration polish (~1 day) 1. **`fp.job` form smart-button row**: add `Holds`, `Checks`, `NCRs`, `CAPAs`, `RMAs` buttons with badge counts. Always-visible (zero is OK). 2. **`sale.order` form smart-button row**: same five, rolled up across all linked jobs. 3. **`res.partner` form**: customer-level "Quality History" smart button that opens a kanban filtered to that partner across all 5 record types. 4. **One-click cross-creation**: - Hold form → `Open NCR` button — pre-fills NCR with hold's part / customer / quantity / linked job. - NCR form → `Spawn CAPA` button — visible when state ∈ {disposition, closed} and severity ≥ medium. - CAPA form → `Verify Effectiveness` button — schedules a follow-up check on the originating NCR. 5. **Unified Quality Dashboard** (`fp_quality_dashboard` client action): - 5 tabs: Holds / Checks / NCRs / CAPAs / RMAs - Each tab is a kanban grouped by `stage_id` (NCRs/RMAs) or `state` (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 6. **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` 1. **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/` for status tracking, carrier instructions. 2. **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. 3. **Monthly Quality Summary** (`fp_quality_monthly` report): - 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: 1. Customer reports issue → create RMA → authorise → email PDF 2. Customer ships → carrier delivers → `fp.receiving` auto-created → RMA receives → NCR + Hold auto-spawn 3. QA triages NCR → finds root cause → spawns CAPA (auto via severity rule) 4. CAPA assigned to engineering → action plan written → implemented → effectiveness check scheduled 5. Effectiveness verified → CAPA closes → NCR closes → RMA resolves (rework path) → replacement job created from original → ships → CoC issued → invoice 6. Run 8D report on the closed NCR/CAPA pair 7. Verify dashboard counts update at every state transition 8. Confirm legacy NCR/CAPA/Hold/Check forms still work (no regressions) 9. 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. 1. 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) 2. Remove `fusion_plating_bridge_quality` from `/mnt/extra-addons/custom/` 3. SQL: `UPDATE ir_module_module SET state='to remove' WHERE name IN ('fusion_plating_bridge_quality', 'quality_control', 'quality') AND state='installed';` 4. Restart odoo → cascade uninstall fires 5. ALTER TABLE drop the three NCR columns 6. (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: ```bash 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: ```bash 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__.py` for migrations to fire (current `fusion_plating_quality`: `19.0.3.0.0`; bump to `19.0.4.0.0` for Sub 12). ### Build order (executable checklist for fresh session) 1. Read this Sub 12 section in full + the Sub 11 section above (for context on what's already native). 2. Bump `fusion_plating_quality/__manifest__.py` version to `19.0.4.0.0`. 3. Phase A — RMA: create `fp_rma.py` model + `fp_rma_views.xml` + `fp_rma_sequence.xml` + ACL rows + add to `__manifest__.py` data list. 4. Phase A migration: not needed (new model, fresh table). 5. Phase B — categorisation: create the 4 small models + their views + ACL. Add `tag_ids / reason_id / team_id` M2M/M2O to NCR, CAPA, Hold, Check, RMA. Add `stage_id` to NCR + RMA. 6. Phase B data: seed default stages + a few starter tags/reasons/teams in `fp_quality_categorisation_data.xml`. 7. Phase C — `fp.quality.point` model + view + ACL + the 4 trigger hooks (in `fp.receiving`, `fp.job.action_confirm`, `fp.job.button_mark_done`, `fp.job.step.button_finish`). 8. Phase D — smart buttons on `fp.job`, `sale.order`, `res.partner`. Cross-creation buttons. Dashboard client action. 9. Phase E — three QWeb reports. 10. Phase F — manual smoke test + ACL drilldown + screenshot the dashboard. 11. Deploy each phase as it lands (don't batch — easier to roll back). Bump version each time. 12. 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/*`, and `fusion_plating_quality` depends on `fusion_plating_shopfloor` for SCSS tokens. - **Don't re-introduce `production_id` references anywhere.** Use `job_id` / `x_fc_job_id`. MRP is gone. - **Don't forget `rma_id` inverse 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) ```sql -- 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; ```