From 03c12fd9b50234ad79a944d4fe1e56febf78d55c Mon Sep 17 00:00:00 2001 From: Chinenye Nmoh Date: Thu, 22 Jan 2026 22:41:06 +0100 Subject: [PATCH] added a new penal charge endpoint --- app/config.py | 3 ++ app/models/loan_repayment_schedule.py | 17 +++++++++- app/routes/autocall.py | 41 ++++++++++++++++++++++++- app/services/loan_repayment_schedule.py | 4 ++- openapi.yml | 6 ++++ 5 files changed, 68 insertions(+), 3 deletions(-) diff --git a/app/config.py b/app/config.py index 1a1f906..4b5951b 100644 --- a/app/config.py +++ b/app/config.py @@ -60,6 +60,9 @@ class Config: OVERDUE_LOAN_BATCH_DELAY_SECONDS = int( os.getenv("OVERDUE_LOAN_BATCH_DELAY_SECONDS", 5) ) + OVERDUE_GRACE_PERIOD_DAYS = int(os.getenv("OVERDUE_GRACE_PERIOD_DAYS", 30)) + + BANK_CALL_API_TIME_OUT = os.getenv("BANK_CALL_API_TIME_OUT", 100) BANK_CALL_BASE_URL = os.getenv("BANK_CALL_BASE_URL", "https://bank-emulator.dev.simbrellang.net/api") BANK_CALL_SMS_BASE_URL= os.getenv("BANK_CALL_SMS_BASE_URL","https://first-advance-middleware-develop.fbn-devops-dev-asenv.appserviceenvironment.net/SMS") diff --git a/app/models/loan_repayment_schedule.py b/app/models/loan_repayment_schedule.py index 5ac5ca5..a6d8369 100644 --- a/app/models/loan_repayment_schedule.py +++ b/app/models/loan_repayment_schedule.py @@ -1,4 +1,4 @@ -from datetime import datetime, timezone +from datetime import datetime, timedelta, timezone from app.extensions import db from app.utils.logger import logger from sqlalchemy.exc import SQLAlchemyError @@ -115,6 +115,21 @@ class LoanRepaymentSchedule(db.Model): logger.error(f"Error fetching active overdue repayment schedules: {e}") return [] @classmethod + def get_overdue_repayment_schedule_with_grace_period(cls, grace_period_days): + """ + Get all overdue repayment schedules that are not repaid and beyond the grace period. + """ + try: + grace_period_date = datetime.now(timezone.utc) - timedelta(days=grace_period_days) + return cls.query.filter( + cls.due_date < grace_period_date, + cls.paid == False + ).order_by(cls.due_date.asc()).all() + except Exception as e: + logger.error(f"Error fetching overdue repayment schedules with grace period: {e}") + return [] + + @classmethod def get_partially_paid_overdue_repayment_schedule(cls): """ Get all overdue repayment schedules that are partially paid. diff --git a/app/routes/autocall.py b/app/routes/autocall.py index 0965c46..0c19993 100644 --- a/app/routes/autocall.py +++ b/app/routes/autocall.py @@ -440,7 +440,46 @@ def report(): except Exception as e: logger.error(f"Error generating or sending report: {e}") return ResponseHelper.error("Failed to send report", status_code=500, error=str(e)) - + +@autocall_bp.route("/process-penal-charges", methods=["GET"]) +def process_penal_charges(): + try: + OVERDUE_GRACE_PERIOD_DAYS = settings.OVERDUE_GRACE_PERIOD_DAYS + + overdue_loans = ( + LoanRepaymentScheduleService + .get_overdue_repayment_schedule_with_grace_period(OVERDUE_GRACE_PERIOD_DAYS) + ) + + logger.info(f"Found {len(overdue_loans)} overdue loans.") + + if not overdue_loans: + return ResponseHelper.success( + message="No overdue loans found", + status_code=200 + ) + + processed_loans = [] + + for loan in overdue_loans: + # TODO: apply penal charge logic here + # PenalChargeService.apply_penal_charge(loan) + + processed_loans.append(loan.to_dict()) + + return ResponseHelper.success( + message="Penal Charges Processed Successfully", + status_code=200, + data=processed_loans + ) + + except Exception as e: + logger.exception(f"Error processing penal charges: {e}") + return ResponseHelper.error( + "Failed to process penal charges", + status_code=500, + error=str(e) + ) @autocall_bp.route("/overdue-loans", methods=["GET"]) def overdue_loans(): diff --git a/app/services/loan_repayment_schedule.py b/app/services/loan_repayment_schedule.py index 1ded547..bf37117 100644 --- a/app/services/loan_repayment_schedule.py +++ b/app/services/loan_repayment_schedule.py @@ -47,7 +47,9 @@ class LoanRepaymentScheduleService: """ return LoanRepaymentSchedule.update_repayment_schedule_description(schedule_id, description) - + @classmethod + def get_overdue_repayment_schedule_with_grace_period(cls, grace_period_days): + return LoanRepaymentSchedule.get_overdue_repayment_schedule_with_grace_period(grace_period_days) @staticmethod def handle_schedule_updates(updated_loan, data, amount_collected, message, loan_data): """ diff --git a/openapi.yml b/openapi.yml index 3d7af31..4434beb 100644 --- a/openapi.yml +++ b/openapi.yml @@ -216,6 +216,12 @@ paths: responses: 200: description: A successful response + /autocall/process-penal-charges: + get: + summary: Get all overdue loans with grace period + responses: + 200: + description: A successful response /autocall/direct/loan: post: summary: Direct call for loan disbursement -- 2.34.1