added a new penal charge endpoint #63

Merged
ameye merged 1 commits from test into master 2026-01-23 01:04:23 +00:00
5 changed files with 68 additions and 3 deletions
+3
View File
@@ -60,6 +60,9 @@ class Config:
OVERDUE_LOAN_BATCH_DELAY_SECONDS = int( OVERDUE_LOAN_BATCH_DELAY_SECONDS = int(
os.getenv("OVERDUE_LOAN_BATCH_DELAY_SECONDS", 5) 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_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_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") BANK_CALL_SMS_BASE_URL= os.getenv("BANK_CALL_SMS_BASE_URL","https://first-advance-middleware-develop.fbn-devops-dev-asenv.appserviceenvironment.net/SMS")
+16 -1
View File
@@ -1,4 +1,4 @@
from datetime import datetime, timezone from datetime import datetime, timedelta, timezone
from app.extensions import db from app.extensions import db
from app.utils.logger import logger from app.utils.logger import logger
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
@@ -115,6 +115,21 @@ class LoanRepaymentSchedule(db.Model):
logger.error(f"Error fetching active overdue repayment schedules: {e}") logger.error(f"Error fetching active overdue repayment schedules: {e}")
return [] return []
@classmethod @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): def get_partially_paid_overdue_repayment_schedule(cls):
""" """
Get all overdue repayment schedules that are partially paid. Get all overdue repayment schedules that are partially paid.
+39
View File
@@ -441,6 +441,45 @@ def report():
logger.error(f"Error generating or sending report: {e}") logger.error(f"Error generating or sending report: {e}")
return ResponseHelper.error("Failed to send report", status_code=500, error=str(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"]) @autocall_bp.route("/overdue-loans", methods=["GET"])
def overdue_loans(): def overdue_loans():
+3 -1
View File
@@ -47,7 +47,9 @@ class LoanRepaymentScheduleService:
""" """
return LoanRepaymentSchedule.update_repayment_schedule_description(schedule_id, description) 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 @staticmethod
def handle_schedule_updates(updated_loan, data, amount_collected, message, loan_data): def handle_schedule_updates(updated_loan, data, amount_collected, message, loan_data):
""" """
+6
View File
@@ -216,6 +216,12 @@ paths:
responses: responses:
200: 200:
description: A successful response 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: /autocall/direct/loan:
post: post:
summary: Direct call for loan disbursement summary: Direct call for loan disbursement