added repayment_schedule
This commit is contained in:
@@ -6,7 +6,6 @@ from app.services.loan_repayment_schedule import LoanRepaymentScheduleService
|
||||
from app.utils.auth import get_headers
|
||||
from app.utils.extras import preprocess_loan_charges_data
|
||||
import random
|
||||
import random
|
||||
import string
|
||||
from app.extensions import db
|
||||
from app.utils.logger import logger
|
||||
@@ -17,11 +16,13 @@ from app.extensions import db
|
||||
from app.services.repayments_data import RepaymentsData
|
||||
from app.services.salary import SalaryService
|
||||
from app.enums.loan_status import LoanStatus
|
||||
from app.models.loan_repayment_schedule import LoanRepaymentSchedule
|
||||
from decimal import Decimal, ROUND_HALF_UP
|
||||
from requests.exceptions import SSLError, RequestException,Timeout
|
||||
import sys
|
||||
from requests.exceptions import ReadTimeout, ConnectTimeout
|
||||
import socket
|
||||
from app.helpers.collect_loan_helper import CollectLoanHelper
|
||||
|
||||
|
||||
class SimbrellaClient:
|
||||
@@ -243,19 +244,10 @@ class SimbrellaClient:
|
||||
logger.info(f"Calling CollectLoan api_url==> : {api_url}")
|
||||
logger.info(f"Calling CollectLoan endpoint with data: {data}")
|
||||
|
||||
repayment = RepaymentService.get_repayment_by_id(id=data['Id'])
|
||||
if not repayment:
|
||||
logger.info(f"Repayment with id: {data['Id']} not found")
|
||||
return ResponseHelper.error("Repayment not found")
|
||||
|
||||
repayment_data = repayment.to_dict()
|
||||
loan = LoanService.get_loan_by_loan_id(loan_id=int(repayment_data['loanId']))
|
||||
if not loan:
|
||||
logger.info(f"Loan with debtId: {repayment_data['loanId']} not found")
|
||||
return ResponseHelper.error("Loan not found")
|
||||
|
||||
repayment_data, loan, error = CollectLoanHelper._validate_repayment_and_loan(data)
|
||||
if error:
|
||||
return error
|
||||
loan_data = loan.to_dict()
|
||||
logger.info(f"Loan data: {loan_data}")
|
||||
|
||||
if repayment_data['repayDate'] is not None:
|
||||
logger.info(f"Repayment already processed at {repayment_data['repayDate']}")
|
||||
@@ -265,28 +257,7 @@ class SimbrellaClient:
|
||||
repayment = RepaymentService.get_repayment_by_transaction_id(transaction_id=data['transactionId'])
|
||||
repayment_data = repayment.to_dict()
|
||||
|
||||
debtId = str(loan_data.get('debtId', "")).strip().zfill(6)
|
||||
t_id = ''.join(random.choices(string.ascii_uppercase, k=22))
|
||||
collect_loan_data = {
|
||||
"transactionId": t_id,
|
||||
"fbnTransactionId": loan_data['transactionId'],
|
||||
"debtId": debtId,
|
||||
"customerId": repayment_data['customerId'],
|
||||
"accountId": loan_data['accountId'],
|
||||
"productId": repayment_data['productId'],
|
||||
"collectAmount": (
|
||||
data['overdueLoanScheduleAmount']
|
||||
if data.get('overdueLoanScheduleAmount') is not None
|
||||
else loan_data.get('balance', 0)
|
||||
),
|
||||
"penalCharge": 0,
|
||||
"channel": "USSD",
|
||||
"collectionMethod": collectionMethod,
|
||||
"lienAmount": 0,
|
||||
"countryId": "NG",
|
||||
"comment": "COLLECT LOAN"
|
||||
}
|
||||
|
||||
collect_loan_data = CollectLoanHelper._build_collect_loan_payload(loan_data, repayment_data, data, collectionMethod)
|
||||
try:
|
||||
logger.info(f"Sending CollectLoan request............ {collect_loan_data}")
|
||||
response = requests.post(api_url, json=collect_loan_data, timeout=90, headers=get_headers())
|
||||
@@ -299,6 +270,9 @@ class SimbrellaClient:
|
||||
'404',
|
||||
'Collection Service url not found'
|
||||
)
|
||||
if(data.get('overdueLoanScheduleId') is not None):
|
||||
LoanRepaymentScheduleService.update_repayment_schedule_description(data['overdueLoanScheduleId'], 'Collection Service url not found')
|
||||
|
||||
logger.error("Received 404 from external service")
|
||||
return ResponseHelper.error("Collection Service URL not found", status_code=404)
|
||||
|
||||
@@ -329,6 +303,7 @@ class SimbrellaClient:
|
||||
else:
|
||||
logger.warning("Failed to add repayment data")
|
||||
updated_loan = None
|
||||
response_message = result.get('responseMessage')
|
||||
if result.get('responseCode') == '00':
|
||||
amount_collected = Decimal(str(result.get('amountCollected', 0))).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
|
||||
logger.info(f"Amount collected: {amount_collected}")
|
||||
@@ -363,66 +338,32 @@ class SimbrellaClient:
|
||||
logger.info(f'Updated loan with partial status: {partial}')
|
||||
updated_loan = partial
|
||||
|
||||
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:
|
||||
# Use DB installment amount as the reference
|
||||
installment_amount = Decimal(str(schedule_to_update.installment_amount)).quantize(Decimal("0.01"))
|
||||
|
||||
if amount_collected >= installment_amount:
|
||||
LoanRepaymentScheduleService.update_repayment_schedule_status(
|
||||
schedule_to_update.id, paid=True)
|
||||
logger.info(
|
||||
f"Installment {data['overdueLoanScheduleId']} fully covered "
|
||||
f"with {amount_collected}, marked as paid"
|
||||
)
|
||||
else:
|
||||
logger.info(
|
||||
f"Installment {data['overdueLoanScheduleId']} not fully covered. "
|
||||
f"Collected={amount_collected}, "
|
||||
f"Installment={installment_amount}"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to update repayment schedule installment: {e}")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
logger.error(f"Error while updating loan status for debtId {updated_loan['debtId']}: {e}")
|
||||
# for now lets update the loan repayment schedule only when full payment has been made
|
||||
# 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')}")
|
||||
if updated_loan and updated_loan.get('status') == LoanStatus.REPAID:
|
||||
repayment_schedule = LoanRepaymentScheduleService.get_repayment_schedule_by_loan_id(updated_loan['debtId'])
|
||||
logger.info(f'Loan repayment schedule: {repayment_schedule}')
|
||||
# if repayment_schedule, loop through each installment
|
||||
if repayment_schedule:
|
||||
for installment in repayment_schedule:
|
||||
try:
|
||||
logger.info(f'Processing installment: {installment}')
|
||||
# Update each installment as paid
|
||||
LoanRepaymentScheduleService.update_repayment_schedule_status(installment.id, True)
|
||||
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')
|
||||
|
||||
LoanRepaymentScheduleService.handle_schedule_updates(
|
||||
updated_loan=updated_loan,
|
||||
data=data,
|
||||
amount_collected=amount_collected,
|
||||
message=response_message,
|
||||
loan_data=loan_data
|
||||
)
|
||||
return ResponseHelper.success(result, "Successful")
|
||||
|
||||
except SSLError as ssl_err:
|
||||
db.session.rollback()
|
||||
logger.exception(f"SSL error while calling Simbrella endpoint: {ssl_err}")
|
||||
RepaymentService.set_repay_result(
|
||||
repayment_data['Id'],
|
||||
'502',
|
||||
'SSL error occurred while calling Simbrella'
|
||||
)
|
||||
if(data.get('overdueLoanScheduleId') is not None):
|
||||
LoanRepaymentScheduleService.update_repayment_schedule_description(data['overdueLoanScheduleId'], 'SSL error occurred')
|
||||
|
||||
return ResponseHelper.error("SSL handshake failed with Simbrella", status_code=502, error=str(ssl_err))
|
||||
|
||||
except (Timeout, ReadTimeout, ConnectTimeout, socket.timeout, TimeoutError) as timeout_err:
|
||||
@@ -433,6 +374,9 @@ class SimbrellaClient:
|
||||
'500',
|
||||
'There was a timeout while calling Simbrella'
|
||||
)
|
||||
if(data.get('overdueLoanScheduleId') is not None):
|
||||
LoanRepaymentScheduleService.update_repayment_schedule_description(data['overdueLoanScheduleId'], 'Timeout occurred')
|
||||
|
||||
return ResponseHelper.error("Connection to Simbrella timed out", status_code=504, error=str(timeout_err))
|
||||
except RequestException as req_err:
|
||||
db.session.rollback()
|
||||
@@ -442,7 +386,8 @@ class SimbrellaClient:
|
||||
'500',
|
||||
'There was a request error while calling Simbrella'
|
||||
)
|
||||
return ResponseHelper.error("Connection to Simbrella failed", status_code=503, error=str(req_err))
|
||||
if(data.get('overdueLoanScheduleId') is not None):
|
||||
LoanRepaymentScheduleService.update_repayment_schedule_description(data['overdueLoanScheduleId'], 'Request error occurred')
|
||||
|
||||
except SystemExit as sys_exit:
|
||||
db.session.rollback()
|
||||
@@ -454,6 +399,9 @@ class SimbrellaClient:
|
||||
|
||||
|
||||
)
|
||||
if(data.get('overdueLoanScheduleId') is not None):
|
||||
LoanRepaymentScheduleService.update_repayment_schedule_description(data['overdueLoanScheduleId'], 'Unexpected shutdown occurred')
|
||||
|
||||
return ResponseHelper.error("Unexpected shutdown detected", status_code=500, error=str(sys_exit))
|
||||
|
||||
except Exception as e:
|
||||
@@ -464,6 +412,9 @@ class SimbrellaClient:
|
||||
'500',
|
||||
'Unexpected error while processing loan collection'
|
||||
)
|
||||
if(data.get('overdueLoanScheduleId') is not None):
|
||||
LoanRepaymentScheduleService.update_repayment_schedule_description(data['overdueLoanScheduleId'], 'Unexpected error occurred')
|
||||
|
||||
return ResponseHelper.error("Unexpected error while processing loan collection", status_code=500, error=str(e))
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user