Comprehensive plan covering Odoo module scaffold, core models, API client, views/menus, sync engine (webhooks + cron), wizards, OWL frontend, WordPress plugin, order lifecycle, advanced features (tax mapping, pricelist sync, multi-currency, refund handling), and deployment. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
60 KiB
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"(NOTtype="json") - Frontend JS:
Interactionclass 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/<module>/... - API client in
lib/directory, NOTmodels/
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
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
{
'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
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 version="1.0" encoding="utf-8"?>
<odoo>
<record id="module_category_woocommerce" model="ir.module.category">
<field name="name">WooCommerce</field>
<field name="sequence">50</field>
</record>
<record id="group_woo_user" model="res.groups">
<field name="name">WooCommerce User</field>
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
</record>
<record id="group_woo_manager" model="res.groups">
<field name="name">WooCommerce Manager</field>
<field name="implied_ids" eval="[(4, ref('group_woo_user'))]"/>
</record>
</odoo>
- Step 6: Create ir.model.access.csv (initial — will expand as models are added)
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
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
from .woo_api_client import WooApiClient
- Step 2: Write
woo_api_client.py
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
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
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.
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.
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.csvwith carrier model access
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 version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="carrier_canada_post" model="woo.shipping.carrier">
<field name="name">Canada Post</field>
<field name="code">canada_post</field>
<field name="tracking_url">https://www.canadapost-postescanada.ca/track-reperage/en#/search?searchFor={tracking}</field>
</record>
<record id="carrier_ups" model="woo.shipping.carrier">
<field name="name">UPS</field>
<field name="code">ups</field>
<field name="tracking_url">https://www.ups.com/track?tracknum={tracking}</field>
</record>
<record id="carrier_fedex" model="woo.shipping.carrier">
<field name="name">FedEx</field>
<field name="code">fedex</field>
<field name="tracking_url">https://www.fedex.com/fedextrack/?trknbr={tracking}</field>
</record>
<record id="carrier_purolator" model="woo.shipping.carrier">
<field name="name">Purolator</field>
<field name="code">purolator</field>
<field name="tracking_url">https://www.purolator.com/en/shipping/tracker?pin={tracking}</field>
</record>
<record id="carrier_dhl" model="woo.shipping.carrier">
<field name="name">DHL</field>
<field name="code">dhl</field>
<field name="tracking_url">https://www.dhl.com/en/express/tracking.html?AWB={tracking}</field>
</record>
<record id="carrier_other" model="woo.shipping.carrier">
<field name="name">Other</field>
<field name="code">other</field>
<field name="tracking_url"></field>
</record>
</odoo>
- Step 6: Commit
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: includeswoo_product_type,woo_parent_id,is_variation,sync_images,woo_image_ids, state withunmapped -
woo.order: includesinvoice_id(Many2one account.move),invoice_synced -
woo.shipment: child of woo.order withcarrier_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 withconflict_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)
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.csvwith 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
docker exec odoo-dev-app odoo -d fusion-dev -i fusion_woocommerce --stop-after-init
- Step 5: Commit
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 towoo.order) — reverse link -
woo_bind_ids(One2many towoo.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 towoo.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 towoo.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 towoo.customer) -
is_woo_customer(Boolean computed field) -
Step 5: Update module, verify installation
docker exec odoo-dev-app odoo -d fusion-dev -u fusion_woocommerce --stop-after-init
- Step 6: Commit
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
docker exec odoo-dev-app odoo -d fusion-dev -u fusion_woocommerce --stop-after-init
- Step 5: Commit
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
docker exec odoo-dev-app odoo -d fusion-dev -u fusion_woocommerce --stop-after-init
- Step 3: Commit
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:
- Validates HMAC signature using
WooApiClient.verify_webhook_signature() - Finds the matching
woo.instanceby webhook secret - Checks dedup (order ID, product ID, etc.)
- Delegates to the appropriate sync method on the model
- 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
from . import webhook
from . import api
from . import product_search
- Step 3: Commit
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/<int:order_id>/documents— WP plugin fetches invoice/delivery PDFs/woo/api/order/<int:order_id>/status— WP plugin fetches order status + timeline/woo/api/order/<int:order_id>/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
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
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 version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<!-- Product sync cron -->
<record id="cron_woo_sync_products" model="ir.cron">
<field name="name">WooCommerce: Sync Products</field>
<field name="model_id" ref="model_woo_instance"/>
<field name="state">code</field>
<field name="code">model._cron_sync_products()</field>
<field name="interval_number">15</field>
<field name="interval_type">minutes</field>
<field name="numbercall">-1</field>
<field name="active">True</field>
</record>
<!-- Order sync cron -->
<record id="cron_woo_sync_orders" model="ir.cron">
<field name="name">WooCommerce: Sync Orders</field>
<field name="model_id" ref="model_woo_instance"/>
<field name="state">code</field>
<field name="code">model._cron_sync_orders()</field>
<field name="interval_number">5</field>
<field name="interval_type">minutes</field>
<field name="numbercall">-1</field>
<field name="active">True</field>
</record>
<!-- Inventory sync cron -->
<record id="cron_woo_sync_inventory" model="ir.cron">
<field name="name">WooCommerce: Sync Inventory</field>
<field name="model_id" ref="model_woo_instance"/>
<field name="state">code</field>
<field name="code">model._cron_sync_inventory()</field>
<field name="interval_number">15</field>
<field name="interval_type">minutes</field>
<field name="numbercall">-1</field>
<field name="active">True</field>
</record>
<!-- Customer sync cron -->
<record id="cron_woo_sync_customers" model="ir.cron">
<field name="name">WooCommerce: Sync Customers</field>
<field name="model_id" ref="model_woo_instance"/>
<field name="state">code</field>
<field name="code">model._cron_sync_customers()</field>
<field name="interval_number">30</field>
<field name="interval_type">minutes</field>
<field name="numbercall">-1</field>
<field name="active">True</field>
</record>
<!-- Sync log cleanup cron -->
<record id="cron_woo_cleanup_logs" model="ir.cron">
<field name="name">WooCommerce: Cleanup Old Sync Logs</field>
<field name="model_id" ref="model_woo_sync_log"/>
<field name="state">code</field>
<field name="code">model._cron_cleanup_logs()</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field name="active">True</field>
</record>
</odoo>
- 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:
- Gets all
connectedinstances - For each instance, calls the WC API
- Applies dedup checks
- Creates/updates records
- Logs to
woo.sync.log - On failure: logs error, sends notification email if enabled
- Step 3: Add log cleanup to
woo_sync_log.py
@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
docker exec odoo-dev-app odoo -d fusion-dev -u fusion_woocommerce --stop-after-init
- Step 6: Commit
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:
- Enter WC URL + consumer key/secret
- Test connection
- Select default warehouse
- Generate Odoo API key
- 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
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:
- Fetches all WC products via paginated API calls (including variations for variable products)
- Auto-match phase 1: SKU match (
internal_reference=sku) - Auto-match phase 2: name similarity suggestions (using
difflib.SequenceMatcher) - Creates
woo.product.maprecords:- SKU matches → state
mappedautomatically - Name suggestions → state
unmappedwith suggested match stored - No match → state
unmapped
- SKU matches → state
- Returns action to open the product mapping view
-
Step 2: Write view with progress indicator
-
Step 3: Commit
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
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_searchcontroller endpoint viarpc() -
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
docker exec odoo-dev-app odoo -d fusion-dev -u fusion_woocommerce --stop-after-init
- Step 7: Commit
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
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
/**
* Plugin Name: Fusion WooDoo
* Plugin URI: https://fusionsoft.ca
* Description: Seamless Odoo integration for WooCommerce — sync products, orders, invoices, and inventory.
* Version: 1.0.0
* Author: Fusion Central
* Author URI: https://fusionsoft.ca
* Requires at least: 6.0
* Requires PHP: 8.0
* WC requires at least: 8.0
* WC tested up to: 9.0
* Text Domain: fusion-woodoo
* Domain Path: /languages
* License: GPL v2 or later
*/
if (!defined('ABSPATH')) exit;
define('FUSION_WOODOO_VERSION', '1.0.0');
define('FUSION_WOODOO_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('FUSION_WOODOO_PLUGIN_URL', plugin_dir_url(__FILE__));
// Check WooCommerce dependency
add_action('plugins_loaded', function() {
if (!class_exists('WooCommerce')) {
add_action('admin_notices', function() {
echo '<div class="error"><p><strong>Fusion WooDoo</strong> requires WooCommerce to be installed and active.</p></div>';
});
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
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 OdooPOST /wp-json/fusion-woodoo/v1/order/invoice— receives invoice PDFPOST /wp-json/fusion-woodoo/v1/order/delivery— receives delivery PDFPOST /wp-json/fusion-woodoo/v1/order/messages— receives customer-visible messages
Each endpoint:
- Validates Odoo API key from Authorization header
- Stores data as WC order meta
- Saves PDFs to protected upload directory
- Returns success/error
- Step 2: Commit
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:
- Register WC webhooks pointing to Odoo:
order.created→{odoo_url}/woo/webhook/orderorder.updated→{odoo_url}/woo/webhook/orderproduct.updated→{odoo_url}/woo/webhook/productcustomer.created→{odoo_url}/woo/webhook/customercustomer.updated→{odoo_url}/woo/webhook/customer
- On deactivation: unregister all webhooks
- On URL change: re-register with new URL
- Display webhook status in admin settings
- Step 2: Commit
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.phpand 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
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_wcmethod) -
Modify:
fusion-woo-odoo/fusion_woocommerce/models/woo_order.py(add processing methods) -
Step 1: Write
_sync_order_from_wconwoo.instance
Method that receives WC order data and:
- Finds/creates customer by email (via
woo.customer) - Checks dedup by
woo_order_id - Maps WC line items to Odoo products via
woo.product.map - Creates
sale.orderwith correct products, quantities, prices, taxes (viawoo.tax.map) - Confirms the SO
- Creates draft invoice
- Creates
woo.ordertracking record - Pushes SO details back to WC
- 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
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_validateoverride
When a delivery linked to a WC order is validated:
- Create
woo.shipmentrecord with tracking + carrier - If tracking number is set: automatically push shipping update to WC
- 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
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_availablefrom Odoo (filtered by configured warehouse) -
Push to WC
stock_quantityvia 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
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
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 reversestock.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
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.partnersearch) -
Create partner with billing/shipping addresses, phone, tag
-
Create
woo.customerlink record -
Push
odoo_customer_idback 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
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 upwoo.tax.mapto find matchingaccount.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_wcwhen 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.ratefor exchange rates -
Step 4: Commit
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()— extractsmail.messagerecords from linkedsale.orderchatter wheresubtype_idindicates "visible to customer" (not internal notes) -
Sends as JSON array
[{author, date, body}]to WP plugin REST endpoint -
Called from
action_push_shipping()andaction_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(viawoo_order_id) -
If so, automatically call
woo_order.action_push_invoice_pdf() -
Mark
invoice_synced = Trueon thewoo.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.orderbywoo_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
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
.pottranslation template -
Step 6: Commit
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
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
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
git add fusion-woo-odoo/
git commit -m "fix: deployment adjustments for production instances"