diff --git a/odex25_base/sms_retry/__init__.py b/odex25_base/sms_retry/__init__.py deleted file mode 100644 index 534b25d0e..000000000 --- a/odex25_base/sms_retry/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# SMS Retry Module -# Copyright (C) 2024 Expert Co. Ltd. (). -# -############################################################################## -from . import models diff --git a/odex25_base/sms_retry/__manifest__.py b/odex25_base/sms_retry/__manifest__.py deleted file mode 100644 index 800a28b64..000000000 --- a/odex25_base/sms_retry/__manifest__.py +++ /dev/null @@ -1,44 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# SMS Retry Module -# Copyright (C) 2024 Expert Co. Ltd. (). -# -############################################################################## -{ - 'name': 'SMS Retry', - 'version': '14.0.1.0.0', - 'category': 'Odex25-base/Communication', - 'author': 'Expert Co. Ltd.', - 'company': 'Expert Co. Ltd.', - 'maintainer': 'Expert Co. Ltd.', - 'website': 'http://www.exp-sa.com', - 'summary': 'Retry failed SMS messages with configurable intervals', - 'description': """ -SMS Retry Module -================ -This module allows automatic retry of failed SMS messages with configurable: -* Retry intervals (in minutes) -* Maximum retry count -* Next retry time tracking per SMS record - -Features: ---------- -* Automatically retries failed SMS messages -* Configurable retry interval -* Configurable maximum retry attempts -* Stores next retry time in each SMS record -* Cron job processes retries based on next_retry_time - """, - 'depends': ['sms', 'base'], - 'data': [ - 'security/ir.model.access.csv', - 'data/ir_cron_data.xml', - 'views/res_config_settings_views.xml', - 'views/sms_sms_views.xml', - ], - 'installable': True, - 'auto_install': False, - 'application': False, - 'license': 'LGPL-3', -} diff --git a/odex25_base/sms_retry/data/ir_cron_data.xml b/odex25_base/sms_retry/data/ir_cron_data.xml deleted file mode 100644 index bd17134f8..000000000 --- a/odex25_base/sms_retry/data/ir_cron_data.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - SMS: Process Retry Queue - - code - model._process_retry_queue() - - 15 - minutes - -1 - - - - - diff --git a/odex25_base/sms_retry/models/__init__.py b/odex25_base/sms_retry/models/__init__.py deleted file mode 100644 index 257927690..000000000 --- a/odex25_base/sms_retry/models/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# SMS Retry Module -# Copyright (C) 2024 Expert Co. Ltd. (). -# -############################################################################## -from . import sms_sms -from . import res_config_settings diff --git a/odex25_base/sms_retry/models/res_config_settings.py b/odex25_base/sms_retry/models/res_config_settings.py deleted file mode 100644 index 4203123b2..000000000 --- a/odex25_base/sms_retry/models/res_config_settings.py +++ /dev/null @@ -1,51 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# SMS Retry Module -# Copyright (C) 2024 Expert Co. Ltd. (). -# -############################################################################## -from odoo import api, fields, models - - -class ResConfigSettings(models.TransientModel): - _inherit = 'res.config.settings' - - sms_retry_enabled = fields.Boolean( - string='Enable SMS Retry', - related='company_id.sms_retry_enabled', - readonly=False, - help='Enable automatic retry of failed SMS messages' - ) - sms_retry_interval_minutes = fields.Integer( - string='Retry Interval (minutes)', - related='company_id.sms_retry_interval_minutes', - readonly=False, - help='Time interval in minutes between retry attempts' - ) - sms_max_retry_count = fields.Integer( - string='Max Retry Count', - related='company_id.sms_max_retry_count', - readonly=False, - help='Maximum number of retry attempts per SMS' - ) - - -class ResCompany(models.Model): - _inherit = 'res.company' - - sms_retry_enabled = fields.Boolean( - string='Enable SMS Retry', - default=False, - help='Enable automatic retry of failed SMS messages for this company' - ) - sms_retry_interval_minutes = fields.Integer( - string='Retry Interval (minutes)', - default=60, - help='Time interval in minutes between retry attempts. Default: 60 minutes' - ) - sms_max_retry_count = fields.Integer( - string='Max Retry Count', - default=3, - help='Maximum number of retry attempts per SMS. Default: 3 attempts' - ) diff --git a/odex25_base/sms_retry/models/sms_sms.py b/odex25_base/sms_retry/models/sms_sms.py deleted file mode 100644 index f9138c3f6..000000000 --- a/odex25_base/sms_retry/models/sms_sms.py +++ /dev/null @@ -1,173 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# SMS Retry Module -# Copyright (C) 2024 Expert Co. Ltd. (). -# -############################################################################## -from datetime import datetime, timedelta -from odoo import api, fields, models, _ -import logging - -_logger = logging.getLogger(__name__) - - -class SmsSms(models.Model): - _inherit = 'sms.sms' - - retry_count = fields.Integer( - string='Retry Count', - default=0, - help='Number of retry attempts made for this SMS' - ) - max_retry_count = fields.Integer( - string='Max Retry Count', - default=3, - help='Maximum number of retry attempts allowed' - ) - next_retry_time = fields.Datetime( - string='Next Retry Time', - index=True, - help='Scheduled time for the next retry attempt' - ) - def _is_retry_enabled(self): - """Check if SMS retry is enabled.""" - company = self.env.user.company_id or self.env['res.company'].sudo().search([('sms_retry_enabled', '=', True)], limit=1) - if company and company.sms_retry_enabled: - return True - # Fallback to system parameter - return self.env['ir.config_parameter'].sudo().get_param('sms.retry.enabled', 'False') == 'True' - - def _get_retry_interval(self): - """Get retry interval in minutes from system parameters or company settings.""" - company = self.env.user.company_id or self.env['res.company'].sudo().search([('sms_retry_enabled', '=', True)], limit=1) - if company and company.sms_retry_enabled: - return company.sms_retry_interval_minutes - # Fallback to system parameter - return int(self.env['ir.config_parameter'].sudo().get_param('sms.retry.interval_minutes', 60)) - - def _get_max_retry_count(self): - """Get max retry count from company settings or system parameters.""" - company = self.env.user.company_id or self.env['res.company'].sudo().search([('sms_retry_enabled', '=', True)], limit=1) - if company and company.sms_retry_enabled: - return company.sms_max_retry_count - # Fallback to system parameter - return int(self.env['ir.config_parameter'].sudo().get_param('sms.retry.max_count', 3)) - - def _should_retry(self, error_code=None): - """Determine if SMS should be retried based on error code and retry settings.""" - self.ensure_one() - if not self._is_retry_enabled(): - return False - - # Don't retry if max retry count reached - if self.retry_count >= self.max_retry_count: - return False - - # Don't retry certain error codes that won't resolve with retry - non_retryable_errors = ['sms_number_missing', 'sms_number_format', 'sms_blacklist', 'sms_duplicate'] - if error_code and error_code in non_retryable_errors: - return False - - return True - - def _schedule_retry(self, error_code=None): - """Schedule a retry for failed SMS.""" - self.ensure_one() - if not self._should_retry(error_code): - _logger.info("SMS %s will not be retried. Error: %s, Retry count: %s/%s", - self.id, error_code, self.retry_count, self.max_retry_count) - return False - - interval_minutes = self._get_retry_interval() - next_retry = fields.Datetime.now() + timedelta(minutes=interval_minutes) - - self.write({ - 'next_retry_time': next_retry, - 'max_retry_count': self._get_max_retry_count(), - }) - - _logger.info("SMS %s scheduled for retry at %s (attempt %s/%s)", - self.id, next_retry, self.retry_count + 1, self.max_retry_count) - return True - - def _retry_sms(self): - """Retry sending failed SMS by resetting state to outgoing.""" - self.ensure_one() - if self.state != 'error': - return False - - if self.retry_count >= self.max_retry_count: - _logger.info("SMS %s has reached max retry count (%s)", self.id, self.max_retry_count) - return False - - # Reset to outgoing state to trigger normal send process - self.write({ - 'state': 'outgoing', - 'retry_count': self.retry_count + 1, - 'next_retry_time': False, - }) - _logger.info("SMS %s retry attempt %s initiated", self.id, self.retry_count) - - # Trigger immediate send - try: - self.send(delete_all=False, auto_commit=True, raise_exception=False) - except Exception as e: - _logger.exception("Error during SMS %s retry: %s", self.id, e) - # State will be updated by _postprocess_iap_sent_sms - return False - - return True - - def _postprocess_iap_sent_sms(self, iap_results, failure_reason=None, delete_all=False): - """Override to schedule retries for failed SMS.""" - # Call parent method first - super(SmsSms, self)._postprocess_iap_sent_sms(iap_results, failure_reason=failure_reason, delete_all=delete_all) - - # Process retries for failed SMS (only if retry is enabled) - if not self._is_retry_enabled(): - return - - for result in iap_results: - if result.get('state') != 'success': - sms_id = result.get('res_id') - if sms_id: - sms = self.browse(sms_id) - if sms.exists() and sms.state == 'error': - error_code = self.IAP_TO_SMS_STATE.get(result.get('state'), 'sms_server') - sms._schedule_retry(error_code=error_code) - - @api.model - def _process_retry_queue(self): - """Process SMS records that are ready for retry. - Called by cron job to find and retry SMS messages.""" - company_ids = self.env['res.company'].search([('sms_retry_enabled', '=', True)]) - if not company_ids: - _logger.debug("SMS retry is not enabled for any company") - return - - now = fields.Datetime.now() - domain = [ - ('state', '=', 'error'), - ('next_retry_time', '<=', now), - ('next_retry_time', '!=', False), - ] - - sms_to_retry = self.search(domain, limit=1000) - - if sms_to_retry: - _logger.info("Processing %s SMS records for retry", len(sms_to_retry)) - for sms in sms_to_retry: - try: - # Check if retry is still enabled and valid before processing - # This will also check retry_count < max_retry_count - if sms._is_retry_enabled() and sms._should_retry(): - sms._retry_sms() - else: - # Clear next_retry_time if retry is no longer valid - sms.write({'next_retry_time': False}) - except Exception as e: - _logger.exception("Error retrying SMS %s: %s", sms.id, e) - - # Note: Commit is handled automatically by the cron job framework - # Do not commit here as it breaks test savepoints diff --git a/odex25_base/sms_retry/security/ir.model.access.csv b/odex25_base/sms_retry/security/ir.model.access.csv deleted file mode 100644 index 28d6c1120..000000000 --- a/odex25_base/sms_retry/security/ir.model.access.csv +++ /dev/null @@ -1,2 +0,0 @@ -id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_sms_sms_user,sms.sms.user,model_sms_sms,base.group_user,1,1,1,1 diff --git a/odex25_base/sms_retry/views/res_config_settings_views.xml b/odex25_base/sms_retry/views/res_config_settings_views.xml deleted file mode 100644 index 48d460181..000000000 --- a/odex25_base/sms_retry/views/res_config_settings_views.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - res.config.settings.view.form.inherit.sms.retry - res.config.settings - - - -
-

SMS Retry Configuration

-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/odex25_base/sms_retry/views/sms_sms_views.xml b/odex25_base/sms_retry/views/sms_sms_views.xml deleted file mode 100644 index f8f314864..000000000 --- a/odex25_base/sms_retry/views/sms_sms_views.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - sms.sms.view.form.inherit.sms.retry - sms.sms - - - - - - - - - - - - - - - - - - sms.sms.view.tree.inherit.sms.retry - sms.sms - - - - - - - - - -