Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 70e15cd325 | |||
| e8d930f9b8 | |||
| c400f1d69d | |||
| f7daa12531 | |||
| 3242a57586 | |||
| 463c0a0def | |||
| bb4d7ac064 | |||
| 5a2161acaa | |||
| 9ea0027f71 |
@@ -17,35 +17,6 @@ class SimbrellaIntegration:
|
||||
url = f"{SimbrellaIntegration.BASE_URL}/{SimbrellaIntegration.ENDPOINT_RAC_CHECKS}"
|
||||
logger.info(f"Contacting Rack Checks EndPoint: {str(url)}", exc_info=True)
|
||||
|
||||
# {
|
||||
# "transactionId": "T001",
|
||||
# "fbnTransactionId": "Tr201712RK9232P115",
|
||||
# "customerId": "CN621868",
|
||||
# "accountId": "2017821799",
|
||||
# "channel": "USSD",
|
||||
# "countryCode": "NG"
|
||||
# }
|
||||
#
|
||||
payload_old = {
|
||||
"customerId": customer_id,
|
||||
"accountId": account_id,
|
||||
"transactionId": str(transaction_id),
|
||||
"fbnTransactionId": f"FBN{transaction_id}",
|
||||
"RAC_Array": [
|
||||
"SalaryAccount",
|
||||
"BVN",
|
||||
"BVNAttachedtoAccount",
|
||||
"CRC",
|
||||
"CRMS",
|
||||
"AccountStatus",
|
||||
"Lien",
|
||||
"NoBouncedCheck",
|
||||
"Whitelist",
|
||||
"NoPastDueSalaryLoan",
|
||||
"NoPastDueOtherLoan",
|
||||
],
|
||||
}
|
||||
|
||||
payload = {
|
||||
"customerId": customer_id,
|
||||
"accountId": account_id,
|
||||
@@ -54,7 +25,6 @@ class SimbrellaIntegration:
|
||||
"countryCode": "NG",
|
||||
"channel": "USSD"
|
||||
}
|
||||
# logger.info(f"This is PayLoad: {str(payload)}", exc_info=True)
|
||||
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
|
||||
@@ -88,6 +88,7 @@ def loan_status():
|
||||
@jwt_required()
|
||||
def repayment():
|
||||
data = request.get_json()
|
||||
logger.error(f"HERE 0000a **** ")
|
||||
# logger.info(f"Repayment request received: {data}")
|
||||
response = RepaymentService.process_request(data)
|
||||
return response
|
||||
|
||||
@@ -48,12 +48,14 @@ class BaseService:
|
||||
"""
|
||||
Create a new transaction.
|
||||
"""
|
||||
channel = "USSD" if validated_data.get("channel") is None else validated_data.get("channel")
|
||||
|
||||
return Transaction.create_transaction(
|
||||
transaction_id = validated_data.get("transactionId"),
|
||||
customer_id = validated_data.get('customerId', None),
|
||||
account_id = validated_data.get("accountId", None),
|
||||
type = cls.TRANSACTION_TYPE,
|
||||
channel = validated_data.get("channel"),
|
||||
channel = channel,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -81,7 +81,7 @@ class EligibilityCheckService(BaseService):
|
||||
|
||||
return ResponseHelper.error(result_description=f"RACCheck failed")
|
||||
|
||||
rack_checks_response = response['data']['racResponse']
|
||||
rack_checks_response = response['racResponse']
|
||||
|
||||
rac_check = RACCheck.add_rac_check(
|
||||
customer_id = customer_id,
|
||||
|
||||
@@ -55,6 +55,7 @@ class LoanStatusService(BaseService):
|
||||
# Simulated processing logic
|
||||
response_data = {
|
||||
"customerId": customer_id,
|
||||
"accountId": account_id,
|
||||
"transactionId": transactionId,
|
||||
"loans": loans,
|
||||
"totalDebtAmount": total_debt_amount,
|
||||
|
||||
@@ -2,6 +2,10 @@ from app.models import Offer, TransactionOffer
|
||||
from app.models.loan import Loan
|
||||
import random
|
||||
import logging
|
||||
|
||||
from app.config import Config
|
||||
|
||||
RAC_CHECK_RULES = Config.rac_true_rules
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class OfferAnalysis:
|
||||
@@ -34,7 +38,8 @@ class OfferAnalysis:
|
||||
return transaction_offer, offer, eligible_amount, original_transaction
|
||||
@staticmethod
|
||||
def _analyze_rack_checks(rack_response):
|
||||
|
||||
logger.info(f"This is PayLoad for ANALYSYS ***** : {str(rack_response)}", exc_info=True)
|
||||
logger.info(f"RACk RUKES {str(RAC_CHECK_RULES)}", exc_info=True)
|
||||
# "racResponse": {
|
||||
# "accountStatus": true,
|
||||
# "bvnValidated": true,
|
||||
@@ -48,6 +53,17 @@ class OfferAnalysis:
|
||||
# },
|
||||
#
|
||||
|
||||
'''
|
||||
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
|
||||
@@ -56,8 +72,7 @@ class OfferAnalysis:
|
||||
# if we have active offers - we have to feed off it
|
||||
logger.info(f"**RACK ANALYSIS** {customer_id}")
|
||||
# Analyze Rack Checks
|
||||
# _analyze_rack_checks(rack_checks_response) --> We need detail analysis
|
||||
|
||||
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
|
||||
|
||||
@@ -29,6 +29,7 @@ class RepaymentService(BaseService):
|
||||
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')
|
||||
@@ -37,9 +38,9 @@ class RepaymentService(BaseService):
|
||||
# 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)
|
||||
|
||||
@@ -61,7 +62,8 @@ class RepaymentService(BaseService):
|
||||
if not transaction:
|
||||
logger.error(f"Failed to log transaction")
|
||||
return ResponseHelper.error(result_description="Failed to log transaction.")
|
||||
else:
|
||||
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
|
||||
|
||||
@@ -48,4 +48,26 @@ class Config:
|
||||
RAC_RESULT_isWhitelisted = os.environ.get("RAC_RESULT_isWhitelisted", "true")
|
||||
RAC_RESULT_noBouncedCheck = os.environ.get("RAC_RESULT_noBouncedCheck", "true")
|
||||
|
||||
rac_true_rules = [
|
||||
"rule1-45day-sal",
|
||||
"rule2-2m-sal",
|
||||
"rule3-no-bounced-check",
|
||||
"rule4-current-loan-payments",
|
||||
"rule5-no-past-due-fadv-loan",
|
||||
"rule6--no-past-due-other-loan",
|
||||
"rule7-consistent-salary-amount",
|
||||
"rule8-whitelisted",
|
||||
"rule9-regular-account",
|
||||
"rule10-bvn-validation",
|
||||
"rule11-CRC-no-delinquency",
|
||||
"rule12-CRMS-no-delinquency",
|
||||
"rule13-BVN-ignore",
|
||||
"rule14-no-lien",
|
||||
"rule15-null-ignore"
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
settings = Config()
|
||||
|
||||
@@ -9,6 +9,7 @@ from .charge import Charge
|
||||
from .rac_checks import RACCheck
|
||||
from .loan_repayment_schedule import LoanRepaymentSchedule
|
||||
from .transaction_offers import TransactionOffer
|
||||
from .repayments_data import RepaymentsData
|
||||
|
||||
|
||||
__all__ = ['Customer', 'Account', 'Loan', 'Transaction', 'Repayment', 'LoanCharge', 'Offer', 'Charge', 'RACCheck', 'LoanRepaymentSchedule', 'TransactionOffer']
|
||||
__all__ = ['Customer', 'Account', 'Loan', 'Transaction', 'Repayment', 'LoanCharge', 'Offer', 'Charge', 'RACCheck', 'LoanRepaymentSchedule', 'TransactionOffer', 'RepaymentsData']
|
||||
@@ -45,6 +45,10 @@ class Loan(db.Model):
|
||||
disburse_date = db.Column(db.DateTime, nullable=True)
|
||||
disburse_verify = db.Column(db.DateTime, nullable=True)
|
||||
reference = db.Column(db.String(50), nullable=True)
|
||||
disburse_result = db.Column(db.String(10), nullable=True)
|
||||
disburse_description = db.Column(db.String(100), nullable=True)
|
||||
verify_result = db.Column(db.String(10), nullable=True)
|
||||
verify_description = db.Column(db.String(100), nullable=True)
|
||||
|
||||
customer = relationship(
|
||||
"Customer",
|
||||
|
||||
@@ -21,6 +21,12 @@ class Repayment(db.Model):
|
||||
created_at = db.Column(db.DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = db.Column(db.DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
transaction_id = db.Column(db.String(50), nullable=True)
|
||||
repay_date = db.Column(db.DateTime, default=datetime.now(timezone.utc))
|
||||
repay_result = db.Column(db.String(10), nullable=True)
|
||||
repay_description = db.Column(db.String(100), nullable=True)
|
||||
verify_date = db.Column(db.DateTime, default=datetime.now(timezone.utc))
|
||||
verify_result = db.Column(db.String(10), nullable=True)
|
||||
verify_description = db.Column(db.String(100), nullable=True)
|
||||
|
||||
@classmethod
|
||||
def create_repayment(cls, customer_id, loan, transaction_id):
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
from datetime import datetime, timezone
|
||||
from app.extensions import db
|
||||
|
||||
class RepaymentsData(db.Model):
|
||||
__tablename__ = 'repayments_data'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
||||
transaction_id = db.Column(db.String(50), nullable=False)
|
||||
added_date = db.Column(db.DateTime(timezone=True), default=datetime.now(timezone.utc), nullable=False)
|
||||
response_code = db.Column(db.String(10), nullable=True)
|
||||
response_descr = db.Column(db.String(255), nullable=True)
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"transaction_id": self.transaction_id,
|
||||
"added_date": self.added_date.isoformat() if self.added_date else None,
|
||||
"response_code": self.response_code,
|
||||
"response_descr": self.response_descr,
|
||||
}
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return f"<RepaymentsData id={self.id}, transaction_id={self.transaction_id}>"
|
||||
@@ -0,0 +1,35 @@
|
||||
"""Migration on Tue Jun 10 08:41:00 WAT 2025
|
||||
|
||||
Revision ID: 0acd553309a1
|
||||
Revises: 45790fd659fb
|
||||
Create Date: 2025-06-10 08:41:45.222513
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '0acd553309a1'
|
||||
down_revision = '45790fd659fb'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('repayments_data',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('transaction_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('added_date', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column('response_code', sa.String(length=10), nullable=True),
|
||||
sa.Column('response_descr', sa.String(length=255), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('repayments_data')
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,54 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 45790fd659fb
|
||||
Revises: b3a5e10bc77e
|
||||
Create Date: 2025-06-04 12:37:48.180736
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '45790fd659fb'
|
||||
down_revision = 'b3a5e10bc77e'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('loans', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('disburse_result', sa.String(length=10), nullable=True))
|
||||
batch_op.add_column(sa.Column('disburse_description', sa.String(length=100), nullable=True))
|
||||
batch_op.add_column(sa.Column('verify_result', sa.String(length=10), nullable=True))
|
||||
batch_op.add_column(sa.Column('verify_description', sa.String(length=100), nullable=True))
|
||||
|
||||
with op.batch_alter_table('repayments', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('repay_date', sa.DateTime(), nullable=True))
|
||||
batch_op.add_column(sa.Column('repay_result', sa.String(length=10), nullable=True))
|
||||
batch_op.add_column(sa.Column('repay_description', sa.String(length=100), nullable=True))
|
||||
batch_op.add_column(sa.Column('verify_date', sa.DateTime(), nullable=True))
|
||||
batch_op.add_column(sa.Column('verify_result', sa.String(length=10), nullable=True))
|
||||
batch_op.add_column(sa.Column('verify_description', sa.String(length=100), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('repayments', schema=None) as batch_op:
|
||||
batch_op.drop_column('verify_description')
|
||||
batch_op.drop_column('verify_result')
|
||||
batch_op.drop_column('verify_date')
|
||||
batch_op.drop_column('repay_description')
|
||||
batch_op.drop_column('repay_result')
|
||||
batch_op.drop_column('repay_date')
|
||||
|
||||
with op.batch_alter_table('loans', schema=None) as batch_op:
|
||||
batch_op.drop_column('verify_description')
|
||||
batch_op.drop_column('verify_result')
|
||||
batch_op.drop_column('disburse_description')
|
||||
batch_op.drop_column('disburse_result')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
Reference in New Issue
Block a user