Merge branch 'oluyemi' of DigiFi/digifi-EventManager into master

This commit is contained in:
2025-04-17 21:07:23 +00:00
committed by Gogs
12 changed files with 346 additions and 19 deletions
+56 -10
View File
@@ -1,6 +1,9 @@
import requests
from app.config import settings
from app.helpers.response_helper import ResponseHelper
from app.services.loan import LoanService
from app.utils.auth import get_headers
from app.utils.extras import preprocess_loan_charges_data
from app.utils.logger import logger
from flask import jsonify, current_app
from app.services.transactions import TransactionService
@@ -26,21 +29,42 @@ class SimbrellaClient:
logger.info(f"Transaction id: {data['transactionId']}, was not found")
return 0
# Fetch the loan based on the transaction_id
logger.info(f"Fetching the loan with transaction ID: {data['transactionId']}")
loan = LoanService.get_loan_by_transaction_id(transaction_id=data['transactionId'])
logger.info(f"Response from database: {loan}")
# If loan is not found
if not loan:
logger.info(f"Could not find loan with transaction id: {data['transactionId']}")
return 0
loan_data = loan.to_dict()
logger.info(f"Here is your loan data: {loan_data}")
loan_charges = preprocess_loan_charges_data([loan_charge.to_dict() for loan_charge in loan.loan_charges])
logger.info(f"Here are your loan_charges: {loan_charges}")
mgt_fee = loan_charges.get("MGTFEE")['amount']
vat_fee = loan_charges.get("VAT")['amount']
disbursement_data ={
"requestId": data['requestId'],
"transactionId": data['transactionId'],
"debtId": "273194670",
"debtId": loan_data['debtId'],
"customerId": data['customerId'],
"accountId": data['accountId'],
"productId": "101",
"provideAmount": 100000,
"productId": loan_data['productId'],
"provideAmount": loan_data['currentLoanAmount'],
"collectAmountInterest": 5000,
"collectAmountMgtFee": 1000,
"collectAmountMgtFee": mgt_fee,
"collectAmountInsurance": 1000,
"collectAmountVAT": 75,
"collectAmountVAT": vat_fee,
"countryId": "01",
"comment": "Loan Disbursement",
}
try:
logger.info(f"Here is your Disbursement Request data ****** : {disbursement_data}")
response = requests.post(api_url, json=disbursement_data, timeout=10, headers=get_headers())
@@ -48,10 +72,8 @@ class SimbrellaClient:
except Exception as e:
logger.info(f"Failed to call Disbursement endpoint: {e}")
#raise
return 0
# return jsonify(response.json()), response.status_code
return 1
@staticmethod
@@ -99,10 +121,11 @@ class SimbrellaClient:
def verify_transaction():
try:
return {
data = {
"status": "00",
"message": "Transaction verified"
}
return ResponseHelper.success(data, "Successful")
except Exception as e:
logger.info(f"Failed to call TransactionVerify endpoint: {e}")
@@ -114,7 +137,7 @@ class SimbrellaClient:
try:
logger.info(f"Here is your Disbursement Request data ***** : {data}")
return data
return ResponseHelper.success(data, "Successful")
except Exception as e:
logger.info(f"Failed to call Disbursement endpoint: {e}")
@@ -126,8 +149,31 @@ class SimbrellaClient:
try:
logger.info(f"Here is your Payment Callback Request data ***** : {data}")
return data
return ResponseHelper.success(data, "Successful")
except Exception as e:
logger.info(f"Failed to call Payment Callback endpoint: {e}")
raise
@staticmethod
def penal_charge(data):
api_url = f"{SimbrellaClient.BANK_CALL_BASE_URL}/PenalCharge"
logger.info(f"Calling Penal Charge endpoint with data: {data}")
try:
logger.info(f"Here is your Penal Charge Request data ***** : {data}")
try:
logger.info(f"Here is your Penal Charge Request data ****** : {data}")
response = requests.post(api_url, json=data, timeout=10, headers=get_headers())
logger.info(f"Penal Charge response: {response.json()}")
return ResponseHelper.success(response.json(), "Successful")
except Exception as e:
logger.info(f"Failed to call Penal Charge endpoint: {e}")
return ResponseHelper.error("An error occurred", 500)
except Exception as e:
logger.info(f"Failed to call Penal Charge endpoint: {e}")
raise
+5 -1
View File
@@ -1,4 +1,8 @@
from .transactions import Transaction
from .repayment import Repayment
from .loan import Loan
from .loan_charge import LoanCharge
from .customer import Customer
from .account import Account
__all__ = ['Transaction', 'Repayment']
__all__ = ['Transaction', 'Repayment', 'Loan', 'LoanCharge', 'Customer', 'Account']
+25
View File
@@ -0,0 +1,25 @@
from datetime import datetime, timezone
from sqlalchemy.orm import relationship
from app.extensions import db
class Account(db.Model):
__tablename__ = 'accounts'
id = db.Column(db.String(50), primary_key=True)
customer_id = db.Column(db.String(50), nullable=False)
account_type = db.Column(db.String(50))
status = db.Column(db.String(20), default='active')
lien_amount = 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))
customer = relationship(
"Customer",
primaryjoin="Customer.id == Account.customer_id",
foreign_keys=[customer_id],
back_populates="accounts",
)
def __repr__(self):
return f'<Account {self.id}>'
+41
View File
@@ -0,0 +1,41 @@
from datetime import datetime, timezone
from sqlalchemy.orm import relationship
from app.extensions import db
class Customer(db.Model):
__tablename__ = 'customers'
id = db.Column(db.String(50), primary_key=True)
msisdn = db.Column(db.String(20), unique=True, nullable=False)
country_code = db.Column(db.String(3), nullable=False)
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))
accounts = relationship(
"Account",
primaryjoin="Customer.id == Account.customer_id",
foreign_keys="Account.customer_id",
back_populates="customer",
)
loans = relationship(
"Loan",
primaryjoin="Customer.id == Loan.customer_id",
foreign_keys="Loan.customer_id",
back_populates="customer",
)
@classmethod
def get_customer(cls, customer_id):
"""
Get customer by ID.
"""
customer = cls.query.filter_by(id=customer_id).first()
if not customer:
raise ValueError(f"Customer does not exist")
return customer
def __repr__(self):
return f'<Customer {self.id}>'
+64
View File
@@ -0,0 +1,64 @@
from datetime import datetime, timezone
from app.extensions import db
from sqlalchemy.orm import relationship
class Loan(db.Model):
__tablename__ = "loans"
id = db.Column(
db.Integer,
primary_key=True,
autoincrement=True,
)
customer_id = db.Column(db.String(50), nullable=False)
transaction_id = db.Column(db.String(50), nullable=True)
account_id = db.Column(db.String(50), nullable=False)
offer_id = db.Column(db.String(20), nullable=False)
product_id = db.Column(db.String(20), nullable=True)
collection_type = db.Column(db.String(20), nullable=True)
current_loan_amount = db.Column(db.Float, nullable=True)
initial_loan_amount = db.Column(db.Float, nullable=False)
default_penalty_fee = db.Column(db.Float, default=0)
continuous_fee = db.Column(db.Float, default=0)
status = db.Column(db.String(20), default='pending')
due_date = db.Column(db.DateTime, nullable=True)
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))
customer = relationship(
"Customer",
primaryjoin="Customer.id == Loan.customer_id",
foreign_keys=[customer_id],
back_populates="loans",
)
loan_charges = relationship(
"LoanCharge",
primaryjoin="Loan.id == LoanCharge.loan_id",
foreign_keys="LoanCharge.loan_id",
back_populates="loan",
)
def __repr__(self):
return f"<Loan {self.id}>"
def to_dict(self):
"""
Convert the Loan object to a dictionary format for JSON serialization.
"""
return {
'debtId': self.id,
'initialLoanAmount': self.initial_loan_amount,
'currentLoanAmount': self.current_loan_amount,
'defaultPenaltyFee': self.default_penalty_fee,
'continuousFee': self.continuous_fee,
'collectionType': self.collection_type,
'status': self.status,
'productId': self.product_id,
'dueDate': self.due_date.isoformat() if self.due_date else None,
'loanDate': self.created_at.isoformat if self.created_at else None
}
@classmethod
def get_loan_by_transaction_id(cls, transaction_id):
return cls.query.filter_by(transaction_id=transaction_id).first()
+49
View File
@@ -0,0 +1,49 @@
from datetime import datetime, timezone, timedelta
from os.path import devnull
from app.extensions import db
from sqlalchemy.orm import relationship
class LoanCharge(db.Model):
__tablename__ = 'loan_charges'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
loan_id = db.Column(db.Integer, nullable=False)
transaction_id = db.Column(db.String(50), nullable=True)
code = db.Column(db.String(50), nullable=False)
amount = db.Column(db.Float, default=0.0)
percent = db.Column(db.Float, default=0.0)
description = db.Column(db.Text, nullable=True)
due = db.Column(db.Integer, nullable=False)
due_date = db.Column(db.DateTime, nullable=True)
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))
loan = relationship(
"Loan",
primaryjoin="LoanCharge.loan_id == Loan.id",
foreign_keys=[loan_id],
back_populates="loan_charges",
)
def __repr__(self):
return f"<LoanCharge {self.id} - Loan {self.loan_id} - {self.code}>"
def to_dict(self):
"""
Convert the Loan charge object to a dictionary format for JSON serialization.
"""
return {
'id': self.id,
'loanId': self.loan_id,
'transactionId': self.transaction_id,
'code': self.code,
'amount': self.amount,
'percent': self.percent,
'description': self.description,
'due': self.due
}
@classmethod
def get_loan_charge_by_debt_id(cls, debt_id):
return cls.query.filter_by(loan_id=debt_id)
+13
View File
@@ -20,6 +20,19 @@ class Transaction(db.Model):
def __repr__(self):
return f'<Transaction {self.id}>'
def to_dict(self):
"""
Convert the Transaction object to a dictionary format for JSON serialization.
"""
return {
'id': self.id,
'transaction_id': self.transaction_id,
'account_id': self.account_id,
'customer_id': self.customer_id,
'type': self.type,
'channel': self.channel,
}
@classmethod
def get_transaction_by_transaction_id(cls, transaction_id):
return cls.query.filter_by(transaction_id=transaction_id).first()
+4 -4
View File
@@ -15,7 +15,7 @@ def health():
@auth_bp.route("/login", methods=["POST"])
def login():
data = request.json
data = request.get_json()
api_url = f"{BASE_URL}/login"
response = requests.post(api_url, json=data)
@@ -26,7 +26,7 @@ def login():
@auth_bp.route("/status-call", methods=["POST"])
def status_call():
data = request.json
data = request.get_json()
api_url = f"{BASE_URL}/StatusCall"
# response = requests.post(api_url, json=data, headers=get_headers())
@@ -49,7 +49,7 @@ def status_call():
@auth_bp.route("/sms", methods=["POST"])
def sms():
data = request.json
data = request.get_json()
api_url = f"{BASE_URL}/SMS"
# response = requests.post(api_url, json=data, headers=get_headers())
@@ -66,7 +66,7 @@ def sms():
@auth_bp.route("/bulk-sms", methods=["POST"])
def bulk_sms():
data = request.json
data = request.get_json()
api_url = f"{BASE_URL}/BulkSMS"
# response = requests.post(api_url, json=data, headers=get_headers())
+14 -4
View File
@@ -1,6 +1,7 @@
from flask import Blueprint, request, jsonify, current_app
import requests
from app.config import settings
from app.helpers.response_helper import ResponseHelper
from app.utils.auth import get_headers
from app.utils.logger import logger
from app.integrations.simbrella import SimbrellaClient
@@ -14,7 +15,7 @@ def verify_transaction():
response = SimbrellaClient.verify_transaction()
return jsonify(response), 200
return response
@autocall_bp.route("/refresh-disbursement", methods=["GET"])
def disbursement():
@@ -23,14 +24,23 @@ def disbursement():
response = SimbrellaClient.verify_transaction()
return jsonify(response), 200
return response
@autocall_bp.route("/payment-callback", methods=["POST"])
def payment_callback():
data = request.json()
data = request.get_json()
logger.info(f"Calling Callback Components")
response = SimbrellaClient.payment_callback(data)
return jsonify(response), 200
return response
@autocall_bp.route("/penal-charge", methods=["POST"])
def penal_charge():
data = request.get_json()
logger.info(f"Calling Penal Charge Endpoints")
response = SimbrellaClient.penal_charge(data[0])
return response
+17
View File
@@ -0,0 +1,17 @@
from app.models import Loan, LoanCharge
class LoanService:
@classmethod
def get_loan_by_transaction_id(cls, transaction_id):
"""
Get the loan by transaction ID
"""
return Loan.get_loan_by_transaction_id(transaction_id)
@classmethod
def get_loan_charge_by_debt_id(cls, debt_id):
"""
Get the loan charge by debt ID
"""
return LoanCharge.get_loan_charge_by_debt_id(debt_id)
+16
View File
@@ -0,0 +1,16 @@
def preprocess_loan_charges_data(data):
"""
Preprocesses the data into a dictionary for efficient lookups by 'code'.
Args:
data: A list of dictionaries.
Returns:
A dictionary where keys are 'code' values and values are the corresponding dictionaries from the input data.
If multiple items have the same code, the last one encountered will be stored.
"""
preprocessed = {}
for item in data:
if 'code' in item:
preprocessed[item['code']] = item
return preprocessed
+42
View File
@@ -113,6 +113,48 @@ paths:
/autocall/payment-callback:
get:
summary: The Payment callback
responses:
200:
description: A successful response
/autocall/penal-charge:
post:
summary: Penal Charge Request
requestBody:
required: true
content:
application/json:
schema:
type: array
items:
type: object
properties:
transactionId:
type: string
example: "T004"
fbnTransactionId:
type: string
example: "Tr201712RK9232P115"
debtId:
type: string
example: "273194670"
customerId:
type: string
example: "CN621868"
accountId:
type: string
example: "2017821799"
penalCharge:
type: number
example: "1.2"
lienAmount:
type: number
example: "101.2"
countryId:
type: string
example: "01"
comment:
type: string
example: "Testing PenalCharge"
responses:
200:
description: A successful response