- Add model naming convention table (fp.* for new, fusion.plating.* for existing)
- Add fusion_plating_certificates as dedicated module with fp.thickness.reading model
- Fix complexity_surcharge: companion model instead of JSON text field
- Add recipe_id domain constraint [('node_type', '=', 'recipe')]
- Align security groups with existing 4-level privilege hierarchy
- Add currency_id to all monetary models
- Clarify fp.quote.configurator as persistent model with state lifecycle
- Fix canonical model names (fusion.plating.portal.job, fusion.plating.delivery)
- Add auto-population rules for invoice strategy and configurator defaults
- Lighten bridge_mrp deps: gates as mixins in receiving/invoicing modules
- Add deployment strategy for fusion_tasks (same server, not standalone)
- Add data migration section for existing quote request coexistence
- Add work centre mapping note (fusion.plating.work.center ↔ mrp.workcenter)
- Change x_fc_account_hold_date to Datetime for audit precision
- Add bilingual CoC implementation note (QWeb, not ir.translation)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
39 KiB
EN Tech Plating — End-to-End Workflow Design Spec
Date: 2026-04-12 Author: Nexa Systems (Claude-assisted) Client: EN Technologies (Electroless Nickel Technologies Inc.) Status: Approved for implementation planning
1. Overview
Complete end-to-end ERP workflow for an electroless nickel plating shop, replacing Steelhead Software. The system covers customer inquiry through invoicing, with a quotation configurator, parts receiving/inspection, flexible invoicing strategies, automated email notifications, certificate management, and local delivery dispatch.
Model Naming Convention
| Context | Convention | Example |
|---|---|---|
| New custom models (all new modules) | fp.* prefix |
fp.part.catalog, fp.certificate |
| Existing custom models (already in codebase) | Keep fusion.plating.* |
fusion.plating.portal.job, fusion.plating.delivery |
| New fields on standard Odoo models | x_fc_* prefix |
x_fc_po_number on sale.order |
| Legacy fields (from Studio era) | x_studio_* |
Preserved, not renamed |
The fp.* prefix is the new short-form convention for all models created in the new modules. Existing fusion.plating.* models are NOT renamed — they keep their current _name. All references in this spec use canonical _name values.
Architecture: Approach 2 — Dedicated Modules per Sub-System
| Module | Purpose | Location |
|---|---|---|
fusion_plating_configurator |
Quotation configurator, 3D viewer, pricing engine, part catalog | fusion-plating/ |
fusion_plating_receiving |
Parts receiving, inspection, damage logging, PO matching | fusion-plating/ |
fusion_plating_invoicing |
Invoice strategy engine (deposit/progress/net/COD), account holds | fusion-plating/ |
fusion_plating_notifications |
Auto-email engine, notification templates, audit log | fusion-plating/ |
fusion_plating_certificates |
Certificate registry (CoC, thickness, Nadcap, customer-specific) | fusion-plating/ |
fusion_tasks |
Local delivery dispatch (forked from Westin, stripped of claims) | Entech Plating/ |
Plus updates to existing modules:
fusion_plating_bridge_mrp— Recipe-to-WO generation (lightweight; gates live in their own modules)fusion_plating_portal— Customer-facing configurator UI, 3D previewfusion_plating_reports— CoC template updates, thickness report templatefusion_plating_quality— Thickness reading model for Fischerscope data
2. Sales Integration — Fusion Plating as Single Hub
The Fusion Plating app becomes the single workspace. Users never leave it. Sale orders are managed with custom plating-specific views.
Menu Structure
Fusion Plating (app)
├── Sales (default landing)
│ ├── Quotations (sale.order, state=draft/sent)
│ ├── Sale Orders (sale.order, state=sale)
│ ├── Customers (res.partner, customer_rank > 0)
│ └── Part Catalog (fp.part.catalog)
├── Configurator
│ ├── New Quote (fp.quote.configurator, persistent model)
│ ├── Coating Configurations (fp.coating.config)
│ └── Pricing Rules (fp.pricing.rule)
├── Manufacturing
│ ├── Manufacturing Orders
│ ├── Work Orders
│ └── Plant Overview
├── Receiving & Inspection
├── Shipping & Delivery
│ ├── Deliveries (fp.delivery)
│ ├── Local Delivery Tasks (fusion.delivery.task)
│ └── Routes (fp.route)
├── Certificates
│ ├── All Certificates (fp.certificate)
│ ├── Certificates of Conformance (filtered: type=coc)
│ └── Thickness Reports (filtered: type=thickness_report)
├── Quality
├── Portal Jobs
├── Reports
└── Configuration
Sale Order Extensions (fields on sale.order)
x_fc_configurator_id— link back to configurator sessionx_fc_part_catalog_id— customer part being orderedx_fc_coating_config_id— coating configurationx_fc_po_number— customer PO reference (Char)x_fc_po_attachment_id— uploaded PO documentx_fc_po_received— Booleanx_fc_po_override— Boolean (manager override — proceed without PO)x_fc_po_override_reason— Textx_fc_invoice_strategy— Selection (deposit, progress, net_terms, cod_prepay)x_fc_deposit_percent— Floatx_fc_rush_order— Booleanx_fc_delivery_method— Selection (local_delivery, shipping_partner, customer_pickup)x_fc_receiving_status— Selection (not_received, partial, received, inspected) — computed- Smart buttons: Portal Job, Manufacturing Order, Delivery, Receiving, Invoices, Certificates
Custom Sale Order Views
- List: Customer, PO#, Part, Coating, Qty, Total, Receiving Status, Job Status, Delivery Method
- Form: inherits sale.order form, adds plating tabs (Part Details, Coating Config, Receiving, Job Tracking)
- Kanban: cards grouped by stage (Draft → Quoted → PO Received → Parts Received → In Production → Shipped → Invoiced)
Permission-Based Visibility
The existing codebase defines a 4-level privilege hierarchy in fusion_plating/security/fp_security.xml: Operator → Supervisor → Manager → Administrator. These new groups are role-based and work alongside (not replacing) the existing privilege levels. A user has both a privilege level (what they can do: read/write/create/delete) and one or more roles (what they can see: which menus appear).
| Role Group | Menu Visibility | Required Privilege Level |
|---|---|---|
fp_group_estimator |
Sales, Configurator, Customers, Part Catalog | Supervisor+ |
fp_group_shop_manager |
Everything (full menu) | Manager+ |
fp_group_shop_floor |
Manufacturing, Work Orders, Plant Overview only | Operator+ |
fp_group_receiving |
Receiving & Inspection, can view Sales (read-only) | Operator+ |
fp_group_shipping |
Shipping & Delivery, can view Sales (read-only) | Operator+ |
fp_group_quality |
Quality, can view Manufacturing | Supervisor+ |
fp_group_accounting |
Sales (invoicing fields), Reports | Supervisor+ |
Users are assigned to one or more role groups. The existing privilege hierarchy controls CRUD permissions; role groups control menu/view visibility. fp_group_shop_manager implies all other role groups (full access).
Standard Odoo groups are still required for underlying model access (e.g. sales_team.group_sale_salesman for SO access).
3. fusion_plating_configurator — Quotation Configurator & Pricing Engine
Users
- Primary: Internal estimator (full control, detailed configurator, price override)
- Secondary: Portal customer (simplified self-service, estimated pricing, 3D preview, lead gen)
Core Models
fp.part.catalog — Customer Part Library
| Field | Type | Description |
|---|---|---|
partner_id |
Many2one res.partner | Customer (required) |
name |
Char | Part name/description |
part_number |
Char | Customer's part number |
revision |
Char | Revision letter/number |
substrate_material |
Selection | aluminium, steel, stainless, copper, titanium, other |
geometry_source |
Selection | 3d_model, manual, pdf_drawing |
model_attachment_id |
Many2one ir.attachment | STEP/STL/IGES file |
drawing_attachment_ids |
Many2many ir.attachment | PDF drawings |
surface_area |
Float | Surface area value |
surface_area_uom |
Selection | sq_in, sq_ft, sq_cm, sq_m |
weight |
Float | For shipping cost calc |
dimensions_length |
Float | Manual measurement |
dimensions_width |
Float | Manual measurement |
dimensions_height |
Float | Manual measurement |
complexity |
Selection | simple, moderate, complex, very_complex |
masking_zones |
Integer | Number of areas requiring masking |
masking_description |
Text | e.g. "mask threaded holes" |
has_blind_holes |
Boolean | Complexity flag |
has_recesses |
Boolean | Complexity flag |
has_threads |
Boolean | Complexity flag |
notes |
Html | |
active |
Boolean | Archivable |
fp.coating.config — Coating Configuration Template
| Field | Type | Description |
|---|---|---|
name |
Char | e.g. "EN Mid-Phos AMS 2404" |
process_type_id |
Many2one fusion.plating.process.type | Process type |
recipe_id |
Many2one fusion.plating.process.node | Default recipe. Domain: [('node_type', '=', 'recipe')] |
phosphorus_level |
Selection | low_phos, mid_phos, high_phos, na |
thickness_min |
Float | Min thickness |
thickness_max |
Float | Max thickness |
thickness_uom |
Selection | mils, microns, inches |
spec_reference |
Char | e.g. "AMS 2404" |
certification_level |
Selection | commercial, mil_spec, nadcap, nuclear |
pre_treatment_ids |
Many2many fp.treatment | Bead blast, zincate, etc. |
post_treatment_ids |
Many2many fp.treatment | Bake, passivate, chromate, etc. |
active |
Boolean |
fp.treatment — Pre/Post Treatment
| Field | Type | Description |
|---|---|---|
name |
Char | e.g. "Bead Blast", "Zincate", "Bake" |
treatment_type |
Selection | pre, post |
default_duration_minutes |
Float | Estimated duration |
currency_id |
Many2one res.currency | Company currency (default) |
default_cost |
Monetary | Cost per application |
fp.pricing.rule — Formula-Based Pricing Engine
| Field | Type | Description |
|---|---|---|
name |
Char | Rule description |
coating_config_id |
Many2one fp.coating.config | Optional filter (global if blank) |
substrate_material |
Selection | Optional filter |
certification_level |
Selection | Optional filter |
pricing_method |
Selection | per_sqin, per_sqft, per_piece, flat_rate, formula |
currency_id |
Many2one res.currency | Company currency (default) |
base_rate |
Monetary | $ per unit |
thickness_factor |
Float | Multiplier per mil of thickness |
complexity_surcharge_ids |
One2many fp.pricing.complexity.surcharge | Surcharges by complexity level |
masking_rate_per_zone |
Monetary | $ per masking area |
setup_fee |
Monetary | One-time per batch |
minimum_charge |
Monetary | Floor price |
rush_surcharge_percent |
Float | Rush premium % |
sequence |
Integer | Priority — first matching rule wins |
active |
Boolean |
fp.pricing.complexity.surcharge — Complexity-Based Surcharge Line
| Field | Type | Description |
|---|---|---|
rule_id |
Many2one fp.pricing.rule | Parent rule (cascade) |
complexity |
Selection | simple, moderate, complex, very_complex |
surcharge_percent |
Float | Surcharge % for this complexity level |
fp.quote.configurator — The Configurator Session (Persistent Model)
This is a models.Model (NOT transient). Records persist for audit trail, re-quoting, and linking back from sale orders. The SO links back via x_fc_configurator_id.
| Field | Type | Description |
|---|---|---|
name |
Char | Auto-sequence (CFG-00001) |
state |
Selection | draft, confirmed, cancelled |
partner_id |
Many2one res.partner | Customer |
part_catalog_id |
Many2one fp.part.catalog | For repeat parts |
coating_config_id |
Many2one fp.coating.config | Coating selection |
quantity |
Integer | Number of parts |
batch_size |
Integer | Parts per rack/barrel |
surface_area |
Float | From catalog or entered |
thickness_requested |
Float | |
masking_zones |
Integer | |
complexity |
Selection | simple, moderate, complex, very_complex |
rush_order |
Boolean | |
turnaround_days |
Integer | |
delivery_method |
Selection | local_delivery, shipping_partner, customer_pickup |
currency_id |
Many2one res.currency | Company currency (default) |
shipping_fee |
Monetary | |
delivery_fee |
Monetary | |
calculated_price |
Monetary | Computed from pricing rules |
price_breakdown_html |
Html | Rendered breakdown |
estimator_override_price |
Monetary | Final price (defaults to calculated) |
sale_order_id |
Many2one sale.order | Created SO (set on "Create Quotation") |
notes |
Text |
Lifecycle: draft → (estimator builds quote) → confirmed (when SO created) → cancelled (if abandoned). Confirmed records are read-only. Re-quoting creates a new configurator record.
Price Calculation Flow
Part Catalog (surface area, complexity, masking)
+ Coating Config (process, thickness, spec level)
+ Pricing Rules (matched by coating + substrate + cert level)
+ Quantity / Batch Size
+ Rush surcharge (if applicable)
+ Delivery / Shipping fees
= Calculated Price
→ Estimator reviews & overrides if needed
→ Final quote price
3D Viewer
- OWL component using Three.js for STL rendering and OCCT (OpenCascade) WASM for STEP parsing
- Renders on both backend configurator form and portal page
- Features: wireframe/solid toggle, rotate/zoom, surface area highlight
- Server-side surface area calculation: Python
trimesh(STL) /cadquery/OCP(STEP) - Fallback: manual measurements if server can't parse the file
- Roadmap: Claude Vision for PDF drawing measurement extraction
Portal Side (Customer-Facing)
- Simplified wizard: upload part → select coating type → see estimated price range
- Uses same
fp.quote.configuratormodel with restricted fields - Customer sees estimated price range (not exact), 3D preview
- Submits → creates
fp.quote.requestwith configurator data attached - Internal estimator sees customer's config and refines it
Configurator → Sale Order Flow
- Estimator opens Configurator → builds quote
- Clicks "Create Quotation" → sale.order created with all x_fc_* fields
- SO line(s) auto-created (product = service product per coating type, qty, price = estimator's final price)
- Estimator reviews SO → sends quotation
- Customer accepts → confirms SO → triggers downstream flow
4. fusion_plating_receiving — Parts Receiving & Inspection
Core Models
fp.receiving — Receiving Record
| Field | Type | Description |
|---|---|---|
name |
Char | Auto-sequence (RCV-00001) |
sale_order_id |
Many2one sale.order | Required. Design decision: one receiving record per SO. If a customer ships parts for multiple SOs in one box, create separate receiving records per SO (receiver splits the count). This keeps the SO↔receiving link clean and avoids Many2many complexity. |
partner_id |
Many2one res.partner | Related from SO |
po_number |
Char | Related from SO x_fc_po_number |
received_by_id |
Many2one res.users | Who logged it |
received_date |
Datetime | Default=now |
state |
Selection | draft, inspecting, accepted, discrepancy, resolved |
expected_qty |
Integer | From SO line |
received_qty |
Integer | Entered by receiver |
qty_match |
Boolean | Computed: received == expected |
carrier_name |
Char | Who delivered |
carrier_tracking |
Char | Inbound tracking # |
notes |
Html | |
line_ids |
One2many fp.receiving.line | Per-part detail |
damage_ids |
One2many fp.receiving.damage | Damage log |
attachment_ids |
Many2many ir.attachment | Photos |
fp.receiving.line — Per-Part Receiving Detail
| Field | Type | Description |
|---|---|---|
receiving_id |
Many2one fp.receiving | Cascade |
part_catalog_id |
Many2one fp.part.catalog | Optional |
part_number |
Char | |
description |
Char | |
expected_qty |
Integer | |
received_qty |
Integer | |
condition |
Selection | good, damaged, mixed |
notes |
Text |
fp.receiving.damage — Damage Log Entry
| Field | Type | Description |
|---|---|---|
receiving_id |
Many2one fp.receiving | Cascade |
description |
Text | What's damaged |
severity |
Selection | cosmetic, functional, rejected |
photo_ids |
Many2many ir.attachment | |
action_required |
Selection | none, notify_customer, return_parts, proceed_as_is |
customer_notified |
Boolean | |
customer_response |
Text | |
resolved |
Boolean |
Workflow
SO Confirmed → Receiving record auto-created (state=draft)
→ Parts arrive → receiver enters qty, inspects condition
→ Match + good → state=accepted
→ Mismatch or damage → state=discrepancy
→ SO flagged, follow-up activity created
→ Customer contacted → resolution logged → state=resolved
→ Accepted/Resolved → SO x_fc_receiving_status = 'received'
→ Manufacturing can proceed
Manufacturing Gate
mrp.production.action_confirm()checkssale_order.x_fc_receiving_status- If not received → warning dialog (manager can override)
- Soft gate — warns, doesn't hard-block (flexibility for handshake deals, urgent jobs)
5. fusion_plating_invoicing — Invoice Strategy Engine
Invoice Strategies
| Strategy | Behaviour |
|---|---|
deposit |
Deposit invoice for X% on SO confirmation. Balance after shipping. |
progress |
Invoice per MO as each completes. |
net_terms |
Single invoice after shipping. Payment on terms. |
cod_prepay |
Full invoice on SO confirmation. Manufacturing blocked until paid. |
Core Models
fp.invoice.strategy.default — Customer-Level Default
| Field | Type | Description |
|---|---|---|
partner_id |
Many2one res.partner | Unique per customer |
default_strategy |
Selection | deposit, progress, net_terms, cod_prepay |
default_deposit_percent |
Float | e.g. 50.0 |
payment_term_id |
Many2one account.payment.term | |
notes |
Text |
Auto-Population Rules
When a customer is selected on a new SO:
- Look up
fp.invoice.strategy.defaultfor thatpartner_id - If found → auto-fill
x_fc_invoice_strategyandx_fc_deposit_percentfrom the default - If not found → leave blank (estimator must select manually)
- Estimator can always override per order
When a coating config is selected in the configurator:
- Auto-fill
thickness_requestedfromcoating_config.thickness_min(default to minimum) - Auto-fill surface area UOM from company default setting
When a part catalog entry is selected:
- Auto-fill
surface_area,complexity,masking_zones,substrate_materialfrom the catalog entry - These can be overridden per-quote if the part has changed
Account Hold (extends res.partner)
| Field | Type | Description |
|---|---|---|
x_fc_account_hold |
Boolean | Manually set by accounting |
x_fc_account_hold_reason |
Text | Why hold was placed |
x_fc_account_hold_date |
Datetime | When placed (Datetime for audit precision) |
x_fc_account_hold_by_id |
Many2one res.users | Who placed it |
Account Hold Behaviour
| Action | Hold Active | Result |
|---|---|---|
| Create new SO | Yes | Warning banner. SO can still be created. |
| Confirm SO | Yes | Blocked. Manager override available. |
| Create invoice | Yes | Blocked. Manager override available. |
| Ship / mark delivered | Yes | Blocked. Manager override available. |
| Customer visits portal | Yes | No visible indication. |
Roadmap: auto-hold computed from account.move aging.
Invoice Automation
- Deposit: SO confirmed → auto-create deposit invoice (X%) → balance invoice after shipping
- Progress: Each MO done → invoice for that MO's portion → final invoice for remaining balance
- Net terms: Delivery complete → auto-create full invoice → payment terms applied
- COD/Prepay: SO confirmed → auto-create full invoice → MO blocked until payment reconciled
Shipping Method Price Adjustment
- Method changes after invoicing:
- Draft invoice → amend the line
- Posted invoice → supplementary invoice or credit note
6. fusion_plating_notifications — Auto-Email Engine
Notification Triggers
| Trigger Event | Email Name | Attachments | Recipient |
|---|---|---|---|
| Quotation sent | Quote Ready | Quote PDF | Customer contact |
| SO confirmed | Order Confirmation | SO PDF | Customer contact |
| Parts received | Parts Received | — | Customer contact |
| MO complete | Ready for Pickup/Ship | — | Customer contact |
| Delivery shipped (carrier) | Shipment Notification | CoC, Thickness Report, Invoice | Customer contact |
| Delivery completed (local) | Delivery Confirmation | CoC, Thickness Report, Invoice, POD | Customer contact |
| Invoice posted | Invoice Notification | Invoice PDF | Billing contact |
| Deposit invoice created | Deposit Required | Deposit Invoice PDF | Billing contact |
Core Models
fp.notification.template — Configurable Email Templates
| Field | Type | Description |
|---|---|---|
name |
Char | Template name |
trigger_event |
Selection | Event type |
mail_template_id |
Many2one mail.template | Actual Odoo template |
active |
Boolean | Can disable specific notifications |
attach_coc |
Boolean | |
attach_thickness_report |
Boolean | |
attach_invoice |
Boolean | |
attach_packing_list |
Boolean | |
attach_pod |
Boolean | |
cc_internal_ids |
Many2many res.users | Internal CCs |
fp.notification.log — Audit Trail
| Field | Type | Description |
|---|---|---|
template_id |
Many2one fp.notification.template | |
trigger_event |
Selection | |
sale_order_id |
Many2one sale.order | |
partner_id |
Many2one res.partner | |
sent_date |
Datetime | |
recipient_email |
Char | |
attachment_names |
Text | Comma-separated list |
status |
Selection | sent, failed, bounced |
error_message |
Text | |
mail_mail_id |
Many2one mail.mail |
Document Assembly (Shipment Email)
- Find portal job linked to SO/MO
- Generate CoC PDF (bilingual EN/FR, customer logo, Nadcap badge if applicable)
- Attach thickness report if available
- Attach invoice PDF
- Include tracking info in email body (carrier tracking # or driver ETA)
- Send to customer contact
- Log in fp.notification.log
CoC Report Updates
- Customer logo placement (from partner.image_1920)
- Nadcap badge (conditional)
- EN Tech branding (replace Steelhead)
- Recorded thickness field
- Process description with spec references
- Bilingual certification statement
- Quantities: Shipped/Exp, NC Qty columns
- Configurable certifying authority signature
Thickness / Measurement Report (NEW template)
Based on Fischerscope XDAL 600 output:
- EN Tech header
- Equipment info (model, product, application)
- Microscope image (attached photo)
- Reading data table (NiP mils, Ni %, P %)
- Statistical summary (Mean, Std Dev, CoV%, Range)
- Calibration standard reference
- Operator, date/time
- Data entry: manual for now, future Fischerscope CSV import
Work Centre Mapping Note
The codebase has two work centre models: fusion.plating.work.center (core) and mrp.workcenter (standard MRP). Recipe nodes reference fusion.plating.work.center; MRP work orders use mrp.workcenter. The recipe-to-WO generation logic in fusion_plating_bridge_mrp must map between them. Each fusion.plating.work.center should have an x_fc_mrp_workcenter_id field linking to the corresponding mrp.workcenter. This mapping field should be added to the core module.
7. fusion_plating_certificates — Certificate Registry
Module owner: fusion_plating_certificates (NEW dedicated module).
Dependencies: fusion_plating, fusion_plating_portal, fusion_plating_reports, mrp
This module owns fp.certificate and fp.thickness.reading. It depends on fusion_plating_portal for the fusion.plating.portal.job link and on fusion_plating_reports for report generation.
Model: fp.certificate
| Field | Type | Description |
|---|---|---|
name |
Char | Auto-sequence (CERT-00001) |
certificate_type |
Selection | coc, thickness_report, mill_test, nadcap_cert, customer_specific |
partner_id |
Many2one res.partner | Customer |
sale_order_id |
Many2one sale.order | |
production_id |
Many2one mrp.production | |
portal_job_id |
Many2one fusion.plating.portal.job | Uses canonical model name |
part_number |
Char | Denormalized for fast search |
process_description |
Char | e.g. "ELECTROLESS NICKEL PLATING PER AMS 2404" |
spec_reference |
Char | |
po_number |
Char | Customer PO ref |
entech_wo_number |
Char | Internal WO # |
quantity_shipped |
Integer | |
issued_by_id |
Many2one res.users | |
certified_by_id |
Many2one res.users | Signing authority |
issue_date |
Date | Default=today |
attachment_id |
Many2one ir.attachment | Generated PDF |
thickness_reading_ids |
One2many fp.thickness.reading | Linked measurements |
state |
Selection | draft, issued, voided |
void_reason |
Text | |
notes |
Html |
Model: fp.thickness.reading — Fischerscope Measurement Data
| Field | Type | Description |
|---|---|---|
certificate_id |
Many2one fp.certificate | Parent certificate (cascade) |
production_id |
Many2one mrp.production | Link to MO (independent of cert) |
reading_number |
Integer | Reading sequence (n=1, n=2, n=3) |
nip_mils |
Float(10,4) | NiP thickness in mils |
ni_percent |
Float(6,3) | Nickel content % |
p_percent |
Float(6,4) | Phosphorus content % |
position_label |
Char | Where on the part this reading was taken |
equipment_model |
Char | e.g. "Fischerscope XDAL 600" |
product_ref |
Char | e.g. "2805031 / NiP/Al-alloys 2805030" |
calibration_std_ref |
Char | e.g. "NiP/Al STD SET SN 100174568" |
microscope_image_id |
Many2one ir.attachment | Microscope photo |
operator_id |
Many2one res.users | Who took the reading |
reading_datetime |
Datetime | When reading was taken |
measuring_time_seconds |
Integer | e.g. 120 |
Statistical fields (computed from reading lines per certificate):
mean_nip_mils,stddev_nip_mils,cov_percent,range_nip_mils— computed onfp.certificatefrom itsthickness_reading_ids
Auto-Creation
When CoC or thickness report is generated, fp.certificate record auto-created with PDF attached.
Views
- List (default, newest first): Issue Date, Cert #, Type, Customer, Part #, PO #, Entech WO#, Process, Qty, Issued By, Status
- Search: Quick filters by Customer, Certificate Type, Date Range, Part Number, PO Number. Group by: Customer, Type, Month, Issued By.
- Form: Certificate type badge, state buttons (Issue/Void), customer info, part details, tabs for Thickness Readings, Attachments, Notes. "Regenerate PDF" and "Send to Customer" buttons.
CoC Bilingual Implementation
The bilingual EN/FR certification statement uses QWeb template logic with t-if on a bilingual flag (default: True for Canadian compliance). The English and French text blocks are both rendered in the same template — not using Odoo's ir.translation system, since both languages must appear on the same document simultaneously.
8. fusion_tasks (Entech Plating) — Local Delivery Dispatch
Fork & Strip Strategy
Remove:
- Cross-instance sync (fusion.task.sync.config, shadow tasks)
fusion_claims.*config parameters → rename tofusion_tasks.*sales_teamdependency- Irrelevant task types (repair, troubleshoot, assessment, ltc_visit, maintenance, installation)
- All sync-related fields (x_fc_sync_*)
Keep:
- Google Maps integration (Leaflet.js map view)
- GPS tracking (fusion.technician.location → fusion.driver.location)
- Geocoding (_geocode_address())
- Route planning / scheduling / conflict avoidance
- Push notifications (fusion.push.subscription)
- Map view JS/SCSS/XML
Adapted Model: fusion.delivery.task
Renamed from fusion.technician.task.
Task Types (reduced):
delivery— outbound deliverypickup— collect parts from customerreturn— return rejected/damaged partsrush— same-day urgent
Status Workflow:
pending→scheduled→en_route→delivered(orfailed)
Key Fields:
| Field | Type | Description |
|---|---|---|
delivery_id |
Many2one fusion.plating.delivery | Links to logistics |
sale_order_id |
Many2one sale.order | |
portal_job_id |
Many2one fusion.plating.portal.job | Uses canonical model name |
partner_id |
Many2one res.partner | Customer |
driver_id |
Many2one hr.employee | Renamed from technician_id |
vehicle_id |
Many2one fusion.plating.vehicle | From logistics module |
packages_count |
Integer | Number of boxes/crates |
weight_total |
Float | Total weight |
requires_signature |
Boolean | POD required |
requires_photo |
Boolean | Photo proof required |
coc_attachment_id |
Many2one ir.attachment | CoC to hand to customer |
Delivery Integration:
action_mark_delivered(): logs GPS + timestamp, captures signature/photo, updates fp.delivery → delivered, cascades to portal job → shipped, triggers shipment notification emailaction_mark_failed(): logs reason, creates follow-up activity
Dependencies & Deployment
'depends': ['base', 'mail', 'hr', 'fusion_plating_logistics'],
Deployment strategy: fusion_tasks lives in a separate repo (Entech Plating/) but is deployed to the SAME server as the fusion_plating modules. Both repos are copied to /mnt/extra-addons/custom/ on the target server. The fusion_plating_logistics dependency is therefore always available at install time. There is no standalone driver-only install scenario — drivers access the system via the same Odoo instance. The standalone "Delivery Dispatch" menu (below) provides a driver-focused view without needing a separate deployment.
Menu Placement
Inside Fusion Plating: Shipping & Delivery → Local Delivery Tasks, Driver Map
Standalone (optional): Delivery Dispatch app for drivers.
9. Complete End-to-End Workflow
Stage 1: Customer Inquiry
- Portal path: Customer uploads 3D/PDF on portal → sees estimated price → submits → fp.quote.request created
- Email/phone path: Estimator creates fp.quote.request manually, uploads customer's files
- Modules: fusion_plating_portal, fusion_plating_configurator
Stage 2: Quotation
- Estimator opens Configurator inside Fusion Plating app
- Selects/creates part in Part Catalog
- 3D model → auto surface area + 3D preview; PDF → manual measurements
- Selects Coating Configuration
- Pricing engine calculates from rules; estimator reviews/overrides
- Adds delivery/shipping fees, sets invoice strategy
- "Create Quotation" → sale.order with all x_fc_* fields
- Sends quotation → email notification with Quote PDF
- Modules: fusion_plating_configurator, fusion_plating_notifications
Stage 3: Order Confirmation
- Customer accepts + submits PO (email or portal)
- PO number entered on SO, PO document uploaded (or manager override for handshake deals)
- SO confirmed:
- Account hold check → blocked if hold active (manager override)
- Invoice strategy fires (deposit/COD → auto-invoice; net_terms/progress → no invoice yet)
- Email: Order Confirmation + SO PDF
- Receiving record auto-created (draft)
- Portal job auto-created (received)
- Modules: fusion_plating_invoicing, fusion_plating_receiving, fusion_plating_bridge_mrp, fusion_plating_notifications
Stage 4: Parts Receiving
- Parts arrive → receiver opens receiving record
- Counts parts, inspects condition
- Good → accepted; damage/mismatch → discrepancy → follow-up → resolved
- Email: Parts Received
- Portal job → in_progress
- Modules: fusion_plating_receiving, fusion_plating_notifications
Stage 5: Manufacturing Planning
- MO created from SO (standard sale_mrp)
- Receiving gate: warns if parts not received
- COD gate: warns if prepay not paid
- Planner assigns recipe, configures opt-in/out steps
- Recipe → Work Orders generated (one WO per operation node, steps = WO instructions)
- Modules: fusion_plating_bridge_mrp
Stage 6: Manufacturing Execution
- Operators work WOs on shopfloor (Plant Overview kanban, timers, bath/tank assignment)
- Quality holds if needed
- All WOs done → MO done:
- Portal job → ready_to_ship
- fp.delivery auto-created (draft)
- Thickness readings entered
- CoC + thickness report generated → fp.certificate records created
- Progress invoicing: if strategy=progress, invoice this MO's portion
- Modules: fusion_plating_shopfloor, fusion_plating_bridge_mrp, fusion_plating_quality, fusion_plating_invoicing
Stage 7: Shipping / Local Delivery
Shipping Partner (Purolator, FedEx, UPS, etc.):
- fp.delivery scheduled with carrier + tracking #
- Packing list generated, delivery marked shipped
- Module: fusion_plating_logistics
Local Delivery (EN Tech driver):
- fusion.delivery.task created, driver + vehicle assigned
- Driver Map shows live GPS tracking
- Driver delivers → signature/photo POD → cascades to fp.delivery
- Module: fusion_tasks (Entech Plating)
Customer Pickup:
- Email: Ready for Pickup
- Customer arrives → parts released → signature → fp.delivery marked delivered
All paths:
- Portal job → shipped
- Email: CoC + Thickness Report + Invoice + Tracking/ETA
- Modules: fusion_plating_logistics, fusion_tasks, fusion_plating_notifications
Stage 8: Invoicing & Payment
- Strategy determines timing:
- deposit → balance invoice after shipping
- progress → final invoice for remaining balance
- net_terms → full invoice after shipping
- cod_prepay → already invoiced & paid
- Delivery method change after invoice → supplementary invoice or credit note
- Invoice posted → portal job → complete → email with Invoice PDF
- Modules: fusion_plating_invoicing, fusion_plating_bridge_mrp, fusion_plating_notifications
Stage 9: Customer Portal
- Full job lifecycle visible: progress bar (received → complete)
- Documents tab: CoC, thickness report, invoice — downloadable
- Part catalog: saved parts with 3D preview
- Order history: past orders, re-order from catalog
- Quote request history, tracking info, notification history
- Modules: fusion_plating_portal
10. Module Dependency Graph
fusion_plating (core)
├── fusion_plating_configurator
│ └── depends: fusion_plating, sale_management
├── fusion_plating_receiving
│ └── depends: fusion_plating, sale_management
│ └── provides: mrp.production gate mixin (overrides action_confirm)
├── fusion_plating_invoicing
│ └── depends: fusion_plating, sale_management, account
│ └── provides: invoice strategy automation, account hold on res.partner
├── fusion_plating_notifications
│ └── depends: fusion_plating, fusion_plating_reports, mail
├── fusion_plating_certificates
│ └── depends: fusion_plating, fusion_plating_portal,
│ fusion_plating_reports, mrp
├── fusion_plating_bridge_mrp (lighter — gates live in receiving/invoicing)
│ └── depends: fusion_plating, fusion_plating_configurator, mrp
│ └── soft-depends: fusion_plating_receiving, fusion_plating_invoicing
├── fusion_plating_portal
│ └── depends: fusion_plating, fusion_plating_configurator,
│ fusion_plating_notifications, portal
├── fusion_plating_logistics
│ └── depends: fusion_plating
└── fusion_tasks (Entech Plating — separate repo, same server)
└── depends: fusion_plating_logistics, hr, mail
Note on bridge_mrp: The receiving gate and invoice strategy gates are implemented as lightweight mixins within fusion_plating_receiving and fusion_plating_invoicing respectively (each overrides mrp.production independently). This avoids funnelling all dependencies through bridge_mrp. Bridge_mrp focuses on recipe-to-WO generation and the configurator link.
11. Pricing Variables Reference
All 10 pricing variables that drive the configurator:
- Surface area — more area = more chemistry consumed
- Coating type — EN, chrome, anodize, black oxide (different bath costs)
- Thickness spec — more passes/dwell time
- Substrate material — aluminium needs zincate pre-treatment
- Quantity / batch size — more parts per rack = lower per-unit cost
- Part complexity — blind holes, recesses, masking areas
- Masking requirements — labour-intensive
- Spec / certification level — Nadcap/aerospace = more QC overhead
- Turnaround time — rush = premium
- Pre/post treatment — bead blast, bake, passivate
12. Key Architectural Decisions
| Decision | Resolution |
|---|---|
| Configurator primary user | Internal estimator; portal is simplified lead-gen |
| 3D file handling | STEP/STL auto surface area calc + 3D preview; PDF manual (Claude Vision roadmap) |
| Pricing model | Formula-calculated with estimator override |
| Part catalog | Customer part library for repeat business + one-off support |
| PO requirement | Required before manufacturing, but manager override available |
| Invoice strategies | All 4 supported (deposit, progress, net_terms, cod_prepay), configurable per order |
| Account hold | Manual for now, auto from aging on roadmap |
| Shipping decision | Set at quote time, changeable later with price adjustment |
| Local delivery | Fork fusion_tasks, strip claims, keep GPS/maps |
| Certificate management | Unified fp.certificate registry with filters, auto-creation on report generation |
| Recipe → WO mapping | One WO per operation node, steps become WO instructions |
13. Data Migration: Existing Quote Request Flow
The existing fusion.plating.quote.request model has an action_create_sale_order() method that creates basic SOs. The new configurator introduces a parallel, richer path.
Coexistence strategy:
- The existing
action_create_sale_order()onfusion.plating.quote.requestremains functional — it is the "quick path" for simple quotes that don't need the full configurator - The new configurator is the "full path" for detailed quotes with part catalog, coating config, and pricing rules
- When a quote request comes in via portal, the estimator chooses: use the configurator (creates
fp.quote.configurator→ SO) or use the quick path (existingaction_create_sale_order()) - Both paths create SOs with
x_fc_*fields. The quick path leaves configurator-specific fields blank; the full path populates everything - No existing data needs migration — the two paths coexist
14. Roadmap Items (Not in Initial Build)
- Claude Vision for PDF drawing measurement extraction
- Auto account hold computed from invoice aging
- Fischerscope CSV import (auto-populate thickness readings)
- Multi-driver route optimization
- Customer-specific certificate templates
- Product configurator on portal (dynamic pricing preview)
- Tags on recipe nodes
- Dashboard transitions on recipe nodes
- Treatment groups / choices on recipe nodes