feat: add cron jobs, sync engine scaffolding, log cleanup, and email templates

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-03-31 20:41:04 -04:00
parent 0ce599c4ac
commit 116c0b03bf
5 changed files with 201 additions and 1 deletions

View File

@@ -12,6 +12,8 @@
'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',

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<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>
<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>
<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>
<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>
<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>

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="woo_sync_failure_notification" model="mail.template">
<field name="name">WooCommerce: Sync Failure Notification</field>
<field name="model_id" ref="model_woo_instance"/>
<field name="subject">WooCommerce Sync Failed: ${object.name}</field>
<field name="body_html"><![CDATA[
<p>Hello,</p>
<p>A WooCommerce sync has failed for the following instance:</p>
<ul>
<li><strong>Instance:</strong> ${object.name}</li>
<li><strong>Sync Type:</strong> ${ctx.get('sync_type', 'Unknown')}</li>
<li><strong>Error:</strong> ${ctx.get('error_message', 'No details available')}</li>
<li><strong>Timestamp:</strong> ${object.last_sync or 'N/A'}</li>
</ul>
<p>Please review your WooCommerce integration settings and resolve the issue.</p>
<p>— Fusion WooCommerce</p>
]]></field>
<field name="auto_delete">True</field>
</record>
<record id="woo_new_order_notification" model="mail.template">
<field name="name">WooCommerce: New Order Notification</field>
<field name="model_id" ref="model_woo_instance"/>
<field name="subject">New WooCommerce Order: ${ctx.get('order_number', '')}</field>
<field name="body_html"><![CDATA[
<p>Hello,</p>
<p>A new WooCommerce order has been received:</p>
<ul>
<li><strong>Order Number:</strong> ${ctx.get('order_number', 'N/A')}</li>
<li><strong>Customer:</strong> ${ctx.get('customer_name', 'N/A')}</li>
<li><strong>Total:</strong> $${ctx.get('order_total', '0.00')}</li>
</ul>
<p>Log in to Odoo to review and process this order.</p>
<p>— Fusion WooCommerce</p>
]]></field>
<field name="auto_delete">True</field>
</record>
</odoo>

View File

@@ -115,3 +115,94 @@ class WooInstance(models.Model):
'message': message,
'company_id': self.company_id.id,
})
# ------------------------------------------------------------------
# Cron entry points
# ------------------------------------------------------------------
@api.model
def _cron_sync_products(self):
"""Sync product prices and inventory for all connected instances."""
instances = self.search([('state', '=', 'connected'), ('sync_products', '=', True)])
for instance in instances:
try:
instance._sync_products()
except Exception as e:
_logger.error("Product sync failed for %s: %s", instance.name, str(e))
instance._log_sync('product', 'woo_to_odoo', instance.name, 'failed', str(e))
instance._notify_failure('product', str(e))
@api.model
def _cron_sync_orders(self):
"""Fetch new orders from all connected WooCommerce instances."""
instances = self.search([('state', '=', 'connected'), ('sync_orders', '=', True)])
for instance in instances:
try:
instance._sync_orders()
except Exception as e:
_logger.error("Order sync failed for %s: %s", instance.name, str(e))
instance._log_sync('order', 'woo_to_odoo', instance.name, 'failed', str(e))
instance._notify_failure('order', str(e))
@api.model
def _cron_sync_inventory(self):
"""Push inventory levels to WooCommerce for all connected instances."""
instances = self.search([('state', '=', 'connected'), ('sync_inventory', '=', True)])
for instance in instances:
try:
instance._sync_inventory()
except Exception as e:
_logger.error("Inventory sync failed for %s: %s", instance.name, str(e))
instance._log_sync('inventory', 'odoo_to_woo', instance.name, 'failed', str(e))
instance._notify_failure('inventory', str(e))
@api.model
def _cron_sync_customers(self):
"""Sync customer address updates to WooCommerce."""
instances = self.search([('state', '=', 'connected'), ('sync_customers', '=', True)])
for instance in instances:
try:
instance._sync_customers()
except Exception as e:
_logger.error("Customer sync failed for %s: %s", instance.name, str(e))
instance._log_sync('customer', 'odoo_to_woo', instance.name, 'failed', str(e))
instance._notify_failure('customer', str(e))
# ------------------------------------------------------------------
# Sync method placeholders (filled in during later tasks)
# ------------------------------------------------------------------
def _sync_products(self):
"""Sync products — implemented in Task 22."""
self.ensure_one()
_logger.info("Product sync for %s — not yet implemented", self.name)
def _sync_orders(self):
"""Sync orders — implemented in Task 20."""
self.ensure_one()
_logger.info("Order sync for %s — not yet implemented", self.name)
def _sync_inventory(self):
"""Sync inventory — implemented in Task 22."""
self.ensure_one()
_logger.info("Inventory sync for %s — not yet implemented", self.name)
def _sync_customers(self):
"""Sync customers — implemented in Task 25."""
self.ensure_one()
_logger.info("Customer sync for %s — not yet implemented", self.name)
def _notify_failure(self, sync_type, error_message):
"""Send email notification on sync failure."""
self.ensure_one()
if not self.notify_on_failure or not self.notify_user_ids:
return
template = self.env.ref(
'fusion_woocommerce.woo_sync_failure_notification', raise_if_not_found=False,
)
if template:
for user in self.notify_user_ids:
template.with_context(
sync_type=sync_type,
error_message=error_message,
).send_mail(self.id, force_send=True, email_values={'email_to': user.email})

View File

@@ -1,4 +1,4 @@
from odoo import fields, models
from odoo import api, fields, models
class WooSyncLog(models.Model):
@@ -28,3 +28,14 @@ class WooSyncLog(models.Model):
company_id = fields.Many2one(
'res.company', default=lambda self: self.env.company,
)
@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(), hours=90 * 24)
cutoff_error = fields.Datetime.subtract(fields.Datetime.now(), hours=180 * 24)
self.search([
'|',
'&', ('state', '!=', 'failed'), ('create_date', '<', cutoff_success),
'&', ('state', '=', 'failed'), ('create_date', '<', cutoff_error),
]).unlink()