from app.models.loan_repayment_schedule import LoanRepaymentSchedule from app.utils.logger import logger from app.enums.loan_status import LoanStatus from decimal import Decimal, ROUND_HALF_UP class LoanRepaymentScheduleService: @classmethod def get_repayment_schedule_by_loan_id(cls, loan_id, include_paid=True): return LoanRepaymentSchedule.get_repayment_schedule_by_loan_id(loan_id, include_paid=include_paid) @classmethod def get_overdue_repayment_schedule(cls): return LoanRepaymentSchedule.get_overdue_repayment_schedule() @classmethod def get_repayment_schedule_by_id_and_transaction_id(cls, id, transaction_id): return LoanRepaymentSchedule.get_repayment_schedule_by_id_and_transaction_id(id, transaction_id) @classmethod def get_repayment_schedule_by_transaction_id(cls, transaction_id): return LoanRepaymentSchedule.get_repayment_schedule_by_transaction_id(transaction_id) @classmethod def update_repayment_schedule_status(cls, schedule_id): """ Update repayment schedule status. """ return LoanRepaymentSchedule.update_repayment_schedule_status(schedule_id) @classmethod def update_repayment_schedule_balance(cls, schedule_id, amount_collected): """ Update repayment schedule balance. """ return LoanRepaymentSchedule.update_repayment_schedule_balance(schedule_id, amount_collected) @classmethod def update_repayment_schedule_description(cls, schedule_id, description): """ Update repayment schedule description. """ return LoanRepaymentSchedule.update_repayment_schedule_description(schedule_id, description) @staticmethod def handle_schedule_updates(updated_loan, data, amount_collected, message, loan_data): """ Handles updating loan repayment schedules depending on loan status and overdue schedule data. """ try: # Case 1: Loan fully repaid → mark all schedules paid if updated_loan and updated_loan.get('status') == LoanStatus.REPAID: repayment_schedule = LoanRepaymentScheduleService.get_repayment_schedule_by_loan_id( updated_loan['debtId'], include_paid=False ) logger.info(f'Loan repayment schedule: {repayment_schedule}') if repayment_schedule: for installment in repayment_schedule: try: logger.info(f'Processing installment: {installment}') LoanRepaymentScheduleService.update_repayment_schedule_status(installment.id) LoanRepaymentScheduleService.update_repayment_schedule_description( installment.id, message ) logger.info(f'Updated installment {installment.id} as paid') except Exception as e: logger.error(f"Failed to update installment {installment.id}: {e}") logger.info('All installments processed') # Case 2: Partial repayment made on a full loan without overdueLoanScheduleId elif updated_loan and updated_loan.get('status') == LoanStatus.ACTIVE_PARTIAL and not data.get('overdueLoanScheduleId'): logger.info("Partial repayment detected, but no overdue schedule ID provided.") # TODO: implement proportional installment updates # Case 3: when we are processing Overdue schedule repayment → update balance & description elif data.get('overdueLoanScheduleId') is not None: logger.info(f"Overdue loan schedule ID: {data['overdueLoanScheduleId']}") try: schedule_to_update = LoanRepaymentScheduleService.get_repayment_schedule_by_id_and_transaction_id( data["overdueLoanScheduleId"], data["transactionId"] ) logger.info(f"Schedule to update: {schedule_to_update}") if schedule_to_update is None: logger.warning( f"Repayment schedule not found for ID {data['overdueLoanScheduleId']} " f"and transaction ID {loan_data['transactionId']}" ) else: if not schedule_to_update.paid: update_schedule_balance = LoanRepaymentScheduleService.update_repayment_schedule_balance( schedule_to_update.id, amount_collected ) logger.info(f"Updated loan schedule balance: {update_schedule_balance}") LoanRepaymentScheduleService.update_repayment_schedule_description( schedule_to_update.id, message ) except Exception as e: logger.error(f"Failed to update repayment schedule installment: {e}") except Exception as e: logger.error(f"Unexpected error while handling schedule updates: {e}")