# Fusion WooOdoo Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Build a production-ready bidirectional sync system between Odoo 19 and WooCommerce with product mapping, order lifecycle management, inventory sync, and customer portal. **Architecture:** Odoo module (brain) communicates with WooCommerce via REST API v3. WordPress plugin (thin layer) receives data from Odoo and displays documents in customer My Account portal. Hybrid sync: real-time webhooks + scheduled cron fallback. **Tech Stack:** Python 3 / Odoo 19 OWL / WooCommerce REST API v3 / PHP 8 / WordPress Plugin API / WooCommerce hooks **Spec:** `docs/superpowers/specs/2026-03-31-fusion-woo-odoo-design.md` **Critical Odoo 19 Rules:** - HTTP routes: `type="jsonrpc"` (NOT `type="json"`) - Frontend JS: `Interaction` class from `@web/public/interaction` - Backend OWL: standalone `rpc()` from `@web/core/network/rpc`, `static props = []` - Always read reference files from Docker before coding: `docker exec odoo-dev-app cat /usr/lib/python3/dist-packages/odoo/addons//...` - API client in `lib/` directory, NOT `models/` --- ## Phase 1: Foundation — Odoo Module Scaffold + Core Models ### Task 1: Create module scaffold and manifest **Files:** - Create: `fusion-woo-odoo/fusion_woocommerce/__init__.py` - Create: `fusion-woo-odoo/fusion_woocommerce/__manifest__.py` - Create: `fusion-woo-odoo/fusion_woocommerce/models/__init__.py` - Create: `fusion-woo-odoo/fusion_woocommerce/controllers/__init__.py` - Create: `fusion-woo-odoo/fusion_woocommerce/wizard/__init__.py` - Create: `fusion-woo-odoo/fusion_woocommerce/lib/__init__.py` - Create: `fusion-woo-odoo/fusion_woocommerce/security/ir.model.access.csv` - Create: `fusion-woo-odoo/fusion_woocommerce/security/woo_security.xml` - Create: `fusion-woo-odoo/fusion_woocommerce/static/description/icon.png` (placeholder) - [ ] **Step 1: Create directory structure** ```bash mkdir -p fusion-woo-odoo/fusion_woocommerce/{models,controllers,wizard,lib,views,data,security,static/{description,src/{js,css,xml}},i18n} ``` - [ ] **Step 2: Create `__manifest__.py`** ```python { 'name': 'Fusion WooCommerce', 'version': '19.0.1.0.0', 'category': 'Sales', 'summary': 'Bidirectional WooCommerce ↔ Odoo sync for products, orders, invoices, and inventory', 'description': 'Seamless integration between Odoo and WooCommerce. Sync products, prices, inventory, orders, invoices, customers, and documents bidirectionally.', 'author': 'Fusion Central', 'website': 'https://fusionsoft.ca', 'license': 'LGPL-3', 'depends': ['sale_management', 'stock', 'account', 'contacts', 'mail'], 'data': [ 'security/woo_security.xml', 'security/ir.model.access.csv', 'data/shipping_carriers.xml', 'data/cron.xml', 'data/mail_template.xml', 'views/woo_instance_views.xml', 'views/woo_product_map_views.xml', 'views/woo_order_views.xml', 'views/woo_sync_log_views.xml', 'views/woo_conflict_views.xml', 'views/woo_customer_views.xml', 'views/woo_return_views.xml', 'views/woo_tax_map_views.xml', 'views/woo_pricelist_map_views.xml', 'views/woo_shipping_carrier_views.xml', 'views/sale_order_views.xml', 'views/stock_picking_views.xml', 'views/res_config_settings.xml', 'views/woo_dashboard.xml', 'views/woo_menus.xml', 'wizard/woo_setup_wizard_views.xml', 'wizard/woo_product_fetch_views.xml', ], 'assets': { 'web.assets_backend': [ 'fusion_woocommerce/static/src/css/woo_styles.css', 'fusion_woocommerce/static/src/js/product_mapping.js', 'fusion_woocommerce/static/src/js/ajax_search.js', 'fusion_woocommerce/static/src/js/dashboard.js', 'fusion_woocommerce/static/src/xml/product_mapping.xml', 'fusion_woocommerce/static/src/xml/dashboard.xml', ], }, 'images': ['static/description/icon.png'], 'installable': True, 'application': True, 'auto_install': False, } ``` - [ ] **Step 3: Create root `__init__.py`** ```python from . import models from . import controllers from . import wizard from . import lib ``` - [ ] **Step 4: Create placeholder icon** Create a simple 128x128 PNG placeholder at `static/description/icon.png`. - [ ] **Step 5: Create security group XML** `security/woo_security.xml`: ```xml WooCommerce 50 WooCommerce User WooCommerce Manager ``` - [ ] **Step 6: Create ir.model.access.csv (initial — will expand as models are added)** ```csv id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_woo_instance_user,woo.instance.user,model_woo_instance,group_woo_user,1,0,0,0 access_woo_instance_manager,woo.instance.manager,model_woo_instance,group_woo_manager,1,1,1,1 ``` - [ ] **Step 7: Commit** ```bash git add fusion-woo-odoo/ git commit -m "feat: scaffold fusion_woocommerce Odoo module with manifest and security" ``` --- ### Task 2: WooCommerce API Client (`lib/`) **Files:** - Create: `fusion-woo-odoo/fusion_woocommerce/lib/__init__.py` - Create: `fusion-woo-odoo/fusion_woocommerce/lib/woo_api_client.py` This is a plain Python class, NOT an Odoo model. It wraps the WooCommerce REST API v3. - [ ] **Step 1: Create `lib/__init__.py`** ```python from .woo_api_client import WooApiClient ``` - [ ] **Step 2: Write `woo_api_client.py`** ```python import hashlib import hmac import json import logging import time from urllib.parse import urljoin import requests _logger = logging.getLogger(__name__) class WooApiClient: """WooCommerce REST API v3 client wrapper.""" def __init__(self, url, consumer_key, consumer_secret, api_version='wc/v3', timeout=30): self.base_url = url.rstrip('/') self.consumer_key = consumer_key self.consumer_secret = consumer_secret self.api_version = api_version self.timeout = timeout self.session = requests.Session() self.session.auth = (consumer_key, consumer_secret) self.session.headers.update({ 'Content-Type': 'application/json', 'User-Agent': 'FusionWooCommerce/1.0', }) def _url(self, endpoint): return f"{self.base_url}/wp-json/{self.api_version}/{endpoint.lstrip('/')}" def _request(self, method, endpoint, data=None, params=None, retries=3): url = self._url(endpoint) for attempt in range(retries): try: response = self.session.request( method, url, json=data, params=params, timeout=self.timeout ) response.raise_for_status() return response.json() except requests.exceptions.RequestException as e: _logger.warning( "WC API %s %s attempt %d failed: %s", method, endpoint, attempt + 1, str(e) ) if attempt < retries - 1: time.sleep(2 ** attempt) # exponential backoff else: raise def get(self, endpoint, params=None): return self._request('GET', endpoint, params=params) def post(self, endpoint, data): return self._request('POST', endpoint, data=data) def put(self, endpoint, data): return self._request('PUT', endpoint, data=data) def delete(self, endpoint): return self._request('DELETE', endpoint) # --- Product endpoints --- def get_products(self, page=1, per_page=100, **kwargs): params = {'page': page, 'per_page': per_page, **kwargs} return self.get('products', params=params) def get_product(self, product_id): return self.get(f'products/{product_id}') def get_product_variations(self, product_id, page=1, per_page=100): params = {'page': page, 'per_page': per_page} return self.get(f'products/{product_id}/variations', params=params) def update_product(self, product_id, data): return self.put(f'products/{product_id}', data) def create_product(self, data): return self.post('products', data) # --- Order endpoints --- def get_orders(self, page=1, per_page=100, **kwargs): params = {'page': page, 'per_page': per_page, **kwargs} return self.get('orders', params=params) def get_order(self, order_id): return self.get(f'orders/{order_id}') def update_order(self, order_id, data): return self.put(f'orders/{order_id}', data) # --- Customer endpoints --- def get_customers(self, page=1, per_page=100, **kwargs): params = {'page': page, 'per_page': per_page, **kwargs} return self.get('customers', params=params) def get_customer(self, customer_id): return self.get(f'customers/{customer_id}') def create_customer(self, data): return self.post('customers', data) def update_customer(self, customer_id, data): return self.put(f'customers/{customer_id}', data) # --- Webhook endpoints --- def create_webhook(self, data): return self.post('webhooks', data) def get_webhooks(self): return self.get('webhooks', params={'per_page': 100}) def delete_webhook(self, webhook_id): return self.delete(f'webhooks/{webhook_id}') # --- Tax endpoints --- def get_tax_classes(self): return self.get('taxes/classes') # --- Utility --- def test_connection(self): """Test if the WC API is reachable and credentials are valid.""" try: result = self.get('system_status') return True, result.get('environment', {}).get('version', 'unknown') except Exception as e: return False, str(e) @staticmethod def verify_webhook_signature(payload, signature, secret): """Verify WC webhook HMAC-SHA256 signature.""" expected = hmac.new( secret.encode('utf-8'), payload, hashlib.sha256 ).digest() import base64 expected_b64 = base64.b64encode(expected).decode('utf-8') return hmac.compare_digest(expected_b64, signature) ``` - [ ] **Step 3: Commit** ```bash git add fusion-woo-odoo/fusion_woocommerce/lib/ git commit -m "feat: add WooCommerce REST API v3 client wrapper" ``` --- ### Task 3: Core Odoo models — `woo.instance` and `woo.shipping.carrier` **Files:** - Create: `fusion-woo-odoo/fusion_woocommerce/models/woo_instance.py` - Create: `fusion-woo-odoo/fusion_woocommerce/models/woo_shipping_carrier.py` - Modify: `fusion-woo-odoo/fusion_woocommerce/models/__init__.py` - [ ] **Step 1: Create `models/__init__.py`** ```python from . import woo_shipping_carrier from . import woo_instance ``` - [ ] **Step 2: Write `woo_shipping_carrier.py`** Model for configurable shipping carriers. Must be created before `woo.instance` since other models reference it. ```python from odoo import models, fields class WooShippingCarrier(models.Model): _name = 'woo.shipping.carrier' _description = 'WooCommerce Shipping Carrier' _order = 'name' name = fields.Char(string='Carrier Name', required=True) code = fields.Char(string='Code', required=True) tracking_url = fields.Char( string='Tracking URL Template', help='Use {tracking} as placeholder. E.g., https://www.canadapost.ca/track/{tracking}' ) active = fields.Boolean(default=True) ``` - [ ] **Step 3: Write `woo_instance.py`** Full `woo.instance` model per spec. Include `test_connection` method and `_generate_api_key` helper. Fields for all sync toggles, warehouse, notifications. Encrypted fields for API keys. ```python import secrets import logging from odoo import models, fields, api, _ from odoo.exceptions import UserError from ..lib.woo_api_client import WooApiClient _logger = logging.getLogger(__name__) class WooInstance(models.Model): _name = 'woo.instance' _description = 'WooCommerce Instance' _inherit = ['mail.thread'] _order = 'name' name = fields.Char(string='Instance Name', required=True, tracking=True) url = fields.Char(string='WooCommerce URL', required=True, tracking=True, help='Full URL of the WooCommerce site, e.g., https://example.com') consumer_key = fields.Char(string='Consumer Key', required=True, groups='base.group_system') consumer_secret = fields.Char(string='Consumer Secret', required=True, groups='base.group_system') webhook_secret = fields.Char(string='Webhook Secret', groups='base.group_system', help='HMAC secret for verifying WooCommerce webhook signatures') wc_api_version = fields.Char(string='API Version', default='wc/v3') odoo_api_key = fields.Char(string='Odoo API Key', groups='base.group_system', help='API key for the WordPress plugin to authenticate with Odoo') company_id = fields.Many2one('res.company', string='Company', required=True, default=lambda self: self.env.company) # Sync configuration sync_interval = fields.Selection([ ('5', '5 Minutes'), ('15', '15 Minutes'), ('30', '30 Minutes'), ('60', '1 Hour'), ], string='Sync Interval', default='15') sync_products = fields.Boolean(string='Sync Products', default=True) sync_orders = fields.Boolean(string='Sync Orders', default=True) sync_invoices = fields.Boolean(string='Sync Invoices', default=True) sync_inventory = fields.Boolean(string='Sync Inventory', default=True) sync_customers = fields.Boolean(string='Sync Customers', default=True) default_warehouse_id = fields.Many2one('stock.warehouse', string='Default Warehouse') # Notifications notify_on_failure = fields.Boolean(string='Email on Sync Failure') notify_user_ids = fields.Many2many('res.users', string='Notification Recipients') # Status state = fields.Selection([ ('draft', 'Draft'), ('connected', 'Connected'), ('error', 'Error'), ], string='Status', default='draft', tracking=True) last_sync = fields.Datetime(string='Last Successful Sync', readonly=True) # Relational product_map_ids = fields.One2many('woo.product.map', 'instance_id', string='Product Mappings') order_ids = fields.One2many('woo.order', 'instance_id', string='Synced Orders') customer_ids = fields.One2many('woo.customer', 'instance_id', string='Synced Customers') sync_log_ids = fields.One2many('woo.sync.log', 'instance_id', string='Sync Logs') # Computed mapped_count = fields.Integer(compute='_compute_counts', string='Mapped Products') unmapped_count = fields.Integer(compute='_compute_counts', string='Unmatched Products') error_count = fields.Integer(compute='_compute_counts', string='Errors (24h)') @api.depends('product_map_ids.state', 'sync_log_ids') def _compute_counts(self): for rec in self: rec.mapped_count = self.env['woo.product.map'].search_count([ ('instance_id', '=', rec.id), ('state', '=', 'mapped') ]) rec.unmapped_count = self.env['woo.product.map'].search_count([ ('instance_id', '=', rec.id), ('state', '=', 'unmapped') ]) twenty_four_ago = fields.Datetime.subtract(fields.Datetime.now(), hours=24) rec.error_count = self.env['woo.sync.log'].search_count([ ('instance_id', '=', rec.id), ('state', '=', 'failed'), ('create_date', '>=', twenty_four_ago), ]) def _get_client(self): """Return a WooApiClient instance for this connection.""" self.ensure_one() return WooApiClient( url=self.url, consumer_key=self.consumer_key, consumer_secret=self.consumer_secret, api_version=self.wc_api_version, ) def action_test_connection(self): """Test the WooCommerce API connection.""" self.ensure_one() client = self._get_client() success, info = client.test_connection() if success: self.state = 'connected' return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': _('Connection Successful'), 'message': _('Connected to WooCommerce %s') % info, 'type': 'success', 'sticky': False, }, } else: self.state = 'error' raise UserError(_('Connection failed: %s') % info) def action_generate_api_key(self): """Generate a new Odoo API key for the WordPress plugin.""" self.ensure_one() self.odoo_api_key = secrets.token_urlsafe(32) return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': _('API Key Generated'), 'message': _('New API key generated. Copy it to the WordPress plugin settings.'), 'type': 'info', 'sticky': False, }, } def _log_sync(self, sync_type, direction, record_ref, state, message=''): """Create a sync log entry.""" self.ensure_one() self.env['woo.sync.log'].sudo().create({ 'instance_id': self.id, 'sync_type': sync_type, 'direction': direction, 'record_ref': record_ref, 'state': state, 'message': message, 'company_id': self.company_id.id, }) ``` - [ ] **Step 4: Update `ir.model.access.csv` with carrier model access** ```csv id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_woo_instance_user,woo.instance.user,model_woo_instance,group_woo_user,1,0,0,0 access_woo_instance_manager,woo.instance.manager,model_woo_instance,group_woo_manager,1,1,1,1 access_woo_shipping_carrier_user,woo.shipping.carrier.user,model_woo_shipping_carrier,group_woo_user,1,0,0,0 access_woo_shipping_carrier_manager,woo.shipping.carrier.manager,model_woo_shipping_carrier,group_woo_manager,1,1,1,1 ``` - [ ] **Step 5: Create `data/shipping_carriers.xml`** ```xml Canada Post canada_post https://www.canadapost-postescanada.ca/track-reperage/en#/search?searchFor={tracking} UPS ups https://www.ups.com/track?tracknum={tracking} FedEx fedex https://www.fedex.com/fedextrack/?trknbr={tracking} Purolator purolator https://www.purolator.com/en/shipping/tracker?pin={tracking} DHL dhl https://www.dhl.com/en/express/tracking.html?AWB={tracking} Other other ``` - [ ] **Step 6: Commit** ```bash git add fusion-woo-odoo/fusion_woocommerce/ git commit -m "feat: add woo.instance and woo.shipping.carrier models with default carriers" ``` --- ### Task 4: Remaining Odoo models **Files:** - Create: `fusion-woo-odoo/fusion_woocommerce/models/woo_product_map.py` - Create: `fusion-woo-odoo/fusion_woocommerce/models/woo_order.py` - Create: `fusion-woo-odoo/fusion_woocommerce/models/woo_shipment.py` - Create: `fusion-woo-odoo/fusion_woocommerce/models/woo_customer.py` - Create: `fusion-woo-odoo/fusion_woocommerce/models/woo_sync_log.py` - Create: `fusion-woo-odoo/fusion_woocommerce/models/woo_conflict.py` - Create: `fusion-woo-odoo/fusion_woocommerce/models/woo_tax_map.py` - Create: `fusion-woo-odoo/fusion_woocommerce/models/woo_pricelist_map.py` - Create: `fusion-woo-odoo/fusion_woocommerce/models/woo_return.py` - Modify: `fusion-woo-odoo/fusion_woocommerce/models/__init__.py` Implement each model exactly per the spec data model tables. Each model must include `company_id` field. All fields, types, and selections from the spec. - [ ] **Step 1: Create all model files** Each model file follows the spec tables exactly. Key points: - `woo.product.map`: includes `woo_product_type`, `woo_parent_id`, `is_variation`, `sync_images`, `woo_image_ids`, state with `unmapped` - `woo.order`: includes `invoice_id` (Many2one account.move), `invoice_synced` - `woo.shipment`: child of woo.order with `carrier_id` (Many2one woo.shipping.carrier), `tracking_number`, `is_backorder` - `woo.customer`: `partner_id`, `woo_customer_id`, `woo_email` - `woo.sync.log`: append-only log - `woo.conflict`: polymorphic with `conflict_type`, `map_id`, `customer_id`, `order_id` - `woo.tax.map`: `tax_id` → `woo_tax_class` - `woo.pricelist.map`: `pricelist_id` → `woo_role` - `woo.return` + `woo.return.line`: return tracking with reason codes - [ ] **Step 2: Update `models/__init__.py`** (only models created so far — sale_order/stock_picking/account_move/res_partner are added in Task 5) ```python from . import woo_shipping_carrier from . import woo_instance from . import woo_product_map from . import woo_order from . import woo_shipment from . import woo_customer from . import woo_sync_log from . import woo_conflict from . import woo_tax_map from . import woo_pricelist_map from . import woo_return ``` - [ ] **Step 3: Update `ir.model.access.csv` with all models** Add read access for `group_woo_user` and full CRUD for `group_woo_manager` for every new model. - [ ] **Step 4: Install module in dev and verify no errors** ```bash docker exec odoo-dev-app odoo -d fusion-dev -i fusion_woocommerce --stop-after-init ``` - [ ] **Step 5: Commit** ```bash git add fusion-woo-odoo/fusion_woocommerce/ git commit -m "feat: add all core data models (product map, order, shipment, customer, sync log, conflict, tax/pricelist map, returns)" ``` --- ### Task 5: Odoo model extensions (sale.order, stock.picking, account.move, res.partner) **Files:** - Create: `fusion-woo-odoo/fusion_woocommerce/models/sale_order.py` - Create: `fusion-woo-odoo/fusion_woocommerce/models/stock_picking.py` - Create: `fusion-woo-odoo/fusion_woocommerce/models/account_move.py` - Create: `fusion-woo-odoo/fusion_woocommerce/models/res_partner.py` - [ ] **Step 1: Write `sale_order.py`** Extend `sale.order` with: - `woo_order_id` (Many2one to `woo.order`) — reverse link - `woo_bind_ids` (One2many to `woo.order`) — all WC bindings - Helper method `_woo_push_status()` to push status changes to WC - [ ] **Step 2: Write `stock_picking.py`** Extend `stock.picking` with: - `woo_shipment_ids` (One2many to `woo.shipment`) - Override `button_validate()` to trigger WC shipping update when delivery is done - Fields for tracking number and carrier on the picking form - [ ] **Step 3: Write `account_move.py`** Extend `account.move` with: - `woo_order_id` (Many2one to `woo.order`) — link to synced order - Method to push invoice PDF to WC when posted - [ ] **Step 4: Write `res_partner.py`** Extend `res.partner` with: - `woo_customer_ids` (One2many to `woo.customer`) - `is_woo_customer` (Boolean computed field) - [ ] **Step 5: Update module, verify installation** ```bash docker exec odoo-dev-app odoo -d fusion-dev -u fusion_woocommerce --stop-after-init ``` - [ ] **Step 6: Commit** ```bash git add fusion-woo-odoo/fusion_woocommerce/models/ git commit -m "feat: extend sale.order, stock.picking, account.move, res.partner with WooCommerce fields" ``` --- ## Phase 2: Views, Settings, and Menus ### Task 6: Instance views and settings page **Files:** - Create: `fusion-woo-odoo/fusion_woocommerce/views/woo_instance_views.xml` - Create: `fusion-woo-odoo/fusion_woocommerce/views/res_config_settings.xml` - Create: `fusion-woo-odoo/fusion_woocommerce/views/woo_menus.xml` - [ ] **Step 1: Create `woo_instance_views.xml`** Form view with: - Connection section: URL, consumer key/secret, webhook secret, API version - "Test Connection" button, "Generate API Key" button - Status indicator (statusbar widget) - Sync Configuration tab: interval, toggles, warehouse, price sync direction - Shipping Carriers tab (inline tree of woo.shipping.carrier) - Notifications tab: toggle + user selector - Stats section: mapped/unmapped/errors computed fields Tree view with: name, URL, state, last_sync, mapped_count - [ ] **Step 2: Create `woo_menus.xml`** Top-level menu "WooCommerce" with submenus: - Dashboard - Instances - Product Mapping - Orders - Customers - Sync Logs - Conflicts - Returns - Configuration (carriers, tax mapping, pricelist mapping) - [ ] **Step 3: Create `res_config_settings.xml`** **Note:** All sync settings live on the `woo.instance` form view (not in Odoo General Settings). The `res_config_settings.xml` is a simple redirect action to the instance list, not a full settings model — this avoids needing a `res.config.settings` Python model. - [ ] **Step 4: Update module and test views load** ```bash docker exec odoo-dev-app odoo -d fusion-dev -u fusion_woocommerce --stop-after-init ``` - [ ] **Step 5: Commit** ```bash git add fusion-woo-odoo/fusion_woocommerce/views/ git commit -m "feat: add instance form/tree views, menus, and settings" ``` --- ### Task 7: All remaining views (product map, orders, sync log, conflicts, etc.) **Files:** - Create: `fusion-woo-odoo/fusion_woocommerce/views/woo_product_map_views.xml` - Create: `fusion-woo-odoo/fusion_woocommerce/views/woo_order_views.xml` - Create: `fusion-woo-odoo/fusion_woocommerce/views/woo_sync_log_views.xml` - Create: `fusion-woo-odoo/fusion_woocommerce/views/woo_conflict_views.xml` - Create: `fusion-woo-odoo/fusion_woocommerce/views/woo_customer_views.xml` - Create: `fusion-woo-odoo/fusion_woocommerce/views/woo_return_views.xml` - Create: `fusion-woo-odoo/fusion_woocommerce/views/woo_tax_map_views.xml` - Create: `fusion-woo-odoo/fusion_woocommerce/views/woo_pricelist_map_views.xml` - Create: `fusion-woo-odoo/fusion_woocommerce/views/woo_shipping_carrier_views.xml` - Create: `fusion-woo-odoo/fusion_woocommerce/views/sale_order_views.xml` - Create: `fusion-woo-odoo/fusion_woocommerce/views/stock_picking_views.xml` - [ ] **Step 1: Create views for each model** Each model gets a tree view and form view. Key views: - **Product map**: tree with Odoo product, WC product, SKUs, price comparison, status badges - **Orders**: tree with WC order #, SO reference, status, shipment count. Form with shipments tab, invoice link - **Sync log**: tree (read-only list), filterable by type/direction/state. Search view with group-by - **Conflicts**: tree with resolution actions, form with "Use Odoo" / "Use WC" buttons - **Returns**: form with order link, return lines, status workflow buttons - **Sale order**: inherit form to add WooCommerce tab showing linked WC order, shipments, tracking - **Stock picking**: inherit form to add tracking number + carrier fields - [ ] **Step 2: Update module and verify all views** ```bash docker exec odoo-dev-app odoo -d fusion-dev -u fusion_woocommerce --stop-after-init ``` - [ ] **Step 3: Commit** ```bash git add fusion-woo-odoo/fusion_woocommerce/views/ git commit -m "feat: add all model views (product map, orders, sync log, conflicts, returns, tax/pricelist maps)" ``` --- ## Phase 3: Sync Engine — Webhooks and Cron ### Task 8: Webhook controller (receive WC events) **Files:** - Create: `fusion-woo-odoo/fusion_woocommerce/controllers/webhook.py` - Modify: `fusion-woo-odoo/fusion_woocommerce/controllers/__init__.py` - [ ] **Step 1: Write `webhook.py`** Odoo HTTP controller with `type="jsonrpc"` endpoints: - `/woo/webhook/order` — receives new order webhooks from WC - `/woo/webhook/product` — receives product update webhooks - `/woo/webhook/customer` — receives customer creation/update webhooks Each endpoint: 1. Validates HMAC signature using `WooApiClient.verify_webhook_signature()` 2. Finds the matching `woo.instance` by webhook secret 3. Checks dedup (order ID, product ID, etc.) 4. Delegates to the appropriate sync method on the model 5. Returns success/failure response **Important — Webhook format conflict:** WooCommerce webhooks send raw JSON POST bodies, NOT JSON-RPC format. Since Odoo 19 deprecates `type="json"`, use `type="http"` with `auth="none"` and `csrf=False` for webhook endpoints. Parse the raw request body with `json.loads(request.httprequest.get_data())`. This is the one exception to the `type="jsonrpc"` rule — external webhook receivers cannot use JSON-RPC because the sender (WooCommerce) controls the payload format. Read Odoo 19 reference controller from Docker first to verify `type="http"` is still supported. - [ ] **Step 2: Update `controllers/__init__.py`** ```python from . import webhook from . import api from . import product_search ``` - [ ] **Step 3: Commit** ```bash git add fusion-woo-odoo/fusion_woocommerce/controllers/ git commit -m "feat: add webhook controller for receiving WC order/product/customer events" ``` --- ### Task 9: API controller (endpoints for WP plugin) **Files:** - Create: `fusion-woo-odoo/fusion_woocommerce/controllers/api.py` - [ ] **Step 1: Write `api.py`** REST-like endpoints that the WordPress plugin calls: - `/woo/api/order//documents` — WP plugin fetches invoice/delivery PDFs - `/woo/api/order//status` — WP plugin fetches order status + timeline - `/woo/api/order//messages` — WP plugin fetches customer-visible messages - `/woo/api/return/create` — WP plugin submits return request Each endpoint validates the Odoo API key (bearer token in Authorization header). **Important:** Use `type="jsonrpc"` per Odoo 19 rules. - [ ] **Step 2: Commit** ```bash git add fusion-woo-odoo/fusion_woocommerce/controllers/api.py git commit -m "feat: add API controller for WordPress plugin communication" ``` --- ### Task 10: AJAX search controller (product mapping) **Files:** - Create: `fusion-woo-odoo/fusion_woocommerce/controllers/product_search.py` - [ ] **Step 1: Write `product_search.py`** Endpoints for live AJAX search in the product mapping UI: - `/woo/search/odoo_products` — search Odoo products by name/SKU/internal_reference - `/woo/search/woo_products` — search fetched WC products by name/SKU - `/woo/search/mapped` — search mapped products Returns JSON results, debounced on the client side (300ms). **Important:** Use `type="jsonrpc"`. - [ ] **Step 2: Commit** ```bash git add fusion-woo-odoo/fusion_woocommerce/controllers/product_search.py git commit -m "feat: add AJAX search controller for product mapping UI" ``` --- ### Task 11: Cron jobs and sync engine **Files:** - Create: `fusion-woo-odoo/fusion_woocommerce/data/cron.xml` - Add sync methods to: `fusion-woo-odoo/fusion_woocommerce/models/woo_instance.py` - [ ] **Step 1: Create `data/cron.xml`** ```xml WooCommerce: Sync Products code model._cron_sync_products() 15 minutes -1 True WooCommerce: Sync Orders code model._cron_sync_orders() 5 minutes -1 True WooCommerce: Sync Inventory code model._cron_sync_inventory() 15 minutes -1 True WooCommerce: Sync Customers code model._cron_sync_customers() 30 minutes -1 True WooCommerce: Cleanup Old Sync Logs code model._cron_cleanup_logs() 1 days -1 True ``` - [ ] **Step 2: Add sync methods to `woo_instance.py`** Add class methods: - `_cron_sync_products()` — iterates connected instances, syncs prices/inventory for mapped products - `_cron_sync_orders()` — fetches recent WC orders not yet synced, creates SOs - `_cron_sync_inventory()` — pushes Odoo stock levels to WC for mapped products - `_cron_sync_customers()` — syncs address updates Odoo → WC Each method: 1. Gets all `connected` instances 2. For each instance, calls the WC API 3. Applies dedup checks 4. Creates/updates records 5. Logs to `woo.sync.log` 6. On failure: logs error, sends notification email if enabled - [ ] **Step 3: Add log cleanup to `woo_sync_log.py`** ```python @api.model def _cron_cleanup_logs(self): """Purge sync logs older than 90 days (180 for errors).""" cutoff_success = fields.Datetime.subtract(fields.Datetime.now(), days=90) cutoff_error = fields.Datetime.subtract(fields.Datetime.now(), days=180) self.search([ '|', '&', ('state', '!=', 'failed'), ('create_date', '<', cutoff_success), '&', ('state', '=', 'failed'), ('create_date', '<', cutoff_error), ]).unlink() ``` - [ ] **Step 4: Create email notification templates** `data/mail_template.xml` with templates for: - Sync failure notification - New WC order received notification - [ ] **Step 5: Update module and verify crons registered** ```bash docker exec odoo-dev-app odoo -d fusion-dev -u fusion_woocommerce --stop-after-init ``` - [ ] **Step 6: Commit** ```bash git add fusion-woo-odoo/fusion_woocommerce/ git commit -m "feat: add cron jobs, sync engine methods, log cleanup, and email templates" ``` --- ## Phase 4: Wizards (Setup + Product Fetch) ### Task 12: Setup wizard **Files:** - Create: `fusion-woo-odoo/fusion_woocommerce/wizard/woo_setup_wizard.py` - Create: `fusion-woo-odoo/fusion_woocommerce/wizard/woo_setup_wizard_views.xml` - Modify: `fusion-woo-odoo/fusion_woocommerce/wizard/__init__.py` - [ ] **Step 1: Write `woo_setup_wizard.py`** Multi-step transient model wizard: 1. Enter WC URL + consumer key/secret 2. Test connection 3. Select default warehouse 4. Generate Odoo API key 5. Trigger product fetch - [ ] **Step 2: Write `woo_setup_wizard_views.xml`** Form view with step indicator, fields for each step, navigation buttons. - [ ] **Step 3: Commit** ```bash git add fusion-woo-odoo/fusion_woocommerce/wizard/ git commit -m "feat: add first-time setup wizard" ``` --- ### Task 13: Product fetch and auto-match wizard **Files:** - Create: `fusion-woo-odoo/fusion_woocommerce/wizard/woo_product_fetch.py` - Create: `fusion-woo-odoo/fusion_woocommerce/wizard/woo_product_fetch_views.xml` - [ ] **Step 1: Write `woo_product_fetch.py`** Transient model that: 1. Fetches all WC products via paginated API calls (including variations for variable products) 2. Auto-match phase 1: SKU match (`internal_reference` = `sku`) 3. Auto-match phase 2: name similarity suggestions (using `difflib.SequenceMatcher`) 4. Creates `woo.product.map` records: - SKU matches → state `mapped` automatically - Name suggestions → state `unmapped` with suggested match stored - No match → state `unmapped` 5. Returns action to open the product mapping view - [ ] **Step 2: Write view with progress indicator** - [ ] **Step 3: Commit** ```bash git add fusion-woo-odoo/fusion_woocommerce/wizard/ git commit -m "feat: add product fetch wizard with SKU auto-match and name suggestions" ``` --- ## Phase 5: OWL Frontend (Product Mapping UI + Dashboard) ### Task 14: Product mapping OWL component **Files:** - Create: `fusion-woo-odoo/fusion_woocommerce/static/src/js/product_mapping.js` - Create: `fusion-woo-odoo/fusion_woocommerce/static/src/js/ajax_search.js` - Create: `fusion-woo-odoo/fusion_woocommerce/static/src/xml/product_mapping.xml` - Create: `fusion-woo-odoo/fusion_woocommerce/static/src/css/woo_styles.css` **Important Odoo 19 rules:** - Use standalone `rpc()` from `@web/core/network/rpc` - `static props = []` not `{}` - Read reference OWL components from Docker first - [ ] **Step 1: Read reference OWL component from Odoo 19** ```bash docker exec odoo-dev-app cat /usr/lib/python3/dist-packages/odoo/addons/web/static/src/core/network/rpc.js ``` - [ ] **Step 2: Write `ajax_search.js`** Reusable search component with: - Debounced input (300ms) - Calls `product_search` controller endpoint via `rpc()` - Emits results to parent component - No enter key required - [ ] **Step 3: Write `product_mapping.js`** Main OWL component with three tabs: - Mapped Products: table with live search, bulk actions - Unmatched Products: split view with search bars on each side, click-to-map - Conflicts: resolution table with action buttons - [ ] **Step 4: Write `product_mapping.xml` (OWL template)** - [ ] **Step 5: Write `woo_styles.css`** Modern styling for the mapping UI: clean table styles, status badges, search bar styling, tab navigation. - [ ] **Step 6: Update module and verify UI loads** ```bash docker exec odoo-dev-app odoo -d fusion-dev -u fusion_woocommerce --stop-after-init ``` - [ ] **Step 7: Commit** ```bash git add fusion-woo-odoo/fusion_woocommerce/static/ git commit -m "feat: add OWL product mapping UI with live AJAX search" ``` --- ### Task 15: Dashboard widget **Files:** - Create: `fusion-woo-odoo/fusion_woocommerce/static/src/js/dashboard.js` - Create: `fusion-woo-odoo/fusion_woocommerce/static/src/xml/dashboard.xml` - Create: `fusion-woo-odoo/fusion_woocommerce/views/woo_dashboard.xml` - [ ] **Step 1: Write dashboard OWL component** Shows at a glance: - Orders pending sync (count + link) - Last sync time - Errors in last 24h (count + link) - Products mapped/unmapped (progress bar) - Quick actions: Sync Now, View Conflicts, Open Mapping - [ ] **Step 2: Register as action in `woo_dashboard.xml`** - [ ] **Step 3: Commit** ```bash git add fusion-woo-odoo/fusion_woocommerce/ git commit -m "feat: add WooCommerce sync dashboard widget" ``` --- ## Phase 6: WordPress Plugin — fusion-woodoo ### Task 16: WordPress plugin scaffold **Files:** - Create: `fusion-woo-odoo/fusion-woodoo/fusion-woodoo.php` - Create: `fusion-woo-odoo/fusion-woodoo/includes/class-fusion-woodoo.php` - Create: `fusion-woo-odoo/fusion-woodoo/includes/class-admin-settings.php` - Create: `fusion-woo-odoo/fusion-woodoo/templates/admin/settings.php` - Create: `fusion-woo-odoo/fusion-woodoo/assets/css/admin.css` - Create: `fusion-woo-odoo/fusion-woodoo/assets/js/admin.js` - Create: `fusion-woo-odoo/fusion-woodoo/assets/images/icon.png` (placeholder) - Create: `fusion-woo-odoo/fusion-woodoo/readme.txt` - Create: `fusion-woo-odoo/fusion-woodoo/languages/fusion-woodoo.pot` - [ ] **Step 1: Create `fusion-woodoo.php`** Plugin header, activation/deactivation hooks, main class bootstrap. Requires WooCommerce. ```php

Fusion WooDoo requires WooCommerce to be installed and active.

'; }); return; } require_once FUSION_WOODOO_PLUGIN_DIR . 'includes/class-fusion-woodoo.php'; Fusion_WooDoo::instance(); }); // Activation hook register_activation_hook(__FILE__, function() { // Create upload directories with .htaccess protection $dirs = ['invoices', 'deliveries']; foreach ($dirs as $dir) { $path = wp_upload_dir()['basedir'] . '/fusion-woodoo/' . $dir; wp_mkdir_p($path); file_put_contents($path . '/.htaccess', 'deny from all'); } }); // Deactivation hook — unregister WC webhooks register_deactivation_hook(__FILE__, function() { require_once FUSION_WOODOO_PLUGIN_DIR . 'includes/class-webhooks.php'; Fusion_WooDoo_Webhooks::unregister_all(); }); ``` - [ ] **Step 2: Create `class-fusion-woodoo.php`** Singleton main class, loads all includes, registers hooks. - [ ] **Step 3: Create admin settings page** WP admin page under WooCommerce menu: - Odoo Instance URL field - API Key field - "Test Connection" button (AJAX) - Portal toggle settings - Webhook status display - [ ] **Step 4: Commit** ```bash git add fusion-woo-odoo/fusion-woodoo/ git commit -m "feat: scaffold fusion-woodoo WordPress plugin with admin settings" ``` --- ### Task 17: REST endpoints (receive from Odoo) **Files:** - Create: `fusion-woo-odoo/fusion-woodoo/includes/class-rest-endpoints.php` - [ ] **Step 1: Write REST endpoints** Register WP REST API endpoints that Odoo calls: - `POST /wp-json/fusion-woodoo/v1/order/update` — receives order status + tracking + documents from Odoo - `POST /wp-json/fusion-woodoo/v1/order/invoice` — receives invoice PDF - `POST /wp-json/fusion-woodoo/v1/order/delivery` — receives delivery PDF - `POST /wp-json/fusion-woodoo/v1/order/messages` — receives customer-visible messages Each endpoint: 1. Validates Odoo API key from Authorization header 2. Stores data as WC order meta 3. Saves PDFs to protected upload directory 4. Returns success/error - [ ] **Step 2: Commit** ```bash git add fusion-woo-odoo/fusion-woodoo/includes/class-rest-endpoints.php git commit -m "feat: add REST endpoints for receiving Odoo data (orders, invoices, deliveries)" ``` --- ### Task 18: Webhook registration **Files:** - Create: `fusion-woo-odoo/fusion-woodoo/includes/class-webhooks.php` - [ ] **Step 1: Write webhook management class** On plugin activation / settings save: 1. Register WC webhooks pointing to Odoo: - `order.created` → `{odoo_url}/woo/webhook/order` - `order.updated` → `{odoo_url}/woo/webhook/order` - `product.updated` → `{odoo_url}/woo/webhook/product` - `customer.created` → `{odoo_url}/woo/webhook/customer` - `customer.updated` → `{odoo_url}/woo/webhook/customer` 2. On deactivation: unregister all webhooks 3. On URL change: re-register with new URL 4. Display webhook status in admin settings - [ ] **Step 2: Commit** ```bash git add fusion-woo-odoo/fusion-woodoo/includes/class-webhooks.php git commit -m "feat: add WC webhook registration/lifecycle management" ``` --- ### Task 19: My Account portal — tabs and templates **Files:** - Create: `fusion-woo-odoo/fusion-woodoo/includes/class-my-account.php` - Create: `fusion-woo-odoo/fusion-woodoo/includes/class-order-timeline.php` - Create: `fusion-woo-odoo/fusion-woodoo/includes/class-returns.php` - Create: `fusion-woo-odoo/fusion-woodoo/includes/class-api-client.php` - Create: `fusion-woo-odoo/fusion-woodoo/templates/my-account/sales-orders.php` - Create: `fusion-woo-odoo/fusion-woodoo/templates/my-account/invoices.php` - Create: `fusion-woo-odoo/fusion-woodoo/templates/my-account/deliveries.php` - Create: `fusion-woo-odoo/fusion-woodoo/templates/my-account/returns.php` - Create: `fusion-woo-odoo/fusion-woodoo/templates/my-account/order-timeline.php` - Create: `fusion-woo-odoo/fusion-woodoo/templates/my-account/communication.php` - Create: `fusion-woo-odoo/fusion-woodoo/assets/css/my-account.css` - Create: `fusion-woo-odoo/fusion-woodoo/assets/js/my-account.js` - [ ] **Step 1: Write `class-my-account.php`** Register new WC My Account tabs via `woocommerce_account_menu_items` filter: - Sales Orders - Invoices - Deliveries - Returns Register endpoints via `woocommerce_account_{slug}_endpoint`. Load templates. - [ ] **Step 2: Write sales orders template** Table of Odoo-synced sales orders. Columns: Order #, Date, Items, Total, Status. "Reorder" button using stored WC product IDs. - [ ] **Step 3: Write invoices template** Table of Odoo invoices. "Download PDF" button serving file through access-controlled PHP handler that validates user ownership. - [ ] **Step 4: Write deliveries template** Table of deliveries with carrier, tracking # (clickable link to carrier tracking URL), status. "Download PDF" for delivery receipt. - [ ] **Step 5: Write `class-order-timeline.php` and template** Visual timeline component: ``` ● Confirmed → ● Processing → ● Shipped → ● Delivered → ● Completed ``` CSS-driven, shows dates, tracking info at shipping stage. - [ ] **Step 6: Write returns tab and `class-returns.php`** Return request form: select order → select items + quantities → select reason → submit. Sends to Odoo API. Displays existing returns with status. - [ ] **Step 7: Write communication history template** Threaded conversation view from `_odoo_messages` meta. - [ ] **Step 8: Write `class-api-client.php`** PHP client for calling Odoo API endpoints (for return submission, status checks). - [ ] **Step 9: Write CSS and JS for portal** `my-account.css`: modern styling for tabs, tables, timeline, return form. `my-account.js`: AJAX for return submission, reorder button. - [ ] **Step 10: Commit** ```bash git add fusion-woo-odoo/fusion-woodoo/ git commit -m "feat: add My Account portal with sales orders, invoices, deliveries, returns, timeline, and communication history" ``` --- ## Phase 7: Order Lifecycle Integration ### Task 20: Order sync — WC order → Odoo SO + Invoice **Files:** - Modify: `fusion-woo-odoo/fusion_woocommerce/models/woo_instance.py` (add `_sync_order_from_wc` method) - Modify: `fusion-woo-odoo/fusion_woocommerce/models/woo_order.py` (add processing methods) - [ ] **Step 1: Write `_sync_order_from_wc` on `woo.instance`** Method that receives WC order data and: 1. Finds/creates customer by email (via `woo.customer`) 2. Checks dedup by `woo_order_id` 3. Maps WC line items to Odoo products via `woo.product.map` 4. Creates `sale.order` with correct products, quantities, prices, taxes (via `woo.tax.map`) 5. Confirms the SO 6. Creates draft invoice 7. Creates `woo.order` tracking record 8. Pushes SO details back to WC 9. Logs everything to `woo.sync.log` - [ ] **Step 2: Add shipping/completion push methods to `woo.order`** - `action_push_shipping()` — pushes tracking + carrier to WC, triggers WC shipping email - `action_push_completed()` — pushes completed status to WC, triggers WC completion email - `action_push_invoice_pdf()` — renders Odoo invoice PDF, pushes to WP plugin REST endpoint - `action_push_delivery_pdf()` — renders delivery receipt PDF, pushes to WP plugin - [ ] **Step 3: Commit** ```bash git add fusion-woo-odoo/fusion_woocommerce/models/ git commit -m "feat: implement order sync (WC → Odoo SO/invoice) and status push (Odoo → WC)" ``` --- ### Task 21: Shipping and completion workflow on stock.picking **Files:** - Modify: `fusion-woo-odoo/fusion_woocommerce/models/stock_picking.py` - [ ] **Step 1: Extend `button_validate` override** When a delivery linked to a WC order is validated: 1. Create `woo.shipment` record with tracking + carrier 2. If tracking number is set: automatically push shipping update to WC 3. Handle partial deliveries (backorders): create additional shipment records - [ ] **Step 2: Add tracking fields to picking form** Add `woo_tracking_number` and `woo_carrier_id` fields to `stock.picking`, visible when the picking is linked to a WC order. - [ ] **Step 3: Commit** ```bash git add fusion-woo-odoo/fusion_woocommerce/models/stock_picking.py git commit -m "feat: integrate shipping workflow with WC status push and backorder handling" ``` --- ## Phase 8: Advanced Features ### Task 22: Product price and inventory sync logic **Files:** - Modify: `fusion-woo-odoo/fusion_woocommerce/models/woo_instance.py` - Modify: `fusion-woo-odoo/fusion_woocommerce/models/woo_product_map.py` - [ ] **Step 1: Implement price sync in cron** For each mapped product with `sync_price`: - Fetch WC price via API - Compare to Odoo `list_price` - If different on one side only → update the other - If different on both → create `woo.conflict` - [ ] **Step 2: Implement inventory sync** For each mapped product with `sync_inventory`: - Get `qty_available` from Odoo (filtered by configured warehouse) - Push to WC `stock_quantity` via API - [ ] **Step 3: Implement image sync** For mapped products with `sync_images`: - Compare image checksums - Push new/changed images to WC media library - Pull WC images to Odoo `ir.attachment` - Track WC media IDs in `woo_image_ids` - [ ] **Step 4: Commit** ```bash git add fusion-woo-odoo/fusion_woocommerce/models/ git commit -m "feat: implement price, inventory, and image sync logic" ``` --- ### Task 23: Conflict resolution **Files:** - Modify: `fusion-woo-odoo/fusion_woocommerce/models/woo_conflict.py` - [ ] **Step 1: Add resolution methods** - `action_use_odoo()` — pushes Odoo value to WC, resolves conflict - `action_use_woo()` — pulls WC value to Odoo, resolves conflict - `action_bulk_resolve_odoo()` — resolves all selected conflicts with Odoo values - `action_bulk_resolve_woo()` — resolves all selected conflicts with WC values - [ ] **Step 2: Commit** ```bash git add fusion-woo-odoo/fusion_woocommerce/models/woo_conflict.py git commit -m "feat: implement conflict resolution actions" ``` --- ### Task 24: Return/RMA and refund handling **Files:** - Modify: `fusion-woo-odoo/fusion_woocommerce/models/woo_return.py` - Modify: `fusion-woo-odoo/fusion_woocommerce/controllers/api.py` - [ ] **Step 1: Implement return workflow** `woo.return` methods: - `action_approve()` — creates reverse `stock.picking`, pushes status to WC - `action_reject()` — rejects return, pushes status to WC - `action_receive()` — marks return received - `action_refund()` — creates credit note on linked invoice, syncs refund to WC - [ ] **Step 2: Add return endpoint to API controller** Process incoming return requests from WP plugin. - [ ] **Step 3: Commit** ```bash git add fusion-woo-odoo/fusion_woocommerce/ git commit -m "feat: implement return/RMA workflow and refund handling" ``` --- ### Task 25: Customer sync logic **Files:** - Modify: `fusion-woo-odoo/fusion_woocommerce/models/woo_customer.py` - Modify: `fusion-woo-odoo/fusion_woocommerce/models/woo_instance.py` - [ ] **Step 1: Implement customer matching and creation** - Match by email (`res.partner` search) - Create partner with billing/shipping addresses, phone, tag - Create `woo.customer` link record - Push `odoo_customer_id` back to WC user meta - [ ] **Step 2: Implement address sync (Odoo → WC)** Cron method to push Odoo address changes to WC for mapped customers. - [ ] **Step 3: Commit** ```bash git add fusion-woo-odoo/fusion_woocommerce/models/ git commit -m "feat: implement customer sync with email matching and address updates" ``` --- ### Task 26: Tax mapping, pricelist sync, and multi-currency logic **Files:** - Modify: `fusion-woo-odoo/fusion_woocommerce/models/woo_tax_map.py` - Modify: `fusion-woo-odoo/fusion_woocommerce/models/woo_pricelist_map.py` - Modify: `fusion-woo-odoo/fusion_woocommerce/models/woo_instance.py` - [ ] **Step 1: Implement tax mapping application** Add helper methods: - `_get_odoo_tax_from_wc_class(instance, wc_tax_class)` — looks up `woo.tax.map` to find matching `account.tax` - `_get_wc_tax_class_from_odoo_tax(instance, tax_id)` — reverse lookup for pushing products to WC - Apply tax mapping in `_sync_order_from_wc` when creating SO lines - Apply tax mapping when pushing products to WC - [ ] **Step 2: Implement pricelist sync logic** - When WC order arrives: check customer's WC role → find mapped pricelist via `woo.pricelist.map` → apply pricelist to SO - When syncing prices to WC: if role-based pricing plugin is detected, push pricelist-specific prices per role - Fallback to default public pricelist if no mapping - [ ] **Step 3: Implement multi-currency support** - When syncing products: include currency in WC product update - When receiving WC orders: respect order currency, set on SO - Invoice amounts honour the original order currency - Leverage Odoo's existing `res.currency.rate` for exchange rates - [ ] **Step 4: Commit** ```bash git add fusion-woo-odoo/fusion_woocommerce/models/ git commit -m "feat: implement tax mapping, pricelist sync, and multi-currency support" ``` --- ### Task 27: Communication history push and invoice trigger **Files:** - Modify: `fusion-woo-odoo/fusion_woocommerce/models/woo_order.py` - Modify: `fusion-woo-odoo/fusion_woocommerce/models/account_move.py` - [ ] **Step 1: Implement communication history push** Add method on `woo.order`: - `_push_messages_to_wc()` — extracts `mail.message` records from linked `sale.order` chatter where `subtype_id` indicates "visible to customer" (not internal notes) - Sends as JSON array `[{author, date, body}]` to WP plugin REST endpoint - Called from `action_push_shipping()` and `action_push_completed()` automatically - Incremental: tracks last pushed message ID, only sends new messages - [ ] **Step 2: Add invoice push trigger on `account.move`** Override `action_post()` on `account.move`: - After posting, check if the invoice is linked to a `woo.order` (via `woo_order_id`) - If so, automatically call `woo_order.action_push_invoice_pdf()` - Mark `invoice_synced = True` on the `woo.order` - [ ] **Step 3: Handle incoming WC refund webhooks** Add webhook handler for `order.refunded` events: - Receive WC refund data (partial or full) - Find linked `woo.order` by `woo_order_id` - Create Odoo credit note on the linked invoice - Dedup by `woo_order_id` + WC refund ID (stored as reference on the credit note) - [ ] **Step 4: Commit** ```bash git add fusion-woo-odoo/fusion_woocommerce/models/ git commit -m "feat: add communication history push, invoice auto-trigger, and WC refund webhook handler" ``` --- ### Task 28: Connection health check, rate limiting, and readme **Files:** - Modify: `fusion-woo-odoo/fusion_woocommerce/models/woo_instance.py` - Modify: `fusion-woo-odoo/fusion_woocommerce/data/cron.xml` - Create: `fusion-woo-odoo/fusion-woodoo/readme.txt` - Create: `fusion-woo-odoo/fusion-woodoo/languages/fusion-woodoo.pot` - [ ] **Step 1: Add connection health check cron** New cron job that runs hourly: pings each connected WC instance, updates state to `error` if unreachable, sends notification. - [ ] **Step 2: Add rate limiting to webhook endpoints** Simple in-memory rate limiter on Odoo webhook controller: max 100 requests per minute per source IP. Returns 429 if exceeded. - [ ] **Step 3: Add stale webhook detection to WP plugin** In admin settings, check if registered webhook delivery URLs are reachable. Show warning if Odoo URL is unreachable. - [ ] **Step 4: Write WordPress plugin `readme.txt`** Standard WordPress plugin readme with description, installation instructions, FAQ, changelog. - [ ] **Step 5: Generate `.pot` translation template** - [ ] **Step 6: Commit** ```bash git add fusion-woo-odoo/ git commit -m "feat: add health checks, rate limiting, stale webhook detection, and plugin readme" ``` --- **Note on parallelisation:** The following task groups are independent and can run concurrently if multiple agents are available: - **Phases 1-5** (Odoo module) and **Phase 6** (WordPress plugin) are fully independent - **Tasks 8, 9, 10** (controllers) can run in parallel - **Tasks 14 and 15** (OWL components) can run in parallel --- ## Phase 9: Testing and Polish ### Task 29: End-to-end testing on dev instances - [ ] **Step 1: Install Odoo module on dev** ```bash docker exec odoo-dev-app odoo -d fusion-dev -i fusion_woocommerce --stop-after-init ``` - [ ] **Step 2: Create a test WooCommerce instance connection** Configure connection to a test WC site, test connection, generate API key. - [ ] **Step 3: Test product fetch and mapping** Run the product fetch wizard, verify auto-match, test manual mapping with search. - [ ] **Step 4: Test order lifecycle end-to-end** Place a test WC order, verify it syncs to Odoo SO, process delivery, push shipping, push completion. - [ ] **Step 5: Test customer portal** Verify invoices, deliveries, returns, timeline display in WP My Account. - [ ] **Step 6: Test inventory sync** Change stock in Odoo, verify WC stock updates. - [ ] **Step 7: Fix any issues found** - [ ] **Step 8: Commit fixes** ```bash git add fusion-woo-odoo/ git commit -m "fix: address issues found during end-to-end testing" ``` --- ### Task 30: Deploy to Westin and Mobility - [ ] **Step 1: Install on odoo-westin (VM101)** SSH to `odoo-westin`, deploy module, configure connection to westinhealthcare.ca. - [ ] **Step 2: Install WP plugin on westinhealthcare.ca (VM301)** SSH to `westin-wp`, upload plugin, activate, configure Odoo URL + API key. - [ ] **Step 3: Install on odoo-mobility (VM115)** SSH to `odoo-mobility`, deploy module, configure connection to mobilityspecialties.ca. - [ ] **Step 4: Install WP plugin on mobilityspecialties.ca (VM305)** SSH to `wordpress-mobility`, upload plugin, activate, configure. - [ ] **Step 5: Run initial product fetch on both instances** - [ ] **Step 6: Verify sync is working on both sites** - [ ] **Step 7: Commit any deployment fixes** ```bash git add fusion-woo-odoo/ git commit -m "fix: deployment adjustments for production instances" ```