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) <noreply@anthropic.com>
This commit is contained in:
677
docs/superpowers/specs/2026-03-31-fusion-woo-odoo-design.md
Normal file
677
docs/superpowers/specs/2026-03-31-fusion-woo-odoo-design.md
Normal file
@@ -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 |
|
||||
Reference in New Issue
Block a user