code clean up 2
This commit is contained in:
@@ -1,16 +0,0 @@
|
||||
from marshmallow import Schema, fields
|
||||
|
||||
# Provide Loan Schema
|
||||
class ProvideLoanSchema(Schema):
|
||||
type = fields.Str(required=False)
|
||||
requestId = fields.Str(required=True)
|
||||
transactionId = fields.Str(required=True)
|
||||
customerId = fields.Str(required=True)
|
||||
accountId = fields.Str(required=True)
|
||||
msisdn = fields.Str(required=False)
|
||||
# productId = fields.Str(required=True)
|
||||
# lienAmount = fields.Float(required=True)
|
||||
requestedAmount = fields.Float(required=True)
|
||||
collectionType = fields.Int(required=True)
|
||||
offerId = fields.Str(required=True)
|
||||
channel = fields.Str(required=True)
|
||||
@@ -1,14 +0,0 @@
|
||||
from marshmallow import Schema, fields
|
||||
|
||||
# Select Offer Schema
|
||||
class SelectOfferSchema(Schema):
|
||||
requestId = fields.Str(required=True)
|
||||
transactionId = fields.Str(required=True)
|
||||
customerId = fields.Str(required=True)
|
||||
accountId = fields.Str(required=True)
|
||||
msisdn = fields.Str(required=True)
|
||||
requestedAmount = fields.Float(required=True)
|
||||
productId = fields.Str(required=True)
|
||||
offerId = fields.Str(required=True)
|
||||
channel = fields.Str(required=True)
|
||||
|
||||
@@ -1,12 +1,4 @@
|
||||
# from app.api.services.eligibility_check import EligibilityCheckService
|
||||
# from app.api.services.select_offer import SelectOfferService
|
||||
# from app.api.services.provide_loan import ProvideLoanService
|
||||
# from app.api.services.loan_status import LoanStatusService
|
||||
# from app.api.services.repayment import RepaymentService
|
||||
# from app.api.services.customer_consent import CustomerConsentService
|
||||
# from app.api.services.notification_callback import NotificationCallbackService
|
||||
from app.api.services.authorization import AuthorizationService
|
||||
# from app.api.services.offer_analysis import OfferAnalysis
|
||||
from app.api.services.login import LoginService
|
||||
from app.api.services.register import RegisterService
|
||||
from app.api.services.products import ProductsService
|
||||
@@ -14,7 +6,6 @@ from app.api.services.account import AccountService
|
||||
from app.api.services.myproduct import MyProductsService
|
||||
from app.api.services.contacts import ContactService
|
||||
from app.api.services.office_auth import OfficeAuthService
|
||||
# from app.api.services.office_dashboard import OfficeDashboardService
|
||||
from app.api.services.web_contents import WebContentsService
|
||||
from app.api.services.subscription import SubscriptionService
|
||||
from app.api.services.common_data import CommonDataService
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
from flask import session, jsonify
|
||||
from app.models.loan import Loan
|
||||
from app.utils.logger import logger
|
||||
from app.api.services.base_service import BaseService
|
||||
# from app.api.schemas.eligibility_check import EligibilityCheckSchema
|
||||
from marshmallow import ValidationError
|
||||
from app.api.enums import TransactionType
|
||||
# from app.api.integrations import SimbrellaIntegration
|
||||
# from app.extensions import db
|
||||
from app.models import Offer, RACCheck
|
||||
# from app.api.services.offer_analysis import OfferAnalysis
|
||||
# from app.api.helpers.response_helper import ResponseHelper
|
||||
|
||||
import random
|
||||
|
||||
|
||||
class EligibilityCheckService(BaseService):
|
||||
TRANSACTION_TYPE = TransactionType.ELIGIBILITY_CHECK
|
||||
|
||||
@staticmethod
|
||||
def process_request(data):
|
||||
pass
|
||||
|
||||
|
||||
@staticmethod
|
||||
def check_loan_limits(customer_id):
|
||||
pass
|
||||
@@ -1,81 +0,0 @@
|
||||
from flask import request, jsonify
|
||||
from marshmallow import ValidationError
|
||||
from app.api.enums.loan_status import LoanStatus
|
||||
from app.models import Customer
|
||||
from app.utils.logger import logger
|
||||
from app.api.schemas.loan_status import LoanStatusSchema
|
||||
from app.api.services.base_service import BaseService
|
||||
from app.api.enums import TransactionType
|
||||
from app.extensions import db
|
||||
from app.api.helpers.response_helper import ResponseHelper
|
||||
|
||||
|
||||
class LoanStatusService(BaseService):
|
||||
TRANSACTION_TYPE = TransactionType.LOAN_STATUS
|
||||
|
||||
@staticmethod
|
||||
def process_request(data):
|
||||
"""
|
||||
Process the Loan Information request.
|
||||
|
||||
Args:
|
||||
data (dict): The request data.
|
||||
|
||||
Returns:
|
||||
dict: A standardized response.
|
||||
"""
|
||||
try:
|
||||
with db.session.begin():
|
||||
# Validate data
|
||||
validated_data = LoanStatusService.validate_data(data, LoanStatusSchema())
|
||||
|
||||
customer_id = validated_data.get('customerId')
|
||||
|
||||
logger.info(f"Looking for customer *** {customer_id}")
|
||||
customer = Customer.get_customer_with_loan_list(customer_id)
|
||||
|
||||
transactionId = validated_data.get('transactionId')
|
||||
account_id = validated_data.get('accountId')
|
||||
|
||||
if(LoanStatusService.validate_account_ownership(account_id = account_id, customer_id = customer_id)):
|
||||
# Get loans
|
||||
loans = [loan.to_dict() for loan in customer.loans if loan.status == LoanStatus.ACTIVE]
|
||||
transaction = LoanStatusService.log_transaction(validated_data = validated_data)
|
||||
if not transaction:
|
||||
logger.error(f"Failed to log transaction")
|
||||
return ResponseHelper.error(result_description="Failed to log transaction.")
|
||||
else:
|
||||
return ResponseHelper.error(result_description="Invalid Customer or Account")
|
||||
|
||||
total_debt_amount = sum(
|
||||
loan.get("currentLoanAmount") or 0
|
||||
for loan in loans
|
||||
)
|
||||
|
||||
# Simulated processing logic
|
||||
response_data = {
|
||||
"customerId": customer_id,
|
||||
"accountId": account_id,
|
||||
"transactionId": transactionId,
|
||||
"loans": loans,
|
||||
"totalDebtAmount": total_debt_amount,
|
||||
}
|
||||
|
||||
db.session.commit()
|
||||
return ResponseHelper.success(data=response_data)
|
||||
|
||||
except ValidationError as err:
|
||||
|
||||
logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}")
|
||||
db.session.rollback()
|
||||
return ResponseHelper.unprocessable_entity(result_description="Validation exception")
|
||||
|
||||
except ValueError as err:
|
||||
logger.error(f"{getattr(err, 'messages', str(err))}")
|
||||
db.session.rollback()
|
||||
return ResponseHelper.error(result_description=str(err))
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"An error occurred: {str(e)}", exc_info=True)
|
||||
db.session.rollback()
|
||||
return ResponseHelper.internal_server_error()
|
||||
@@ -1,250 +0,0 @@
|
||||
from decimal import Decimal
|
||||
from app.models import Offer, TransactionOffer
|
||||
from app.models.loan import Loan
|
||||
import random
|
||||
import logging
|
||||
|
||||
from app.config import Config
|
||||
|
||||
RAC_TRUE_CHECK_RULES = Config.rac_true_rules
|
||||
RAC_FALSE_CHECK_RULES = Config.rac_false_rules
|
||||
RAC_SALARY_PAYMENTS = Config.rac_salary_payments
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class OfferAnalysis:
|
||||
|
||||
@staticmethod
|
||||
def get_offer(transaction_id, rac_response, validated_data):
|
||||
customer_id = validated_data.get("customerId")
|
||||
product_id = validated_data.get("productId")
|
||||
offer_id = validated_data.get("offerId")
|
||||
|
||||
transaction_offer_id = int(offer_id[5:]) # The last part is int
|
||||
|
||||
logger.info(f"customer_id == *************** : {customer_id}")
|
||||
logger.info(f"product_id == *************** : {product_id}")
|
||||
logger.info(f"offer_id == *************** : {offer_id}")
|
||||
logger.info(f"transaction_offer_id == *************** : {transaction_offer_id}")
|
||||
|
||||
transaction_offer = TransactionOffer.is_valid_transaction_offer(transaction_offer_id, customer_id, product_id)
|
||||
|
||||
if not transaction_offer:
|
||||
raise ValueError("Invalid Transaction Offer.")
|
||||
|
||||
eligible_amount = transaction_offer.eligible_amount
|
||||
offer = Offer.is_valid_offer( transaction_offer.offer_id)
|
||||
|
||||
if not offer:
|
||||
raise ValueError("Invalid Offer.")
|
||||
original_transaction = transaction_id
|
||||
|
||||
return transaction_offer, offer, eligible_amount, original_transaction
|
||||
@staticmethod
|
||||
def _analyze_rack_checks(rack_response, offer):
|
||||
logger.info(f"This is PayLoad for ANALYSYS ***** : {str(rack_response)}", exc_info=True)
|
||||
logger.info(f"RACk TRUE RULES {str(RAC_TRUE_CHECK_RULES)}", exc_info=True)
|
||||
logger.info(f"RACk FALSE RULES {str(RAC_FALSE_CHECK_RULES)}", exc_info=True)
|
||||
logger.info(f"RACk SALARY PAYMENTS {str(RAC_SALARY_PAYMENTS)}", exc_info=True)
|
||||
|
||||
if not isinstance(rack_response, dict) or not offer :
|
||||
raise ValueError("Invalid RAC response format.")
|
||||
|
||||
|
||||
|
||||
failed_true_rules = []
|
||||
failed_false_rules = []
|
||||
salaries = []
|
||||
|
||||
# Expects true
|
||||
for rule in RAC_TRUE_CHECK_RULES:
|
||||
if not rack_response.get(rule, False):
|
||||
failed_true_rules.append(rule)
|
||||
|
||||
# Expects false
|
||||
for rule in RAC_FALSE_CHECK_RULES:
|
||||
if rack_response.get(rule, True):
|
||||
failed_false_rules.append(rule)
|
||||
|
||||
|
||||
# Salary rules
|
||||
for key in RAC_SALARY_PAYMENTS:
|
||||
value = rack_response.get(key)
|
||||
|
||||
|
||||
if isinstance(value, Decimal):
|
||||
# Only use values greater than 0
|
||||
if value > 0:
|
||||
salaries.append(value)
|
||||
elif isinstance(value, (int, float, str)):
|
||||
try:
|
||||
value = Decimal(str(value))
|
||||
if value > 0:
|
||||
salaries.append(value)
|
||||
except:
|
||||
logger.warning(f"Could not convert value of {key} to Decimal: {value}")
|
||||
|
||||
|
||||
if failed_true_rules or failed_false_rules or not salaries:
|
||||
logger.warning(f"Failed TRUE rules: {failed_true_rules}")
|
||||
logger.warning(f"Failed FALSE rules: {failed_false_rules}")
|
||||
logger.warning("No salary records found in RAC response.")
|
||||
raise ValueError(f"RAC analysis failed")
|
||||
|
||||
|
||||
|
||||
logger.info(f"These are the salary amounts ***** : {str(salaries)}", exc_info=True)
|
||||
|
||||
#Least salary in the last 6 months
|
||||
min_salary = min(salaries)
|
||||
|
||||
# Check consistency rule
|
||||
consistent_income = rack_response.get("rule7_consistent_salary_amount", False)
|
||||
|
||||
# Determine percentage based on offer tenor
|
||||
tenor = offer.tenor
|
||||
|
||||
if tenor == 30 and consistent_income:
|
||||
eligible_amount = min_salary * Decimal("0.5")
|
||||
logger.info("Applying 50% of least salary in 6 months due to 1-month offer tenor with stable income.")
|
||||
elif tenor == 90 and consistent_income:
|
||||
eligible_amount = min_salary * Decimal("0.75")
|
||||
logger.info("Applying 75% of least salary in 6 months due to 3-months offer tenor with stable income.")
|
||||
|
||||
else: # Income is not consistent
|
||||
eligible_amount = 0
|
||||
logger.info("Applying no percentage on least salary due unstable income.")
|
||||
|
||||
|
||||
|
||||
logger.info(f"Calculated eligible amount from RAC: {eligible_amount} based on {'stable' if consistent_income else 'unstable'} income.")
|
||||
|
||||
return eligible_amount.quantize(Decimal("1.00"))
|
||||
|
||||
# "racResponse": {
|
||||
# "accountStatus": true,
|
||||
# "bvnValidated": true,
|
||||
# "creditBureauCheck": false,
|
||||
# "crmsCheck": true,
|
||||
# "hasLien": false,
|
||||
# "hasPastDueLoan": false,
|
||||
# "hasSalaryAccount": true,
|
||||
# "isWhitelisted": true,
|
||||
# "noBouncedCheck": true
|
||||
# },
|
||||
#
|
||||
|
||||
'''
|
||||
30 days
|
||||
Eligibility amount (monthly SOL) - Adoption of 50% of the least salary inflow in the past 6 months
|
||||
to determine loan eligibility for potential customers.
|
||||
|
||||
3 months
|
||||
Adoption of 75% of the least salary inflow in the past 6 months to determine loan eligibility for
|
||||
potential customers" for customers that have unstable income. 3 months
|
||||
'''
|
||||
# rac_true_rules
|
||||
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def decide_offer(transaction_id, rac_check, validated_data, customer_id, rack_checks_response):
|
||||
eligible_offers = []
|
||||
# if we have active offers - we have to feed off it
|
||||
logger.info(f"**RACK ANALYSIS** {customer_id}")
|
||||
# Analyze Rack Checks
|
||||
# new_eligible_amount = OfferAnalysis._analyze_rack_checks(rack_checks_response) #--> We need detail analysis
|
||||
|
||||
# we can now find the origin transactions
|
||||
# Find the last loan - it will have original_transaction
|
||||
last_customer_loan = Loan.get_customer_last_loan(customer_id)
|
||||
# logger.info(f"{last_customer_loan}")
|
||||
|
||||
if last_customer_loan:
|
||||
original_transaction = last_customer_loan.original_transaction or last_customer_loan.transaction_id
|
||||
logger.info(f"transaction_id |-| original_transaction === > {transaction_id} {original_transaction}")
|
||||
original_loan = Loan.get_customer_original_loan(customer_id, original_transaction)
|
||||
if original_loan is not None:
|
||||
logger.info(f"original_loan === > {original_loan}")
|
||||
logger.info(f"loan_offer_id === > {original_loan.offer_id}")
|
||||
|
||||
original_offer_id = str(original_loan.offer_id[:5]) # The last part is str
|
||||
transaction_offer_id = int(original_loan.offer_id[5:]) # The last part is int
|
||||
original_transaction_offer = TransactionOffer.is_valid_transaction_offer(transaction_offer_id, customer_id, original_loan.product_id)
|
||||
|
||||
active_loans = Loan.get_active_loans_by_original_transaction(original_transaction)
|
||||
sum_active_loans = sum(loan.current_loan_amount for loan in active_loans)
|
||||
logger.info(f"sum_active_loans === > {sum_active_loans}")
|
||||
real_eligible_amount = original_loan.eligible_amount - sum_active_loans
|
||||
|
||||
if real_eligible_amount < original_transaction_offer.min_amount:
|
||||
logger.error(f"Max eligible amount ({real_eligible_amount}) is less than the minimum offer amount ({original_transaction_offer.min_amount}).")
|
||||
raise ValueError("You are not eligible for a loan at this time.")
|
||||
|
||||
transaction_offer = TransactionOffer.create_transaction_offer(
|
||||
customer_id=customer_id,
|
||||
transaction_id=transaction_id,
|
||||
original_transaction=original_transaction,
|
||||
offer_id=original_offer_id,
|
||||
min_amount=original_transaction_offer.min_amount,
|
||||
max_amount=original_transaction_offer.max_amount,
|
||||
eligible_amount=real_eligible_amount,
|
||||
product_id=original_loan.product_id,
|
||||
tenor=original_loan.tenor
|
||||
)
|
||||
|
||||
# Visible offer ID: offer_id + padded(transaction_offer.id)
|
||||
padded_id = str(transaction_offer.id).zfill(6)
|
||||
public_offer_id = f"{original_offer_id}{padded_id}"
|
||||
|
||||
eligible_offers.append({
|
||||
"offerId": public_offer_id,
|
||||
"product_id": original_transaction_offer.product_id,
|
||||
"min_amount": original_transaction_offer.min_amount,
|
||||
"max_amount": round(real_eligible_amount, 2),
|
||||
"tenor": original_loan.tenor
|
||||
})
|
||||
return eligible_offers
|
||||
|
||||
|
||||
offers = Offer.get_all_offers()
|
||||
|
||||
|
||||
for offer in offers:
|
||||
|
||||
new_eligible_amount = OfferAnalysis._analyze_rack_checks(rack_checks_response, offer)
|
||||
|
||||
|
||||
approved_amount = new_eligible_amount
|
||||
approved_amount = round(approved_amount, 2)
|
||||
|
||||
if approved_amount < offer.min_amount:
|
||||
logger.error(f"Max eligible amount ({approved_amount}) is less than the minimum offer amount ({offer.min_amount}).")
|
||||
raise ValueError("You are not eligible for a loan at this time.")
|
||||
|
||||
|
||||
transaction_offer = TransactionOffer.create_transaction_offer(
|
||||
customer_id=customer_id,
|
||||
transaction_id=transaction_id,
|
||||
original_transaction=transaction_id,
|
||||
offer_id=offer.id,
|
||||
min_amount=offer.min_amount,
|
||||
max_amount=offer.max_amount,
|
||||
eligible_amount=approved_amount,
|
||||
product_id=offer.product_id,
|
||||
tenor=offer.tenor
|
||||
)
|
||||
|
||||
# Visible offer ID: offer_id + padded(transaction_offer.id)
|
||||
padded_id = str(transaction_offer.id).zfill(6)
|
||||
public_offer_id = f"{offer.id}{padded_id}"
|
||||
|
||||
eligible_offers.append({
|
||||
"offerId": public_offer_id,
|
||||
"product_id": offer.product_id,
|
||||
"min_amount": offer.min_amount,
|
||||
"max_amount": approved_amount,
|
||||
"tenor": offer.tenor
|
||||
})
|
||||
|
||||
return eligible_offers
|
||||
@@ -1,198 +0,0 @@
|
||||
from flask import request, jsonify
|
||||
from marshmallow import ValidationError
|
||||
from app.api.integrations.kafka import KafkaIntegration
|
||||
from app.api.services.base_service import BaseService
|
||||
from app.api.enums import TransactionType
|
||||
from app.models.customer import Customer
|
||||
from app.models.loan_charge import LoanCharge
|
||||
from app.utils.logger import logger
|
||||
from app.api.schemas.provide_loan import ProvideLoanSchema
|
||||
from threading import Thread
|
||||
from app.models import Loan, Offer, Charge , TransactionOffer, RACCheck
|
||||
from app.api.enums import LoanStatus
|
||||
from app.extensions import db
|
||||
from datetime import datetime, timezone
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from app.models import LoanRepaymentSchedule
|
||||
from app.api.services.offer_analysis import OfferAnalysis
|
||||
from app.api.helpers.response_helper import ResponseHelper
|
||||
|
||||
class ProvideLoanService(BaseService):
|
||||
TRANSACTION_TYPE = TransactionType.PROVIDE_LOAN
|
||||
|
||||
|
||||
@staticmethod
|
||||
def process_request(data):
|
||||
"""
|
||||
Process the ProvideLoan request.
|
||||
|
||||
Args:
|
||||
data (dict): The request data.
|
||||
|
||||
Returns:
|
||||
dict: A standardized response.
|
||||
"""
|
||||
try:
|
||||
with db.session.begin():
|
||||
validated_data = ProvideLoanService.validate_data(data, ProvideLoanSchema())
|
||||
account_id = validated_data.get('accountId')
|
||||
customer_id = validated_data.get('customerId')
|
||||
request_id = validated_data.get('requestId')
|
||||
collection_type = validated_data.get('collectionType')
|
||||
transaction_id = validated_data.get('transactionId')
|
||||
offer_id = validated_data.get('offerId')
|
||||
amount = validated_data.get("requestedAmount")
|
||||
product_id = validated_data.get("productId")
|
||||
channel = validated_data.get('channel')
|
||||
|
||||
customer = Customer.is_valid_customer(customer_id)
|
||||
|
||||
if (ProvideLoanService.validate_account_ownership(account_id = account_id, customer_id = customer_id)):
|
||||
|
||||
rac_response = RACCheck.get_rac_check(customer_id = customer_id, account_id = account_id)
|
||||
|
||||
try:
|
||||
transaction_offer, offer, eligible_amount, original_transaction = OfferAnalysis.get_offer(
|
||||
transaction_id=transaction_id,
|
||||
rac_response=rac_response,
|
||||
validated_data=validated_data
|
||||
)
|
||||
except ValueError as ve:
|
||||
logger.error(str(ve))
|
||||
return ResponseHelper.error(result_description=str(ve))
|
||||
|
||||
|
||||
if(amount < transaction_offer.min_amount):
|
||||
return ResponseHelper.error(result_description="The amount is less than the minimum allowed transaction amount.")
|
||||
elif amount > transaction_offer.max_amount:
|
||||
return ResponseHelper.error(result_description="The amount is greater than the maximum allowed transaction amount.")
|
||||
|
||||
|
||||
# transaction_offer_id = int(offer_id[5:]) # The last part is int
|
||||
|
||||
# transaction_offer = TransactionOffer.is_valid_transaction_offer(transaction_offer_id)
|
||||
|
||||
# if not transaction_offer:
|
||||
# logger.error(f"Invalid Transaction Offer")
|
||||
# return jsonify({
|
||||
# "message": "Invalid Transaction Offer."
|
||||
# }), 400
|
||||
|
||||
# eligible_amount = transaction_offer.eligible_amount
|
||||
# offer = Offer.is_valid_offer( transaction_offer.offer_id)
|
||||
|
||||
# if not offer:
|
||||
# logger.error(f"Invalid Offer")
|
||||
# return jsonify({
|
||||
# "message": "Invalid Offer."
|
||||
# }), 400
|
||||
|
||||
|
||||
# Log Transaction
|
||||
transaction = ProvideLoanService.log_transaction(validated_data=validated_data)
|
||||
|
||||
if not transaction:
|
||||
logger.error(f"Failed to log transaction")
|
||||
return ResponseHelper.error(result_description="Failed to log transaction.")
|
||||
|
||||
|
||||
db.session.flush()
|
||||
|
||||
charges = ProvideLoanService.calculate_charges(offer, amount)
|
||||
upfront_fee = charges["upfront_payment"]
|
||||
repayment_amount = charges["repayment_amount"]
|
||||
#installment_amount = charges["installment_amount"]
|
||||
num_schedules = offer.schedule
|
||||
|
||||
upfront_payment = charges["upfront_payment"]
|
||||
total_amount = charges["total_amount"]
|
||||
installment_amount = charges["installment_amount"]
|
||||
interest = charges["interest"]
|
||||
management = charges["management"]
|
||||
insurance = charges["insurance"]
|
||||
vat = charges["vat"]
|
||||
|
||||
padded_id = str(transaction_id).zfill(12)
|
||||
loan_ref = f"{padded_id}{channel}{offer.product_id}"
|
||||
|
||||
|
||||
# Save the loan details
|
||||
loan = Loan.create_loan(
|
||||
customer_id = customer_id,
|
||||
account_id = account_id,
|
||||
offer_id = offer_id,
|
||||
product_id = offer.product_id,
|
||||
collection_type = collection_type,
|
||||
transaction_id = validated_data.get('transactionId'),
|
||||
original_transaction = transaction_offer.original_transaction,
|
||||
initial_loan_amount = validated_data.get('requestedAmount'),
|
||||
upfront_fee = upfront_fee,
|
||||
repayment_amount = repayment_amount,
|
||||
installment_amount = installment_amount,
|
||||
eligible_amount=eligible_amount,
|
||||
status = LoanStatus.ACTIVE,
|
||||
tenor = offer.tenor,
|
||||
reference = loan_ref
|
||||
)
|
||||
|
||||
if not loan:
|
||||
logger.error(f"Failed to save loan details")
|
||||
|
||||
return ResponseHelper.error(result_description="Failed to save loan details.")
|
||||
|
||||
db.session.flush()
|
||||
current_product_id = offer.product_id
|
||||
schedule = LoanRepaymentSchedule.add_repayment_schedule(loan = loan, num_schedules = num_schedules, transaction_id = transaction_id)
|
||||
|
||||
|
||||
if not schedule:
|
||||
logger.error(f"Failed to create repayment schedule for loan ID {loan.id}")
|
||||
return ResponseHelper.error(result_description="Failed to generate loan repayment schedule.")
|
||||
|
||||
# charges = Charge.get_offer_charges(offer.id)
|
||||
|
||||
# logger.info(f"{charges}")
|
||||
|
||||
loan_id = loan.id
|
||||
|
||||
loan_charges = LoanCharge.create_charges_for_loan(loan_id = loan_id, transaction_id = transaction_id, referenced_amount = 800, charges = charges)
|
||||
|
||||
|
||||
|
||||
else:
|
||||
return ResponseHelper.error(result_description="Invalid Customer or Account")
|
||||
|
||||
|
||||
|
||||
response_data = {
|
||||
"requestId": request_id,
|
||||
"transactionId": transaction_id,
|
||||
"loanRef": loan_ref,
|
||||
"customerId": customer_id,
|
||||
"accountId": account_id,
|
||||
"msisdn": customer.msisdn
|
||||
}
|
||||
|
||||
# KafkaIntegration.send_loan_request(loan_data = response_data, request_id = request_id)
|
||||
# Call Kafka in a background thread
|
||||
thread = Thread(target=ProvideLoanService.async_send_to_kafka, args=(response_data, request_id, "PROCESS_PAYMENT"))
|
||||
thread.start()
|
||||
|
||||
db.session.commit()
|
||||
return ResponseHelper.success(data=response_data)
|
||||
|
||||
except ValidationError as err:
|
||||
|
||||
logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}")
|
||||
db.session.rollback()
|
||||
return ResponseHelper.unprocessable_entity(result_description="Validation exception")
|
||||
|
||||
except ValueError as err:
|
||||
logger.error(f"{getattr(err, 'messages', str(err))}")
|
||||
db.session.rollback()
|
||||
return ResponseHelper.error(result_description=str(err))
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"An error occurred: {str(e)}", exc_info=True)
|
||||
db.session.rollback()
|
||||
return ResponseHelper.internal_server_error()
|
||||
@@ -1,103 +0,0 @@
|
||||
from flask import request, jsonify
|
||||
from marshmallow import ValidationError
|
||||
from app.api.enums.loan_status import LoanStatus
|
||||
from app.api.helpers.response_helper import ResponseHelper
|
||||
from app.models import Repayment
|
||||
from app.models.customer import Customer
|
||||
from app.models.loan import Loan
|
||||
from app.utils.logger import logger
|
||||
from app.api.schemas.repayment import RepaymentSchema
|
||||
from app.api.services.base_service import BaseService
|
||||
from app.api.enums import TransactionType
|
||||
from threading import Thread
|
||||
from app.extensions import db
|
||||
|
||||
class RepaymentService(BaseService):
|
||||
TRANSACTION_TYPE = TransactionType.REPAYMENT
|
||||
|
||||
@staticmethod
|
||||
def process_request(data):
|
||||
"""
|
||||
Process the Repayment request.
|
||||
|
||||
Args:
|
||||
data (dict): The request data.
|
||||
|
||||
Returns:
|
||||
dict: A standardized response.
|
||||
"""
|
||||
try:
|
||||
with db.session.begin():
|
||||
validated_data = RepaymentService.validate_data(data, RepaymentSchema())
|
||||
|
||||
customer_id = validated_data.get('customerId')
|
||||
request_id = validated_data.get('requestId')
|
||||
loan_id = validated_data.get('debtId')
|
||||
account_id = validated_data.get('accountId')
|
||||
loan_ref = validated_data.get('loanRef')
|
||||
# customer = Customer.get_customer_with_loan_list(customer_id)
|
||||
transaction_id = validated_data.get('transactionId')
|
||||
initiated_by = validated_data.get('initiatedBy')
|
||||
logger.error(f"HERE 0002a **** ")
|
||||
if(RepaymentService.validate_account_ownership(account_id = account_id, customer_id = customer_id)):
|
||||
logger.error(f"HERE 0001a **** ")
|
||||
# Check loan exists
|
||||
loan = Loan.get_customer_loan(loan_id = loan_id, customer_id = customer_id)
|
||||
|
||||
# Save the repayment details
|
||||
repayment = Repayment.create_repayment(
|
||||
customer_id = customer_id,
|
||||
loan = loan,
|
||||
transaction_id = transaction_id
|
||||
)
|
||||
|
||||
if not repayment:
|
||||
logger.error(f"Failed to save repayment details")
|
||||
return ResponseHelper.error(result_description="Failed to save repayment details.")
|
||||
|
||||
#Update Loan status
|
||||
Loan.update_status(loan_id = loan_id, status = LoanStatus.START_REPAY) # repay started bu user
|
||||
transaction = RepaymentService.log_transaction(validated_data = validated_data)
|
||||
|
||||
if not transaction:
|
||||
logger.error(f"Failed to log transaction")
|
||||
return ResponseHelper.error(result_description="Failed to log transaction.")
|
||||
else:
|
||||
logger.error(f"Invalid Customer or AccountID {account_id} to CustomerID{customer_id} ")
|
||||
return ResponseHelper.error(result_description="Invalid Customer or Account")
|
||||
|
||||
# Simulated processing logic
|
||||
# TODO start using repayment_id instead if id or Id
|
||||
response_data = {
|
||||
"Id": repayment.id,
|
||||
"repayment_id": repayment.id,
|
||||
"initiated_by": repayment.initiated_by,
|
||||
"transactionId": transaction_id,
|
||||
"customerId": customer_id,
|
||||
"productId": loan.product_id,
|
||||
"loanRef": loan_ref,
|
||||
"debtId": loan_id
|
||||
}
|
||||
|
||||
# Call Kafka in a background thread
|
||||
thread = Thread(target=RepaymentService.async_send_to_kafka, args=(response_data, request_id, "LOAN_REPAYMENT"))
|
||||
thread.start()
|
||||
|
||||
db.session.commit()
|
||||
return ResponseHelper.success(data=response_data)
|
||||
|
||||
except ValidationError as err:
|
||||
|
||||
logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}")
|
||||
db.session.rollback()
|
||||
return ResponseHelper.unprocessable_entity(result_description="Validation exception")
|
||||
|
||||
except ValueError as err:
|
||||
logger.error(f"{getattr(err, 'messages', str(err))}")
|
||||
db.session.rollback()
|
||||
return ResponseHelper.error(result_description=str(err))
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"An error occurred: {str(e)}", exc_info=True)
|
||||
db.session.rollback()
|
||||
return ResponseHelper.internal_server_error()
|
||||
@@ -1,168 +0,0 @@
|
||||
from flask import request, jsonify
|
||||
from marshmallow import ValidationError
|
||||
from app.api.helpers.response_helper import ResponseHelper
|
||||
from app.api.services.base_service import BaseService
|
||||
from app.api.enums import TransactionType
|
||||
from app.models.transaction_offers import TransactionOffer
|
||||
from app.utils.logger import logger
|
||||
from app.api.schemas.select_offer import SelectOfferSchema
|
||||
from app.extensions import db
|
||||
from app.models import Offer
|
||||
from datetime import date
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
class SelectOfferService(BaseService):
|
||||
TRANSACTION_TYPE = TransactionType.SELECT_OFFER
|
||||
|
||||
@staticmethod
|
||||
def process_request(data):
|
||||
"""
|
||||
Process the SelectOffer request.
|
||||
|
||||
Args:
|
||||
data (dict): The request data.
|
||||
|
||||
Returns:
|
||||
dict: A standardized response.
|
||||
"""
|
||||
try:
|
||||
with db.session.begin():
|
||||
validated_data = SelectOfferService.validate_data(
|
||||
data, SelectOfferSchema()
|
||||
)
|
||||
account_id = validated_data.get("accountId")
|
||||
customer_id = validated_data.get("customerId")
|
||||
amount = validated_data.get("requestedAmount")
|
||||
product_id = validated_data.get("productId")
|
||||
transaction_offer_id = validated_data.get("offerId")
|
||||
transaction_id = validated_data.get("transactionId")
|
||||
request_id = validated_data.get("requestId")
|
||||
|
||||
offer_id = int(transaction_offer_id[5:]) # The last part is int
|
||||
|
||||
#"offerId": "SAL30001129",
|
||||
|
||||
if SelectOfferService.validate_account_ownership(
|
||||
account_id=account_id, customer_id=customer_id
|
||||
):
|
||||
transaction = SelectOfferService.log_transaction(
|
||||
validated_data=validated_data
|
||||
)
|
||||
|
||||
if not transaction:
|
||||
logger.error(f"Failed to log transaction")
|
||||
return ResponseHelper.error(result_description="Failed to log transaction.")
|
||||
else:
|
||||
return ResponseHelper.error(result_description="Invalid Customer or Account")
|
||||
|
||||
# Get the offer by product ID
|
||||
offer = Offer.get_offer_by_product_id(product_id)
|
||||
|
||||
transaction_offer = TransactionOffer.get_transaction_offer(transaction_offer_id=offer_id)
|
||||
|
||||
if not transaction_offer:
|
||||
logger.error(f"offer {offer_id} not found for customer {customer_id} and transaction {transaction_id}.")
|
||||
return ResponseHelper.error(result_description="Offer not found.")
|
||||
|
||||
db.session.flush()
|
||||
|
||||
if amount < transaction_offer.min_amount:
|
||||
logger.error(f"The amount {amount} is less than the minimum allowed offer amount {transaction_offer.min_amount}.")
|
||||
return ResponseHelper.error(result_description="The amount is less than the minimum allowed offer amount.")
|
||||
elif amount > transaction_offer.eligible_amount:
|
||||
logger.error(f"The amount {amount} is greater than the eligible offer amount {transaction_offer.eligible_amount}.")
|
||||
return ResponseHelper.error(result_description="The amount is greater than the eligible offer amount.")
|
||||
|
||||
|
||||
|
||||
charges = SelectOfferService.calculate_charges(offer, amount)
|
||||
upfront_payment = charges["upfront_payment"]
|
||||
total_amount = charges["total_amount"]
|
||||
installment_amount = charges["installment_amount"]
|
||||
interest = charges["interest"]
|
||||
management = charges["management"]
|
||||
insurance = charges["insurance"]
|
||||
vat = charges["vat"]
|
||||
repayment_amount = charges["repayment_amount"]
|
||||
interest_amount = charges["interest_amount"]
|
||||
|
||||
|
||||
# Calculate the repayment dates
|
||||
tenor = offer.tenor
|
||||
start_date = date.today()
|
||||
|
||||
# Convert tenor to months
|
||||
months = offer.schedule # tenor // 30
|
||||
|
||||
recommended_repayment_dates = [
|
||||
(start_date + relativedelta(months=i + 1)).isoformat()
|
||||
for i in range(months)
|
||||
]
|
||||
|
||||
|
||||
|
||||
offers = [
|
||||
{
|
||||
"offerId": transaction_offer_id,
|
||||
"productId": product_id,
|
||||
"amount": amount,
|
||||
"upfrontPayment": upfront_payment,
|
||||
"interestRate": offer.interest_rate,
|
||||
"interestFee": interest_amount,
|
||||
"managementRate": offer.management_rate,
|
||||
"managementFee": management["fee"],
|
||||
"insuranceRate": offer.insurance_rate,
|
||||
"insuranceFee": insurance["fee"],
|
||||
"VATRate": offer.vat_rate,
|
||||
"VATAmount": vat["fee"],
|
||||
"recommendedRepaymentDates": recommended_repayment_dates,
|
||||
"repaymentAmount": repayment_amount,
|
||||
"installmentAmount": installment_amount,
|
||||
"totalRepaymentAmount": total_amount,
|
||||
}
|
||||
]
|
||||
|
||||
# "offerId": offer.id,
|
||||
# "productId": product_id,
|
||||
# "amount": amount,
|
||||
# "upfrontPayment": upfront_payment,
|
||||
# "interestRate": interest["rate"],
|
||||
# "managementRate": management["rate"],
|
||||
# "managementFee": management["fee"],
|
||||
# "insuranceRate": insurance["rate"],
|
||||
# "insuranceFee": insurance["fee"],
|
||||
# "VATRate": vat["rate"],
|
||||
# "VATAmount": vat["fee"],
|
||||
# "recommendedRepaymentDates": recommended_repayment_dates,
|
||||
# "installmentAmount": installment_amount,
|
||||
# "totalRepaymentAmount": total_amount,
|
||||
#
|
||||
# Business logic - selecting an offer
|
||||
response_data = {
|
||||
"outstandingDebtAmount": 0,
|
||||
"requestId": request_id,
|
||||
"transactionId": transaction_id,
|
||||
"customerId": customer_id,
|
||||
"accountId": account_id,
|
||||
"loan": offers,
|
||||
}
|
||||
|
||||
db.session.commit()
|
||||
return ResponseHelper.success(data=response_data)
|
||||
|
||||
except ValidationError as err:
|
||||
|
||||
logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}")
|
||||
db.session.rollback()
|
||||
return ResponseHelper.unprocessable_entity(result_description="Validation exception")
|
||||
|
||||
except ValueError as err:
|
||||
logger.error(f"{getattr(err, 'messages', str(err))}")
|
||||
db.session.rollback()
|
||||
return ResponseHelper.error(result_description=str(err))
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"An error occurred: {str(e)}", exc_info=True)
|
||||
db.session.rollback()
|
||||
return ResponseHelper.internal_server_error()
|
||||
|
||||
Reference in New Issue
Block a user