added penal charge to schedule

This commit was merged in pull request #67.
This commit is contained in:
Chinenye Nmoh
2026-03-12 12:50:25 +01:00
parent 6593aedc56
commit 1a0e1f324a
7 changed files with 245 additions and 67 deletions
+37 -50
View File
@@ -28,9 +28,9 @@ class LoanRepaymentSchedule(db.Model):
partial_balance = db.Column(db.Float, default=0.0)
created_at = db.Column(db.DateTime, default=datetime.now(timezone.utc))
updated_at = db.Column(db.DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc))
# penal_charge = db.Column(db.Float, default=0.0)
# penal_count = db.Column(db.Integer, default=0)
penal_charge = db.Column(db.Float, default=0.0)
penal_count = db.Column(db.Integer, default=0)
last_penal_date = db.Column(db.DateTime, nullable=True)
@@ -52,7 +52,11 @@ class LoanRepaymentSchedule(db.Model):
'partial_balance': self.partial_balance,
'paid_at': self.paid_at.isoformat() if self.paid_at else None,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
'updated_at': self.updated_at.isoformat() if self.updated_at else None,
'penal_charge': self.penal_charge,
'penal_count': self.penal_count,
'last_penal_date': self.last_penal_date.isoformat() if self.last_penal_date else None
}
def __repr__(self):
@@ -296,54 +300,37 @@ class LoanRepaymentSchedule(db.Model):
db.session.rollback()
logger.error(f"Error applying repayment for schedule {schedule_id}: {e}")
raise
@classmethod
def update_due_process_date_and_count(cls, schedule_id):
"""
Update the due process date to now and increment the due process count.
"""
try:
schedule = cls.query.get(schedule_id)
if not schedule:
raise ValueError(f"Schedule with ID {schedule_id} does not exist.")
if schedule.due_process_count is None:
schedule.due_process_count = 0
schedule.due_process_count += 1
schedule.due_process_date = datetime.now(timezone.utc)
schedule.updated_at = datetime.now(timezone.utc)
now = datetime.now(timezone.utc)
# Calculate DPD
dpd = max((now.date() - schedule.due_date.date()).days, 0)
# Determine outstanding balance
if schedule.paid_status == RepaymentScheduleStatus.PARTIALLY_PAID and schedule.partial_balance:
outstanding_amount = Decimal(str(schedule.partial_balance))
else:
outstanding_amount = Decimal(str(schedule.installment_amount))
@classmethod
def apply_penal_to_schedule(cls, schedule_id, penal_amount):
# Calculate penal charge (1% default if settings not set)
try:
penalty_rate = getattr(settings, "PENAL_CHARGE_PERCENTAGE", 1)/100
except:
penalty_rate = 0.01
penal_charge = (outstanding_amount * Decimal(str(penalty_rate))).quantize(
Decimal("0.01"), rounding=ROUND_HALF_UP
)
#new balance + penal charge
new_balance = outstanding_amount + penal_charge
schedule = cls.query.get(schedule_id)
db.session.commit()
logger.info(f"""Updated due process date and count for schedule ID {schedule.id},
amount due: {schedule.installment_amount},
outstanding amount: {outstanding_amount},
due process count: {schedule.due_process_count},
DPD: {dpd},
penal charge(1%): {penal_charge},
new balance(plus penal charge): {new_balance}
""")
now = datetime.now(timezone.utc)
schedule.penal_count = (schedule.penal_count or 0) + 1
schedule.penal_charge = (schedule.penal_charge or 0) + penal_amount
schedule.last_penal_date = now
schedule.due_process_date = now
schedule.updated_at = now
db.session.commit()
except Exception as e:
db.session.rollback()
logger.error(f"Error updating due process date and count for schedule {schedule.id}: {e}")
raise
@classmethod
def calculate_penal_charge(cls, schedule):
if schedule.paid_status == RepaymentScheduleStatus.PARTIALLY_PAID:
outstanding = Decimal(str(schedule.partial_balance))
else:
outstanding = Decimal(str(schedule.installment_amount))
rate = Decimal(str(settings.PENAL_CHARGE_PERCENTAGE)) / 100
penal_charge = (outstanding * rate).quantize(
Decimal("0.01"),
rounding=ROUND_HALF_UP
)
return penal_charge