From a1c479f9169ef97a18380c796a716a5031e35d35 Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Tue, 31 Mar 2026 19:35:02 -0400 Subject: [PATCH] Add Fusion WooOdoo design specification Complete design spec for bidirectional Odoo 19 <-> WooCommerce sync system covering product mapping, order lifecycle, inventory sync, customer portal, and all supporting data models. Addresses all review findings including variant support, tax/pricelist mapping models, return/RMA workflow, multi-shipment tracking, and Odoo 19 technical requirements. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../2026-03-31-fusion-woo-odoo-design.md | 677 ++++++++++++++++++ 1 file changed, 677 insertions(+) create mode 100644 docs/superpowers/specs/2026-03-31-fusion-woo-odoo-design.md diff --git a/docs/superpowers/specs/2026-03-31-fusion-woo-odoo-design.md b/docs/superpowers/specs/2026-03-31-fusion-woo-odoo-design.md new file mode 100644 index 00000000..48929be2 --- /dev/null +++ b/docs/superpowers/specs/2026-03-31-fusion-woo-odoo-design.md @@ -0,0 +1,677 @@ +# Fusion WooOdoo — Design Specification + +**Date:** 2026-03-31 +**Project:** Bidirectional Odoo 19 ↔ WooCommerce Sync System +**Components:** Odoo module (`fusion_woocommerce`) + WordPress plugin (`fusion-woodoo`) + +--- + +## 1. Overview + +A production-ready, plug-and-play integration between Odoo 19 and WooCommerce that syncs products, prices, inventory, orders, invoices, customers, and documents bidirectionally. Designed for non-technical users — install, enter API keys, and it works. + +**Two deployable components:** +- **fusion_woocommerce** (Odoo module) — the brain. All sync logic, product mapping, scheduling, conflict resolution, and dashboards. +- **fusion-woodoo** (WordPress plugin) — thin display layer. Receives data from Odoo, displays documents in customer portal, fires webhooks on events. + +## 2. Architecture + +### Communication Pattern: Direct REST API (Approach A) + +``` +Odoo ──── WooCommerce REST API ────→ WordPress +Odoo ←─── WooCommerce Webhooks ───── WordPress +Odoo ──── Custom REST endpoints ───→ WP Plugin (invoices, docs, status) +``` + +- **Odoo is the brain** — all sync logic, mapping, scheduling, conflict resolution +- **WordPress is the display layer** — receives data, displays to customers, fires webhooks +- **Deduplication via external IDs** — every synced record carries a unique reference +- **HTTPS only** — all API communication encrypted +- **Multi-site ready** — one Odoo instance can connect to multiple WooCommerce sites +- **Cross-server compatible** — works regardless of where Odoo and WordPress are hosted + +### Sync Strategy: Hybrid (Webhooks + Cron Fallback) + +- **Real-time webhooks** for orders, customer creation, product changes +- **Scheduled cron** runs at configurable intervals to catch missed webhooks +- **Manual "Sync Now" button** for on-demand sync +- No duplications — every record checked against external IDs before creation + +### Authentication: Bidirectional API Keys + +- **Odoo → WooCommerce:** WC REST API consumer key + consumer secret (both encrypted at rest) +- **WC Webhooks → Odoo:** Validated via HMAC signature (`X-WC-Webhook-Signature` header) using the WC webhook secret — NOT the Odoo API key +- **WP Plugin custom endpoints → Odoo:** Odoo API key stored in WP plugin settings, sent as bearer token +- These are two distinct auth flows — webhook signature validation vs. API key authentication + +### WooCommerce API Version + +- Target: **WC REST API v3** (current, recommended) +- All endpoints use `/wp-json/wc/v3/` prefix + +## 3. Data Models + +### 3.1 Odoo Models + +#### `woo.instance` — WooCommerce Site Connection + +| Field | Type | Purpose | +|-------|------|---------| +| `name` | Char | Display name (e.g., "Westin WooCommerce") | +| `url` | Char | WooCommerce site URL | +| `consumer_key` | Char | WC API consumer key (encrypted) | +| `consumer_secret` | Char | WC API consumer secret (encrypted) | +| `webhook_secret` | Char | WC webhook HMAC secret (encrypted) | +| `wc_api_version` | Char | WC API version (default: "wc/v3") | +| `company_id` | Many2one (res.company) | Company (multi-company support) | +| `odoo_api_key` | Char | API key for WP plugin to call back | +| `sync_interval` | Selection | 5min / 15min / 30min / 1hr | +| `sync_products` | Boolean | Enable product sync | +| `sync_orders` | Boolean | Enable order sync | +| `sync_invoices` | Boolean | Enable invoice sync | +| `sync_inventory` | Boolean | Enable inventory sync | +| `sync_customers` | Boolean | Enable customer sync | +| `default_warehouse_id` | Many2one (stock.warehouse) | Where WC orders pull stock | +| `notify_on_failure` | Boolean | Email on sync failure | +| `notify_user_ids` | Many2many (res.users) | Who gets notified | +| `state` | Selection | draft / connected / error | +| `last_sync` | Datetime | Last successful sync | + +#### `woo.product.map` — Product Mapping (1:1) + +| Field | Type | Purpose | +|-------|------|---------| +| `instance_id` | Many2one (woo.instance) | Which WC site | +| `product_id` | Many2one (product.product) | Odoo product | +| `woo_product_id` | Integer | WooCommerce product ID | +| `woo_product_name` | Char | WC product name (display) | +| `woo_sku` | Char | WC SKU | +| `woo_product_type` | Selection | simple / variable / grouped / external | +| `woo_parent_id` | Integer | WC parent product ID (for variations) | +| `is_variation` | Boolean | Whether this maps a WC variation | +| `sync_price` | Boolean | Sync price for this product | +| `sync_inventory` | Boolean | Sync stock for this product | +| `sync_images` | Boolean | Sync images for this product | +| `woo_image_ids` | Char | JSON list of WC media IDs for image tracking | +| `last_synced` | Datetime | Last sync time | +| `company_id` | Many2one (res.company) | Company | +| `state` | Selection | unmapped / mapped / conflict / error | + +#### `woo.order` — Synced Order Tracking + +| Field | Type | Purpose | +|-------|------|---------| +| `instance_id` | Many2one (woo.instance) | Which WC site | +| `sale_order_id` | Many2one (sale.order) | Odoo sales order | +| `woo_order_id` | Integer | WooCommerce order ID | +| `woo_order_number` | Char | WC order display number | +| `woo_status` | Char | Current WC status | +| `invoice_id` | Many2one (account.move) | Linked Odoo invoice | +| `invoice_synced` | Boolean | Whether invoice PDF was pushed to WC | +| `company_id` | Many2one (res.company) | Company | +| `state` | Selection | new / confirmed / shipped / completed / cancelled | + +#### `woo.shipment` — Per-Shipment Tracking (child of woo.order) + +| Field | Type | Purpose | +|-------|------|---------| +| `order_id` | Many2one (woo.order) | Parent order | +| `picking_id` | Many2one (stock.picking) | Odoo delivery order | +| `carrier_id` | Many2one (woo.shipping.carrier) | Shipping carrier | +| `tracking_number` | Char | Tracking number | +| `shipped_date` | Datetime | When shipped | +| `is_backorder` | Boolean | Whether this is a backorder shipment | +| `synced_to_woo` | Boolean | Whether WC was notified | + +#### `woo.shipping.carrier` — Configurable Carrier List + +| Field | Type | Purpose | +|-------|------|---------| +| `name` | Char | Carrier name (e.g., "Canada Post") | +| `code` | Char | Short code (e.g., "canada_post") | +| `tracking_url` | Char | Tracking URL template (e.g., "https://www.canadapost.ca/track/{tracking}") | +| `active` | Boolean | Whether enabled | + +#### `woo.sync.log` — Sync History + +| Field | Type | Purpose | +|-------|------|---------| +| `instance_id` | Many2one (woo.instance) | Which WC site | +| `sync_type` | Selection | product / order / invoice / inventory / customer | +| `direction` | Selection | odoo_to_woo / woo_to_odoo | +| `record_ref` | Char | Reference (e.g., "SO0042") | +| `state` | Selection | success / failed / conflict | +| `message` | Text | Details or error message | +| `create_date` | Datetime | Timestamp | + +#### `woo.customer` — Customer Link Tracking + +| Field | Type | Purpose | +|-------|------|---------| +| `instance_id` | Many2one (woo.instance) | Which WC site | +| `partner_id` | Many2one (res.partner) | Odoo partner | +| `woo_customer_id` | Integer | WooCommerce customer/user ID | +| `woo_email` | Char | WC customer email | +| `last_synced` | Datetime | Last sync time | +| `company_id` | Many2one (res.company) | Company | + +#### `woo.tax.map` — Tax Class Mapping + +| Field | Type | Purpose | +|-------|------|---------| +| `instance_id` | Many2one (woo.instance) | Which WC site | +| `tax_id` | Many2one (account.tax) | Odoo tax | +| `woo_tax_class` | Char | WC tax class slug | +| `woo_tax_class_name` | Char | WC tax class display name | +| `company_id` | Many2one (res.company) | Company | + +#### `woo.pricelist.map` — Price Tier Mapping + +| Field | Type | Purpose | +|-------|------|---------| +| `instance_id` | Many2one (woo.instance) | Which WC site | +| `pricelist_id` | Many2one (product.pricelist) | Odoo pricelist | +| `woo_role` | Char | WC customer role (e.g., "wholesale_customer") | +| `woo_role_name` | Char | WC role display name | +| `company_id` | Many2one (res.company) | Company | + +#### `woo.return` — Return/RMA Tracking + +| Field | Type | Purpose | +|-------|------|---------| +| `instance_id` | Many2one (woo.instance) | Which WC site | +| `order_id` | Many2one (woo.order) | Original synced order | +| `picking_id` | Many2one (stock.picking) | Odoo return picking | +| `reason` | Text | Customer's return reason | +| `line_ids` | One2many (woo.return.line) | Items being returned | +| `state` | Selection | requested / approved / received / refunded / rejected | +| `company_id` | Many2one (res.company) | Company | + +#### `woo.return.line` — Return Line Items + +| Field | Type | Purpose | +|-------|------|---------| +| `return_id` | Many2one (woo.return) | Parent return | +| `product_id` | Many2one (product.product) | Product being returned | +| `quantity` | Float | Quantity to return | +| `reason` | Selection | defective / wrong_item / not_needed / damaged / other | + +#### `woo.conflict` — Data Conflicts + +| Field | Type | Purpose | +|-------|------|---------| +| `instance_id` | Many2one (woo.instance) | Which WC site | +| `conflict_type` | Selection | product / customer / order | +| `map_id` | Many2one (woo.product.map) | Product map (if product conflict) | +| `customer_id` | Many2one (woo.customer) | Customer (if customer conflict) | +| `field_name` | Char | Which field conflicts | +| `odoo_value` | Char | Value in Odoo | +| `woo_value` | Char | Value in WooCommerce | +| `resolution` | Selection | pending / use_odoo / use_woo | +| `resolved_by` | Many2one (res.users) | Who resolved it | +| `company_id` | Many2one (res.company) | Company | + +### 3.2 WordPress Meta (No Custom Tables) + +| Meta Key | Stored On | Purpose | +|----------|-----------|---------| +| `_odoo_order_id` | Order | Linked Odoo sales order ID | +| `_odoo_invoice_id` | Order | Linked Odoo invoice ID | +| `_odoo_invoice_pdf` | Order | Invoice PDF stored in `wp-content/uploads/fusion-woodoo/invoices/` (access-controlled) | +| `_odoo_delivery_pdf` | Order | Delivery PDF stored in `wp-content/uploads/fusion-woodoo/deliveries/` (access-controlled) | +| `_odoo_tracking_number` | Order | Tracking number from Odoo | +| `_odoo_shipping_carrier` | Order | Carrier name | +| `_odoo_order_status` | Order | Odoo-side status for timeline | +| `_odoo_product_id` | Product | Linked Odoo product ID | +| `_odoo_customer_id` | User | Linked Odoo partner ID | +| `_odoo_instance_url` | Options | Odoo server URL | +| `_odoo_api_key` | Options | API key for Odoo auth | + +## 4. Sync Workflows + +### 4.1 Order Lifecycle (WooCommerce → Odoo → WooCommerce) + +1. Customer places order on WooCommerce +2. WC webhook fires → Odoo receives order +3. Odoo checks customer by email → match existing partner or create new +4. Odoo checks dedup by `woo_order_id` → skip if exists +5. Odoo creates `sale.order` with mapped products, quantities, prices +6. Odoo confirms sale order, creates draft invoice +7. Odoo pushes sales order details back to WC via REST API +8. User validates invoice in Odoo +9. User processes delivery in Odoo +10. User enters tracking number + selects shipping carrier +11. Odoo pushes tracking + carrier to WC → WC status: "shipped" → **Email #1: Shipping notification with tracking** +12. User marks order "Completed" in Odoo +13. Odoo pushes status to WC → WC status: "completed" → **Email #2: Order completed** +14. Odoo pushes invoice PDF + delivery receipt PDF to WC custom endpoint +15. Customer can view/download all documents from My Account + +### 4.2 Product Sync + +**Supported WC Product Types:** +- **Simple products** — standard 1:1 mapping to `product.product` +- **Variable products** — parent maps to `product.template`, each variation maps to a `product.product` variant +- **Grouped products** — mapped as a set of linked simple products +- **External/affiliate products** — excluded from sync (no inventory/price to sync) +- **Downloadable/virtual products** — mapped but inventory sync disabled automatically + +**Initial Setup:** +1. User clicks "Fetch WooCommerce Products" in Odoo +2. Odoo calls WC REST API → GET /products (paginated, includes variations) +3. For variable products: fetch `/products/{id}/variations` to get all variants +4. Auto-match by SKU (`internal_reference` = `sku`) → auto-map (matches both products and variants) +5. Suggest by name similarity for unmatched → user confirms/rejects +6. Remaining unmatched shown with actions: "Create in Odoo" / "Create in WooCommerce" / "Ignore" + +**Ongoing Sync (cron):** +1. For each mapped product with `sync_price` enabled: + - Compare Odoo price vs WC price + - Same → skip + - Changed on one side → update the other + - Changed on both → create `woo.conflict` record +2. For each mapped product with `sync_inventory` enabled: + - Push Odoo `qty_available` to WC `stock_quantity` + +**Ongoing Sync (webhook):** +1. WC fires `product.updated` webhook +2. Odoo finds mapping by `woo_product_id` +3. Updates Odoo product or creates conflict + +### 4.3 Customer Sync + +1. Order webhook arrives with customer email +2. Search `res.partner` by email + - Found → link, update address if changed + - Not found → create with name, email, phone, billing/shipping addresses, tag "WooCommerce Customer" +3. Store `odoo_customer_id` as WC user meta +4. Ongoing: address changes flow Odoo → WooCommerce (Odoo is master) + +### 4.4 Inventory Sync + +**Triggered by:** stock move confirmed in Odoo, or cron fallback + +1. For each mapped product with `sync_inventory` enabled +2. Get Odoo `qty_available` for configured warehouse +3. PUT to WC REST API → update `stock_quantity` +4. Log to `woo.sync.log` + +**Backorder Handling:** +1. Odoo creates partial delivery → push shipped items with tracking +2. Remaining items ship later → push second tracking update +3. Customer sees both shipments in portal timeline + +### 4.5 Price Tier Sync + +- Configured via `woo.pricelist.map` model — maps Odoo pricelists to WC customer roles +- When a WC customer with a specific role (e.g., "wholesale_customer") places an order, Odoo uses the mapped pricelist for pricing +- When syncing prices to WC, if WC supports role-based pricing plugins (e.g., "Wholesale Prices"), push pricelist-specific prices +- Fallback: if no role mapping exists, use Odoo's default public pricelist +- Configurable per `woo.instance` + +### 4.6 Product Image Sync + +- Images sync bidirectionally for mapped products with `sync_images` enabled +- Odoo product images push to WC media library; WC media IDs stored in `woo_image_ids` on the map record +- WC images pull into Odoo as `ir.attachment` linked to the product +- Image order preserved (first image = WC featured image) +- On sync, compare image checksums to avoid re-uploading unchanged images + +### 4.7 Tax Mapping + +- Configured via `woo.tax.map` model — maps Odoo `account.tax` records to WC tax class slugs +- Supports Canadian HST/GST/PST configurations +- When creating orders from WC: look up WC tax class → find mapped Odoo tax → apply to SO lines +- When pushing products to WC: look up Odoo product's tax → find mapped WC tax class → set on WC product +- Configurable mapping table per `woo.instance` + +### 4.8 Return/RMA + +1. Customer clicks "Request Return" in WP My Account +2. Fills return form: selects order, selects items + quantities, selects reason (defective / wrong_item / not_needed / damaged / other), submits +3. WP plugin sends return request to Odoo custom endpoint +4. Odoo creates `woo.return` record (state: requested) +5. Odoo user reviews and approves/rejects the return +6. If approved: Odoo creates a reverse `stock.picking` (NOT `stock.return.picking` which is a transient wizard) linked to the `woo.return` +7. Status updates push back to WC → customer sees progress: requested → approved → received → refunded (or rejected) +8. When return is received and refund issued, Odoo creates credit note → maps to WC refund + +### 4.9 Refund Handling + +- WooCommerce native refunds (partial or full) sync to Odoo as credit notes on the linked invoice +- Odoo credit notes sync back to WC as refunds on the order +- Refund amounts and line items are tracked to prevent double-refunding +- Dedup key: `woo_order_id` + refund ID + +### 4.10 Communication History Sync + +- When Odoo pushes order status updates, it also pushes recent `mail.message` records from the sale.order's chatter +- Messages are sent as part of the order status push (same REST endpoint, included in payload) +- WP plugin stores messages as order meta (`_odoo_messages` — JSON array of {author, date, body}) +- Only internal notes marked "visible to customer" are synced — private internal notes stay in Odoo +- New messages pushed on each status update (incremental, not full history each time) + +### 4.11 Deduplication Rules + +| Record | Unique Key | Check Before Creating | +|--------|-----------|----------------------| +| Order | `woo_order_id` per instance | Always check `woo.order` | +| Customer | `email` | Always search `res.partner` by email | +| Product map | `woo_product_id` per instance | Always check `woo.product.map` | +| Invoice | `sale_order_id` | One invoice per SO | +| Sync log | N/A | Append-only | + +## 5. Product Mapping UI (Odoo) + +### Design Principles +- Modern, intuitive, production-grade +- Live AJAX search — instant filtering as user types (debounced ~300ms) +- No page reloads for search/filter operations + +### Layout + +**Top Bar:** +- Instance selector dropdown (which WC site) +- "Fetch Products" button (initial import) +- "Sync Now" button +- Stats bar: X mapped / Y unmatched / Z conflicts + +**Three Tabs:** + +**Tab 1: Mapped Products** +- Table: Odoo Product | SKU | WC Product | WC SKU | Price (Odoo) | Price (WC) | Stock | Status | Actions +- Live search bar filters both Odoo and WC product names/SKUs in real time +- Bulk actions: Unmap selected, Sync selected, Enable/disable price sync, Enable/disable inventory sync +- Conflict indicator (amber icon) on rows with price conflicts +- Click row to expand details + +**Tab 2: Unmatched Products** +- Split view: Odoo products on left, WC products on right +- Each side has its own live search bar +- Click to select from each side and map together (v1); drag-and-drop as a v2 enhancement +- Action buttons per product: "Create in WooCommerce" / "Create in Odoo" / "Ignore" +- Auto-suggest section at top showing name-based matches with confidence score + +**Tab 3: Conflicts** +- Table: Product | Field | Odoo Value | WC Value | Actions +- Actions: "Use Odoo Value" / "Use WC Value" / "Ignore" +- Bulk resolve: "Use all Odoo values" / "Use all WC values" + +### Search Behaviour +- Debounced at 300ms — starts filtering after user stops typing briefly +- Filters product name, SKU, and internal reference simultaneously +- No enter key required +- Results update in-place without page reload +- Built using Odoo 19 OWL framework with RPC calls to controller endpoints + +## 6. Customer Portal (WordPress — My Account) + +### New Tabs in My Account + +**Tab: Sales Orders** +- List of all Odoo-created sales orders +- Columns: Order #, Date, Items, Total, Status +- Click to view full order details +- "Reorder" button → creates new WC cart with same products (Odoo pushes WC product IDs per line in the order data so the plugin can build the cart directly) + +**Tab: Invoices** +- List of all Odoo invoices +- Columns: Invoice #, Date, Amount, Status (Draft/Paid) +- "Download PDF" button for each invoice +- PDF is the actual Odoo-generated invoice + +**Tab: Deliveries** +- List of all delivery/inventory transfer receipts +- Columns: Delivery #, Date, Carrier, Tracking #, Status +- "Download PDF" button for delivery receipt +- Tracking number is a clickable link to carrier's tracking page + +**Tab: Returns** +- List of return requests +- Columns: Return #, Date, Items, Status +- "Request Return" button to initiate new return +- Return form: select order, select items, reason, submit + +### Order Status Timeline + +Visual timeline/tracker on each order detail page: + +``` +● Confirmed → ● Processing → ● Shipped → ● Delivered → ● Completed + ✓ ✓ ✓ ○ ○ +``` + +- Green checkmark for completed stages +- Blue dot for current stage +- Grey dot for upcoming stages +- Each stage shows date/time when it occurred +- Shipping stage shows carrier + tracking number with link + +### Communication History + +- Messages/notes exchanged between customer and team about an order +- Displayed as a threaded conversation view +- Synced from Odoo chatter (`mail.message` on `sale.order`) — only messages marked visible to customer +- Stored as `_odoo_messages` order meta (JSON array) +- Pushed incrementally with each order status update + +## 7. Settings & Configuration + +### 7.1 Odoo Settings Page (fusion_woocommerce) + +**Connection Section:** +- WooCommerce URL (with "Test Connection" button) +- Consumer Key + Consumer Secret +- Auto-generated Odoo API Key (with "Regenerate" button) +- Connection status indicator (green/red) + +**Sync Configuration:** +- Sync interval dropdown (5min / 15min / 30min / 1hr) +- Toggle switches for each sync type: Products, Orders, Invoices, Inventory, Customers +- Default warehouse selector +- Price sync direction: Odoo → WC / WC → Odoo / Bidirectional + +**Shipping Carriers:** +- Enabled carriers checklist: Canada Post, UPS, FedEx, Purolator, DHL, Other +- "Other" allows custom carrier name + +**Tax Mapping:** +- Table: Odoo Tax | WC Tax Class | Actions +- Add/edit/remove mappings + +**Notifications:** +- Toggle: Email on sync failure +- User selector: who receives notifications +- Toggle: Email on new WC order received + +**Setup Wizard:** +- First-time setup walks user through: enter URL → enter API keys → test connection → select warehouse → fetch products → initial mapping +- No technical knowledge required + +### 7.2 WordPress Settings Page (fusion-woodoo) + +**Connection Section:** +- Odoo Instance URL +- API Key +- "Test Connection" button +- Status indicator + +**Portal Settings:** +- Toggle: Show Sales Orders tab +- Toggle: Show Invoices tab +- Toggle: Show Deliveries tab +- Toggle: Show Returns tab +- Toggle: Show Order Timeline +- Toggle: Enable Reorder button + +**Webhook Configuration:** +- Auto-configured on activation (registers WC webhooks pointing to Odoo) +- Display registered webhooks with status +- On plugin deactivation: unregister all WC webhooks pointing to Odoo +- On Odoo URL change: automatically re-register webhooks with new URL +- Stale webhook detection: if webhooks point to an unreachable Odoo URL, show warning + +## 8. Dashboard Widget (Odoo) + +An Odoo dashboard widget showing sync health at a glance: + +- **Orders pending sync:** count with link to list +- **Last sync time:** timestamp + "X minutes ago" +- **Errors in last 24h:** count with link to sync log (filtered) +- **Products mapped / unmapped:** ratio with progress bar +- **Inventory sync status:** last run + items synced +- **Quick actions:** Sync Now, View Conflicts, Open Mapping + +## 9. Folder Structure + +``` +fusion-woo-odoo/ +├── fusion_woocommerce/ # Odoo 19 module +│ ├── __init__.py +│ ├── __manifest__.py +│ ├── controllers/ +│ │ ├── __init__.py +│ │ ├── webhook.py # Receive WC webhooks +│ │ ├── api.py # REST API for WP plugin +│ │ └── product_search.py # AJAX search endpoints +│ ├── lib/ +│ │ ├── __init__.py +│ │ └── woo_api_client.py # WooCommerce REST API wrapper (NOT an Odoo model) +│ ├── models/ +│ │ ├── __init__.py +│ │ ├── woo_instance.py # WC site connection config +│ │ ├── woo_product_map.py # Product mapping +│ │ ├── woo_order.py # Order sync tracking +│ │ ├── woo_shipment.py # Per-shipment tracking +│ │ ├── woo_shipping_carrier.py # Configurable carrier list +│ │ ├── woo_customer.py # Customer link tracking +│ │ ├── woo_tax_map.py # Tax class mapping +│ │ ├── woo_pricelist_map.py # Price tier mapping +│ │ ├── woo_return.py # Return/RMA tracking + lines +│ │ ├── woo_sync_log.py # Sync history +│ │ ├── woo_conflict.py # Conflict resolution +│ │ ├── sale_order.py # sale.order extensions +│ │ ├── stock_picking.py # Delivery extensions (tracking) +│ │ ├── account_move.py # Invoice extensions +│ │ └── res_partner.py # Partner extensions (WC link) +│ ├── views/ +│ │ ├── woo_instance_views.xml # Settings / connection config +│ │ ├── woo_product_map_views.xml # Product mapping UI +│ │ ├── woo_order_views.xml # Order tracking views +│ │ ├── woo_sync_log_views.xml # Sync log views +│ │ ├── woo_conflict_views.xml # Conflict resolution views +│ │ ├── woo_dashboard.xml # Dashboard widget +│ │ ├── sale_order_views.xml # SO view extensions +│ │ ├── stock_picking_views.xml # Delivery view extensions +│ │ └── res_config_settings.xml # Settings page +│ ├── wizard/ +│ │ ├── __init__.py +│ │ ├── woo_setup_wizard.py # First-time setup wizard +│ │ ├── woo_setup_wizard_views.xml +│ │ ├── woo_product_fetch.py # Fetch & auto-match wizard +│ │ └── woo_product_fetch_views.xml +│ ├── data/ +│ │ ├── cron.xml # Scheduled sync jobs + log cleanup cron +│ │ ├── mail_template.xml # Notification email templates +│ │ └── shipping_carriers.xml # Default carrier data (Canada Post, UPS, FedEx, Purolator, DHL) +│ ├── security/ +│ │ ├── ir.model.access.csv # Access rights +│ │ └── woo_security.xml # Record rules & groups +│ ├── static/ +│ │ ├── description/ +│ │ │ └── icon.png # Module icon (placeholder) +│ │ └── src/ +│ │ ├── js/ +│ │ │ ├── product_mapping.js # OWL component: mapping UI +│ │ │ ├── ajax_search.js # Live search component +│ │ │ └── dashboard.js # Dashboard widget +│ │ ├── css/ +│ │ │ └── woo_styles.css # Custom styles +│ │ └── xml/ +│ │ ├── product_mapping.xml # OWL templates +│ │ └── dashboard.xml # Dashboard template +│ └── i18n/ +│ └── (translation files) +│ +└── fusion-woodoo/ # WordPress plugin + ├── fusion-woodoo.php # Plugin entry point + ├── includes/ + │ ├── class-fusion-woodoo.php # Main plugin class + │ ├── class-api-client.php # Odoo API client + │ ├── class-rest-endpoints.php # Custom REST endpoints (receive from Odoo) + │ ├── class-webhooks.php # WC webhook registration + │ ├── class-my-account.php # My Account tab registration + │ ├── class-order-timeline.php # Order status timeline + │ ├── class-returns.php # Return/RMA handling + │ └── class-admin-settings.php # WP admin settings page + ├── templates/ + │ ├── my-account/ + │ │ ├── sales-orders.php # Sales orders tab + │ │ ├── invoices.php # Invoices tab + │ │ ├── deliveries.php # Deliveries tab + │ │ ├── returns.php # Returns tab + │ │ ├── order-timeline.php # Status timeline component + │ │ └── communication.php # Communication history + │ └── admin/ + │ └── settings.php # Admin settings page + ├── assets/ + │ ├── css/ + │ │ ├── my-account.css # Customer portal styles + │ │ └── admin.css # Admin styles + │ ├── js/ + │ │ ├── my-account.js # Portal interactions + │ │ └── admin.js # Admin JS (test connection, etc.) + │ └── images/ + │ └── icon.png # Plugin icon (placeholder) + ├── languages/ + │ └── fusion-woodoo.pot # Translation template + └── readme.txt # WordPress plugin readme +``` + +## 10. Error Handling & Reliability + +- **Retry logic:** Failed API calls retry up to 3 times with exponential backoff +- **Queue system:** Failed syncs are queued and retried on next cron run +- **Graceful degradation:** If WC site is down, Odoo continues working normally; queued syncs process when connection restores +- **Sync log:** Every operation logged with success/failure status and error details +- **Email alerts:** Configurable notifications on sync failures +- **Connection health check:** Periodic ping to verify WC site is reachable +- **Sync log retention:** Cron purges logs older than 90 days (configurable). Keeps error logs for 180 days. +- **PDF file access control:** Invoice and delivery PDFs stored in protected directories with `.htaccess` deny rules. Served via PHP handler that validates the requesting user owns the order. + +## 11. Security + +- API keys stored encrypted in both systems +- HTTPS required for all API communication +- Odoo API key validated on every incoming request from WP +- WC consumer key/secret validated on every outgoing request from Odoo +- WordPress nonce verification on all admin AJAX calls +- Rate limiting on webhook endpoints to prevent abuse +- Input sanitization on all data received from external systems +- WC webhook signature validation via HMAC-SHA256 (separate from API key auth) +- PDF files served through access-controlled PHP handler, not direct file URLs + +## 12a. Odoo 19 Technical Requirements + +- All HTTP controller routes must use `type="jsonrpc"` (NOT `type="json"` which is deprecated) +- Frontend JS uses `Interaction` class from `@web/public/interaction` +- Backend OWL components use standalone `rpc()` from `@web/core/network/rpc` +- OWL components: `static props = []` not `{}` +- API client class in `lib/` directory (NOT in `models/` — would cause ORM registration errors) +- All custom models include `company_id` field for multi-company support + +## 12. Multi-Currency Support + +- Odoo currency syncs to WooCommerce product prices +- Exchange rates managed in Odoo (existing currency rate feature) +- Products can have different prices per currency +- Invoice amounts respect the order's original currency + +## 13. Target Deployments + +| Pairing | Odoo | WordPress | +|---------|------|-----------| +| Westin | odoo-westin (192.168.1.40, VM101) | westinhealthcare.ca (192.168.1.152, VM301) | +| Mobility | odoo-mobility (192.168.1.102, VM115) | mobilityspecialties.ca (192.168.1.153, VM305) | +| Any third party | Any Odoo 19 instance | Any WooCommerce site |