diff --git a/app/helpers/collect_loan_helper.py b/app/helpers/collect_loan_helper.py index cdb614c..7a1e603 100644 --- a/app/helpers/collect_loan_helper.py +++ b/app/helpers/collect_loan_helper.py @@ -4,6 +4,9 @@ from app.services.repayment import RepaymentService from app.services.loan import LoanService from app.helpers.response_helper import ResponseHelper from app.utils.logger import logger +from decimal import Decimal, ROUND_HALF_UP +from app.services.loan_repayment_schedule import LoanRepaymentScheduleService +from app.enums.loan_status import LoanStatus class CollectLoanHelper: @staticmethod @@ -46,3 +49,5 @@ class CollectLoanHelper: "countryId": "NG", "comment": "COLLECT LOAN" } + + \ No newline at end of file diff --git a/app/integrations/simbrella.py b/app/integrations/simbrella.py index c8482df..4a12dc3 100644 --- a/app/integrations/simbrella.py +++ b/app/integrations/simbrella.py @@ -308,48 +308,8 @@ class SimbrellaClient: amount_collected = Decimal(str(result.get('amountCollected', 0))).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP) logger.info(f"Amount collected: {amount_collected}") - if loan.balance is None or loan.balance <= 0: - logger.warning(f"Loan ID {loan.id} has no balance. Skipping loan update.") - updated_loan = loan.to_dict() - #return ResponseHelper.error("Loan has no balance. Skipping.") - else: - - try: - logger.info(f"Updating loan balance for loan ID {loan_data['debtId']} with amount collected: {amount_collected}") - updated_loan = LoanService.update_loan_balance(int(loan_data['debtId']), amount_collected) - logger.info(f"Updated loan: {updated_loan}") - except Exception as ex: - db.session.rollback() - logger.error(f"Error updating loan balance for loan ID {loan.id}: {ex}") - return ResponseHelper.error("Error updating loan balance") - - try: - updated_balance = Decimal(str(updated_loan['balance'])).quantize(Decimal('0.01')) - logger.info(f"Updated balance: {updated_balance}") - - if updated_balance <= Decimal('0.00'): - logger.info('Loan fully repaid') - repaid = LoanService.update_status(updated_loan['debtId'], LoanStatus.REPAID) - logger.info(f'Updated loan with repaid status: {repaid}') - updated_loan = repaid - else: - logger.info('Loan partially repaid') - partial = LoanService.update_status(updated_loan['debtId'], LoanStatus.ACTIVE_PARTIAL) - logger.info(f'Updated loan with partial status: {partial}') - updated_loan = partial - - except Exception as e: - db.session.rollback() - logger.error(f"Error while updating loan status for debtId {updated_loan['debtId']}: {e}") - # lets update the loan repayment schedule when full payment has been made - # get the repayment schedule by loan_id - logger.info(f"Updated loan status: {updated_loan.get('status')}") - LoanRepaymentScheduleService.handle_schedule_updates( - updated_loan=updated_loan, - data=data, - amount_collected=amount_collected, - message=response_message, - loan_data=loan_data + updated_loan = LoanService._update_loan_after_collection( + loan, loan_data, updated_loan, amount_collected, data, response_message=response_message ) return ResponseHelper.success(result, "Successful") diff --git a/app/services/loan.py b/app/services/loan.py index f8e3e16..9ba1867 100644 --- a/app/services/loan.py +++ b/app/services/loan.py @@ -1,4 +1,8 @@ from app.models import Loan, LoanCharge +from app.utils.logger import logger +from app.enums.loan_status import LoanStatus +from decimal import Decimal, ROUND_HALF_UP +from app.services.loan_repayment_schedule import LoanRepaymentScheduleService class LoanService: @@ -106,5 +110,29 @@ class LoanService: Get all overdue loans. """ return Loan.get_overdue_loans() + @staticmethod + def _update_loan_after_collection(loan, loan_data, updated_loan, amount_collected, data, response_message): + if loan.balance is None or loan.balance <= 0: + logger.warning(f"Loan ID {loan.id} has no balance. Skipping loan update.") + updated_loan = loan.to_dict() + else: + updated_loan = LoanService.update_loan_balance(int(loan_data['debtId']), amount_collected) + updated_balance = Decimal(str(updated_loan['balance'])).quantize(Decimal('0.01')) + + if updated_balance <= Decimal('0.00'): + updated_loan = LoanService.update_status(updated_loan['debtId'], LoanStatus.REPAID) + else: + updated_loan = LoanService.update_status(updated_loan['debtId'], LoanStatus.ACTIVE_PARTIAL) + logger.info(f"Updated loan status: {updated_loan.get('status')}") + # lets update the loan repayment schedule + LoanRepaymentScheduleService.handle_schedule_updates( + updated_loan=updated_loan, + data=data, + amount_collected=amount_collected, + message=response_message, + loan_data=loan_data + ) + return updated_loan + \ No newline at end of file diff --git a/app/services/loan_repayment_schedule.py b/app/services/loan_repayment_schedule.py index b1a3153..9fb9e27 100644 --- a/app/services/loan_repayment_schedule.py +++ b/app/services/loan_repayment_schedule.py @@ -1,6 +1,7 @@ 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: @@ -40,6 +41,7 @@ class LoanRepaymentScheduleService: """ return LoanRepaymentSchedule.update_repayment_schedule_description(schedule_id, description) + @staticmethod def handle_schedule_updates(updated_loan, data, amount_collected, message, loan_data): """ @@ -68,12 +70,12 @@ class LoanRepaymentScheduleService: logger.error(f"Failed to update installment {installment.id}: {e}") logger.info('All installments processed') - # Case 2: Partial repayment without overdueLoanScheduleId + # 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: Overdue schedule repayment → update balance & description + # 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: @@ -102,4 +104,7 @@ class LoanRepaymentScheduleService: except Exception as e: logger.error(f"Unexpected error while handling schedule updates: {e}") + + +