bemade-addons/odoo_to_odoo_bemade/models/sync_manager.py
2025-05-29 21:35:47 -04:00

393 lines
14 KiB
Python

# Copyright 2025 Bemade
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.html)
"""Synchronization Manager for Bemade.
This module extends the base synchronization manager to add Bemade-specific
functionality for managing synchronization operations between Odoo instances.
"""
import logging
from datetime import datetime, timedelta
from odoo import api, fields, models, _
from odoo.exceptions import UserError
_logger = logging.getLogger(__name__)
class OdooToBemadeSyncManager(models.Model):
"""Synchronization manager for Bemade.
Extends the base synchronization manager to handle Bemade-specific
synchronization requirements.
"""
_name = 'odoo.to.bemade.sync.manager'
_description = 'Bemade Sync Manager'
_inherit = 'odoo.sync.manager'
# Add Bemade-specific fields here
bemade_sync_enabled = fields.Boolean(
string='Enable Bemade Sync',
default=True,
help='Enable synchronization with Bemade instances',
)
@api.model
def cron_process_sync_queue(self):
"""Process pending synchronization queue entries.
This method is called by a scheduled action to process
pending queue entries for Bemade synchronization.
Returns:
Boolean indicating success
"""
if not self.env['ir.config_parameter'].sudo().get_param('odoo_to_odoo_bemade.sync_enabled', True):
_logger.info("Bemade synchronization is disabled in system parameters")
return False
# Get active instances
active_instances = self.env['odoo.to.bemade.instance'].search([
('active', '=', True),
('state', '=', 'connected')
])
if not active_instances:
_logger.info("No active and connected Bemade instances found")
return False
# Process queue entries for each active instance
for instance in active_instances:
self._process_queue_for_instance(instance)
return True
def _process_queue_for_instance(self, instance):
"""Process queue entries for a specific Bemade instance.
Args:
instance: The Bemade instance to process queue entries for
Returns:
Boolean indicating success
"""
# Get pending queue entries for this instance
queue_entries = self.env['odoo.to.bemade.sync.queue'].search([
('bemade_instance_id', '=', instance.id),
('state', '=', 'pending'),
'|',
('next_retry', '=', False),
('next_retry', '<=', fields.Datetime.now())
], order='priority, id', limit=50)
if not queue_entries:
_logger.info("No pending queue entries for Bemade instance %s", instance.name)
return False
# Process each queue entry
for entry in queue_entries:
try:
self._process_queue_entry(entry, instance)
except Exception as e:
self.env['odoo.to.bemade.sync.log'].log(
operation=entry.operation_type,
model=entry.model,
record_id=entry.record_id,
result='error',
details=str(e),
instance_id=instance.id
)
# Update entry with error information
retry_count = entry.retry_count + 1
max_retries = int(self.env['ir.config_parameter'].sudo().get_param('odoo_to_odoo_bemade.max_retries', 5))
if retry_count >= max_retries:
entry.write({
'state': 'failed',
'retry_count': retry_count,
'error_message': str(e)
})
else:
# Exponential backoff for retries
delay_minutes = 5 * (2 ** (retry_count - 1))
next_retry = fields.Datetime.now() + timedelta(minutes=delay_minutes)
entry.write({
'state': 'pending',
'retry_count': retry_count,
'error_message': str(e),
'next_retry': next_retry
})
return True
def _process_queue_entry(self, entry, instance):
"""Process a single queue entry.
Args:
entry: The queue entry to process
instance: The Bemade instance to process the entry for
Returns:
Boolean indicating success
"""
# Mark the entry as being processed
entry.write({'state': 'processing'})
# Process based on operation type
if entry.operation_type == 'create':
result = self._sync_create(entry, instance)
elif entry.operation_type == 'update':
result = self._sync_update(entry, instance)
elif entry.operation_type == 'delete':
result = self._sync_delete(entry, instance)
else:
raise UserError(_("Unknown operation type: %s") % entry.operation_type)
# Process Bemade-specific logic
entry.process_bemade_specific_logic()
# Mark the entry as processed
entry.write({'state': 'done'})
# Log success
self.env['odoo.to.bemade.sync.log'].log(
operation=entry.operation_type,
model=entry.model,
record_id=entry.record_id,
result='success',
instance_id=instance.id
)
return True
def _sync_create(self, entry, instance):
"""Synchronize a record creation.
Args:
entry: The queue entry to process
instance: The Bemade instance to sync with
Returns:
The result of the create operation
"""
# Get the model and record
model = self.env[entry.model]
record = model.browse(entry.record_id)
# Get the connection to the remote instance
connection = instance.get_connection()
# Map record data to target model fields
sync_model = self.env['odoo.sync.model'].search([
('name', '=', entry.model),
('instance_id', '=', instance.id)
], limit=1)
if not sync_model:
raise UserError(_("No synchronization configuration found for model %s on instance %s") %
(entry.model, instance.name))
# Prepare the data for the remote instance
# This would involve mapping fields from the local model to the remote model
# based on the configuration in sync_model and its fields
remote_data = self._prepare_remote_data(record, sync_model)
# Create the record on the remote instance
target_model = sync_model.target_model or entry.model
result = connection.create(target_model, remote_data)
return result
def _sync_update(self, entry, instance):
"""Synchronize a record update.
Args:
entry: The queue entry to process
instance: The Bemade instance to sync with
Returns:
The result of the update operation
"""
# Implementation similar to _sync_create, but for updates
# Get the model and record
model = self.env[entry.model]
record = model.browse(entry.record_id)
# Get the connection to the remote instance
connection = instance.get_connection()
# Map record data to target model fields
sync_model = self.env['odoo.sync.model'].search([
('name', '=', entry.model),
('instance_id', '=', instance.id)
], limit=1)
if not sync_model:
raise UserError(_("No synchronization configuration found for model %s on instance %s") %
(entry.model, instance.name))
# Prepare the data for the remote instance
remote_data = self._prepare_remote_data(record, sync_model)
# Get the remote ID
remote_id = self._get_remote_id(record, sync_model, instance)
# Update the record on the remote instance
target_model = sync_model.target_model or entry.model
result = connection.write(target_model, remote_id, remote_data)
return result
def _sync_delete(self, entry, instance):
"""Synchronize a record deletion.
Args:
entry: The queue entry to process
instance: The Bemade instance to sync with
Returns:
The result of the delete operation
"""
# Get the connection to the remote instance
connection = instance.get_connection()
# Map record data to target model fields
sync_model = self.env['odoo.sync.model'].search([
('name', '=', entry.model),
('instance_id', '=', instance.id)
], limit=1)
if not sync_model:
raise UserError(_("No synchronization configuration found for model %s on instance %s") %
(entry.model, instance.name))
# Get the remote ID
# Since the record might already be deleted locally, we need to get the remote ID
# from a mapping table or some other mechanism
remote_id = self._get_remote_id_for_deleted_record(entry.record_id, sync_model, instance)
# Delete the record on the remote instance
target_model = sync_model.target_model or entry.model
result = connection.unlink(target_model, remote_id)
return result
def _prepare_remote_data(self, record, sync_model):
"""Prepare record data for the remote instance.
Args:
record: The record to prepare data for
sync_model: The synchronization model configuration
Returns:
Dictionary of field values for the remote instance
"""
# Get the field mappings from the sync model
field_mappings = self.env['odoo.sync.model.field'].search([
('sync_model_id', '=', sync_model.id)
])
remote_data = {}
# Apply field mappings
for mapping in field_mappings:
source_field = mapping.source_field
target_field = mapping.target_field or source_field
# Get the value from the source field
value = record[source_field]
# Apply any transformations defined in the mapping
# This is a simplified example - real implementation would handle
# complex field types, relationships, etc.
if mapping.transform_type == 'direct':
remote_data[target_field] = value
elif mapping.transform_type == 'function':
# Call a function to transform the value
transform_method = getattr(self, mapping.transform_function, None)
if transform_method:
remote_data[target_field] = transform_method(value, record, mapping)
else:
_logger.warning("Transform function %s not found", mapping.transform_function)
remote_data[target_field] = value
return remote_data
def _get_remote_id(self, record, sync_model, instance):
"""Get the ID of the record on the remote instance.
Args:
record: The local record
sync_model: The synchronization model configuration
instance: The remote instance
Returns:
ID of the record on the remote instance
"""
# In a real implementation, this would involve looking up the remote ID
# from a mapping table or using an external ID mechanism
# This is just a placeholder implementation
return record.id # Simplified - in reality would be a different ID
def _get_remote_id_for_deleted_record(self, record_id, sync_model, instance):
"""Get the ID of a deleted record on the remote instance.
Args:
record_id: The local record ID
sync_model: The synchronization model configuration
instance: The remote instance
Returns:
ID of the record on the remote instance
"""
# Similar to _get_remote_id but for records that have been deleted locally
return record_id # Simplified - in reality would be a different ID
def sync_all(self):
"""Synchronize all active models with Bemade instances.
Returns:
Boolean indicating success
"""
# Get active instances
active_instances = self.env['odoo.to.bemade.instance'].search([
('active', '=', True),
('state', '=', 'connected')
])
if not active_instances:
raise UserError(_("No active and connected Bemade instances found"))
# Get active sync models
active_models = self.env['odoo.sync.model'].search([
('active', '=', True),
('instance_id', 'in', active_instances.ids)
])
if not active_models:
raise UserError(_("No active synchronization models configured"))
# Synchronize each model
for sync_model in active_models:
model = self.env[sync_model.name]
instance = sync_model.instance_id
# Get records to synchronize
# This is a simplified approach - in a real implementation,
# you would need to handle determining which records need to be synced
records = model.search([])
# Create queue entries for each record
queue = self.env['odoo.to.bemade.sync.queue']
for record in records:
queue.create_sync_job(
instance_id=instance.id,
model_name=sync_model.name,
record_id=record.id,
operation_type='update' # Assuming update for existing records
)
return True