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:
@@ -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',
|
||||
|
||||
57
fusion-woo-odoo/fusion_woocommerce/data/cron.xml
Normal file
57
fusion-woo-odoo/fusion_woocommerce/data/cron.xml
Normal 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>
|
||||
39
fusion-woo-odoo/fusion_woocommerce/data/mail_template.xml
Normal file
39
fusion-woo-odoo/fusion_woocommerce/data/mail_template.xml
Normal 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>
|
||||
@@ -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})
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user