bemade-addons/odoo_to_odoo_sync/models/sync_queue.py
mathis b792e43873 [IMP] Odoo Sync Modules: Major Enhancements and Fixes
- Added comprehensive README files for all three sync modules
- Implemented advanced logging system with multiple log levels (DEBUG, INFO, WARNING, ERROR)
- Added automatic sensitive data masking for security in log payloads
- Implemented log retention policy (90 days local, 7 years in AWS S3 Glacier)
- Added AWS S3 integration for long-term log archiving
- Fixed validation error with bemade_instance_id in sync model creation
- Refactored model inheritance structure from _inherit to _inherits for proper delegation
- Fixed duplicate sync model prevention logic
- Restored Test Connection button in instance view
- Fixed connection test logging to properly record success/failure
- Fixed queue creation for connection test logs
- Removed unnecessary Synchronized Module tab from odoo_to_odoo_bemade
- Added database indexing on create_date for performance optimization
- Fixed foreign key constraint violations in field mappings creation
- Enhanced error handling throughout the synchronization process
2025-08-15 08:40:17 -04:00

236 lines
6.8 KiB
Python

# Copyright 2025 Codeium
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.html)
"""Synchronization Queue Management.
This module implements the queue system for managing synchronization operations
between Odoo instances. It handles the scheduling, retry logic, and status
tracking of synchronization tasks.
"""
import json
import logging
from datetime import timedelta
from odoo import api, fields, models
_logger = logging.getLogger(__name__)
class OdooSyncQueue(models.Model):
_name = 'odoo.sync.queue'
_description = 'Synchronization Queue'
_order = 'priority desc, retry_count, create_date'
name = fields.Char(
string='Name',
compute='_compute_name',
store=True
)
model_id = fields.Many2one(
comodel_name='odoo.sync.model',
string='Model',
required=True
)
record_id = fields.Integer(
string='Record ID',
required=True
)
operation = fields.Selection(
selection=[
('create', 'Create'),
('write', 'Update'),
('unlink', 'Delete')
],
required=True
)
state = fields.Selection(
selection=[
('draft', 'Draft'),
('pending', 'Pending'),
('processing', 'Processing'),
('done', 'Done'),
('error', 'Error'),
('cancelled', 'Cancelled')
],
default='draft',
required=True
)
priority = fields.Integer(
string='Priority',
default=1
)
retry_count = fields.Integer(
string='Retry Count',
default=0
)
max_retries = fields.Integer(
string='Max Retries',
default=3
)
next_retry = fields.Datetime(
string='Next Retry'
)
data = fields.Text(
string='Data',
help='JSON data to synchronize'
)
error_message = fields.Text(
string='Error Message'
)
log_ids = fields.One2many(
comodel_name='odoo.sync.log',
inverse_name='queue_id',
string='Logs'
)
@api.depends('model_id', 'record_id', 'operation')
def _compute_name(self):
"""Compute the display name of the queue entry.
The name is generated using the format: 'operation - model_name#record_id'
e.g., 'create - res.partner#42'
Triggered by changes to: model_id, record_id, or operation fields.
"""
for record in self:
record.name = f'{record.operation} - {record.model_id.name}#{record.record_id}'
def action_reset(self):
"""Reset a failed queue entry to pending state.
This method:
- Changes state back to 'pending'
- Resets retry count to 0
- Clears error message and next retry time
Returns:
dict: Result of the write operation
"""
return self.write({
'state': 'pending',
'retry_count': 0,
'error_message': False,
'next_retry': False
})
def action_cancel(self):
"""Cancel the queue entry.
Changes the state of the queue entry to 'cancelled',
preventing further processing attempts.
Returns:
dict: Result of the write operation
"""
return self.write({'state': 'cancelled'})
@api.model
def _process_sync_queue(self):
"""Process pending synchronization queue entries.
This method is called by the cron job to process pending queue entries.
It will:
1. Find pending entries that are ready for processing
2. Dequeue each entry and pass it to the sync manager for processing
3. Update the entry status based on the processing result
Returns:
bool: True if processing completed successfully
"""
# Find pending entries ready for processing
domain = [
('state', '=', 'pending'),
'|',
('next_retry', '=', False),
('next_retry', '<=', fields.Datetime.now())
]
entries = self.search(domain, order='priority desc, retry_count, create_date')
for entry in entries:
# Dequeue the item and process it
sync_manager = self.env['odoo.sync.manager']
sync_manager._process_dequeued_item(self.dequeue(entry.id))
return True
@api.model
def dequeue(self, queue_id):
"""Dequeue a specific queue item for processing.
This method explicitly implements the "Dequeue" step in the synchronization
sequence diagram. It marks the queue item as processing and returns it
for further processing by the sync manager.
Args:
queue_id: ID of the queue item to dequeue
Returns:
odoo.sync.queue: The dequeued queue item record or False if not found
"""
queue_item = self.browse(queue_id)
if not queue_item.exists():
_logger.error(f"[SYNC QUEUE] Cannot dequeue item {queue_id}: record not found")
return False
_logger.debug(f"[SYNC QUEUE] Dequeuing item {queue_id}")
queue_item.write({'state': 'processing'})
return queue_item
def mark_success(self):
"""Mark this queue item as successfully processed.
This method implements the 'Mark Success' step in the synchronization sequence diagram.
"""
_logger.debug(f"[SYNC QUEUE] Marking item {self.id} as done")
self.write({'state': 'done'})
# Create success log
self.env['odoo.sync.log'].create({
'queue_id': self.id,
'state': 'success',
'message': f'Synchronisation réussie'
})
def increment_retry(self, error_message):
"""Increment the retry count for this queue item.
This method implements the 'Increment Retry' step in the synchronization sequence diagram.
Args:
error_message: The error message to log
"""
_logger.debug(f"[SYNC QUEUE] Incrementing retry count for item {self.id}")
vals = {
'state': 'error',
'error_message': error_message,
'retry_count': self.retry_count + 1
}
if self.retry_count + 1 < self.max_retries:
# Exponential backoff
delay = 2 ** (self.retry_count + 1)
vals['next_retry'] = fields.Datetime.now() + timedelta(minutes=delay)
self.write(vals)
# Create error log
self.env['odoo.sync.log'].create({
'queue_id': self.id,
'state': 'error',
'message': error_message,
'details': error_message
})
return True