Add company_id to woo.shipment, woo.sync.log, woo.return.line. Add order_id to woo.conflict for order-type conflicts. Add _odoo_messages to WP meta table. Fix section numbering. Document lib/ import path. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
31 KiB
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-Signatureheader) 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 |
company_id |
Many2one (res.company) | Company |
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 |
company_id |
Many2one (res.company) | Company |
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 |
company_id |
Many2one (res.company) | Company |
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) |
order_id |
Many2one (woo.order) | Order (if order 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_messages |
Order | JSON array of customer-visible messages [{author, date, body}] |
_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)
- Customer places order on WooCommerce
- WC webhook fires → Odoo receives order
- Odoo checks customer by email → match existing partner or create new
- Odoo checks dedup by
woo_order_id→ skip if exists - Odoo creates
sale.orderwith mapped products, quantities, prices - Odoo confirms sale order, creates draft invoice
- Odoo pushes sales order details back to WC via REST API
- User validates invoice in Odoo
- User processes delivery in Odoo
- User enters tracking number + selects shipping carrier
- Odoo pushes tracking + carrier to WC → WC status: "shipped" → Email #1: Shipping notification with tracking
- User marks order "Completed" in Odoo
- Odoo pushes status to WC → WC status: "completed" → Email #2: Order completed
- Odoo pushes invoice PDF + delivery receipt PDF to WC custom endpoint
- 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 aproduct.productvariant - 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:
- User clicks "Fetch WooCommerce Products" in Odoo
- Odoo calls WC REST API → GET /products (paginated, includes variations)
- For variable products: fetch
/products/{id}/variationsto get all variants - Auto-match by SKU (
internal_reference=sku) → auto-map (matches both products and variants) - Suggest by name similarity for unmatched → user confirms/rejects
- Remaining unmatched shown with actions: "Create in Odoo" / "Create in WooCommerce" / "Ignore"
Ongoing Sync (cron):
- For each mapped product with
sync_priceenabled:- Compare Odoo price vs WC price
- Same → skip
- Changed on one side → update the other
- Changed on both → create
woo.conflictrecord
- For each mapped product with
sync_inventoryenabled:- Push Odoo
qty_availableto WCstock_quantity
- Push Odoo
Ongoing Sync (webhook):
- WC fires
product.updatedwebhook - Odoo finds mapping by
woo_product_id - Updates Odoo product or creates conflict
4.3 Customer Sync
- Order webhook arrives with customer email
- Search
res.partnerby email- Found → link, update address if changed
- Not found → create with name, email, phone, billing/shipping addresses, tag "WooCommerce Customer"
- Store
odoo_customer_idas WC user meta - Ongoing: address changes flow Odoo → WooCommerce (Odoo is master)
4.4 Inventory Sync
Triggered by: stock move confirmed in Odoo, or cron fallback
- For each mapped product with
sync_inventoryenabled - Get Odoo
qty_availablefor configured warehouse - PUT to WC REST API → update
stock_quantity - Log to
woo.sync.log
Backorder Handling:
- Odoo creates partial delivery → push shipped items with tracking
- Remaining items ship later → push second tracking update
- Customer sees both shipments in portal timeline
4.5 Price Tier Sync
- Configured via
woo.pricelist.mapmodel — 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_imagesenabled - Odoo product images push to WC media library; WC media IDs stored in
woo_image_idson the map record - WC images pull into Odoo as
ir.attachmentlinked 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.mapmodel — maps Odooaccount.taxrecords 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
- Customer clicks "Request Return" in WP My Account
- Fills return form: selects order, selects items + quantities, selects reason (defective / wrong_item / not_needed / damaged / other), submits
- WP plugin sends return request to Odoo custom endpoint
- Odoo creates
woo.returnrecord (state: requested) - Odoo user reviews and approves/rejects the return
- If approved: Odoo creates a reverse
stock.picking(NOTstock.return.pickingwhich is a transient wizard) linked to thewoo.return - Status updates push back to WC → customer sees progress: requested → approved → received → refunded (or rejected)
- 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.messagerecords 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.messageonsale.order) — only messages marked visible to customer - Stored as
_odoo_messagesorder 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
.htaccessdeny 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
12. Odoo 19 Technical Requirements
- All HTTP controller routes must use
type="jsonrpc"(NOTtype="json"which is deprecated) - Frontend JS uses
Interactionclass 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 inmodels/— would cause ORM registration errors). Import viafrom ..lib.woo_api_client import WooApiClient - All custom models include
company_idfield for multi-company support
13. 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
14. 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 |