# -*- coding: utf-8 -*- # Copyright 2024-2025 Nexa Systems Inc. # License OPL-1 (Odoo Proprietary License v1.0) # Part of the Fusion Claim Assistant product family. import logging from datetime import date, timedelta from odoo import models, api _logger = logging.getLogger(__name__) class ADPPostingScheduleMixin(models.AbstractModel): """Mixin providing ADP posting schedule calculation methods. This mixin can be inherited by any model that needs to calculate ADP posting dates and deadlines. Posting Schedule Logic: - Posting days occur every N days (default 14) from a base date - Submission deadline: Wednesday 6 PM before posting day - Delivery reminder: Tuesday of posting week - Billing reminder: Monday of posting week - Payment processed: Posting day + 7 days - Payment received: Posting day + 10 days """ _name = 'fusion_claims.adp.posting.schedule.mixin' _description = 'ADP Posting Schedule Mixin' @api.model def _get_adp_posting_base_date(self): """Get the configured base posting date from settings.""" ICP = self.env['ir.config_parameter'].sudo() base_date_str = ICP.get_param('fusion_claims.adp_posting_base_date', '2026-01-23') try: return date.fromisoformat(base_date_str) except (ValueError, TypeError): return date(2026, 1, 23) @api.model def _get_adp_posting_frequency(self): """Get the configured posting frequency in days from settings.""" ICP = self.env['ir.config_parameter'].sudo() frequency = ICP.get_param('fusion_claims.adp_posting_frequency_days', '14') try: return int(frequency) except (ValueError, TypeError): return 14 @api.model def _get_next_posting_date(self, from_date=None): """Calculate the next ADP posting date from a given date. Args: from_date: The date to calculate from (default: today) Returns: date: The next posting date """ if from_date is None: from_date = date.today() elif hasattr(from_date, 'date'): from_date = from_date.date() base_date = self._get_adp_posting_base_date() frequency = self._get_adp_posting_frequency() if frequency <= 0: frequency = 14 # Calculate days since base date days_diff = (from_date - base_date).days if days_diff < 0: # from_date is before base_date, so base_date is the next posting date return base_date # Calculate how many complete cycles have passed cycles_passed = days_diff // frequency # The next posting date is (cycles_passed + 1) * frequency days from base next_posting = base_date + timedelta(days=(cycles_passed + 1) * frequency) # If from_date equals a posting date, return the next one if days_diff % frequency == 0: return next_posting return next_posting @api.model def _get_current_posting_date(self, from_date=None): """Get the posting date for the current cycle (may be in the past). Args: from_date: The date to calculate from (default: today) Returns: date: The current cycle's posting date """ if from_date is None: from_date = date.today() elif hasattr(from_date, 'date'): from_date = from_date.date() base_date = self._get_adp_posting_base_date() frequency = self._get_adp_posting_frequency() if frequency <= 0: frequency = 14 days_diff = (from_date - base_date).days if days_diff < 0: return base_date cycles_passed = days_diff // frequency return base_date + timedelta(days=cycles_passed * frequency) @api.model def _get_posting_week_wednesday(self, posting_date): """Get the Wednesday before the posting date (submission deadline day). The submission deadline is Wednesday 6 PM of the posting week. Posting day is typically Friday, so Wednesday is 2 days before. Args: posting_date: The posting date Returns: date: The Wednesday before posting date """ if hasattr(posting_date, 'date'): posting_date = posting_date.date() # Find the Wednesday of the same week # weekday(): Monday=0, Tuesday=1, Wednesday=2, Thursday=3, Friday=4 days_since_wednesday = (posting_date.weekday() - 2) % 7 if days_since_wednesday == 0 and posting_date.weekday() != 2: days_since_wednesday = 7 return posting_date - timedelta(days=days_since_wednesday) @api.model def _get_posting_week_tuesday(self, posting_date): """Get the Tuesday of the posting week (delivery reminder date). Args: posting_date: The posting date Returns: date: The Tuesday of posting week """ if hasattr(posting_date, 'date'): posting_date = posting_date.date() # Find the Tuesday of the same week days_since_tuesday = (posting_date.weekday() - 1) % 7 if days_since_tuesday == 0 and posting_date.weekday() != 1: days_since_tuesday = 7 return posting_date - timedelta(days=days_since_tuesday) @api.model def _get_posting_week_monday(self, posting_date): """Get the Monday of the posting week (billing reminder date). Args: posting_date: The posting date Returns: date: The Monday of posting week """ if hasattr(posting_date, 'date'): posting_date = posting_date.date() # Find the Monday of the same week days_since_monday = posting_date.weekday() # Monday=0 return posting_date - timedelta(days=days_since_monday) @api.model def _get_expected_payment_date(self, posting_date): """Get the expected payment received date (posting + 10 days). Args: posting_date: The posting date Returns: date: The expected payment received date """ if hasattr(posting_date, 'date'): posting_date = posting_date.date() return posting_date + timedelta(days=10) @api.model def _get_payment_processed_date(self, posting_date): """Get the payment processed date (posting + 7 days). Args: posting_date: The posting date Returns: date: The payment processed date """ if hasattr(posting_date, 'date'): posting_date = posting_date.date() return posting_date + timedelta(days=7) @api.model def _get_adp_billing_reminder_user(self): """Get the configured billing reminder user from settings.""" ICP = self.env['ir.config_parameter'].sudo() user_id_str = ICP.get_param('fusion_claims.adp_billing_reminder_user_id', '') if user_id_str: try: user_id = int(user_id_str) return self.env['res.users'].browse(user_id).exists() except (ValueError, TypeError): pass return self.env['res.users'] @api.model def _get_adp_correction_reminder_users(self): """Get the configured correction reminder users from settings.""" ICP = self.env['ir.config_parameter'].sudo() user_ids_str = ICP.get_param('fusion_claims.adp_correction_reminder_user_ids', '') if user_ids_str: try: user_ids = [int(x.strip()) for x in user_ids_str.split(',') if x.strip()] return self.env['res.users'].browse(user_ids).exists() except (ValueError, TypeError): pass return self.env['res.users'] @api.model def _is_past_submission_deadline(self, posting_date=None, check_time=True): """Check if we're past the submission deadline for a posting cycle. Args: posting_date: The posting date to check (default: next posting date) check_time: If True, checks if past 6 PM on Wednesday Returns: bool: True if past deadline """ from datetime import datetime if posting_date is None: posting_date = self._get_next_posting_date() wednesday = self._get_posting_week_wednesday(posting_date) today = date.today() if today > wednesday: return True elif today == wednesday and check_time: # Check if past 6 PM (18:00) now = datetime.now() return now.hour >= 18 return False