Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d3ceda7245 | |||
| 6e08b0680f | |||
| 3c0e00d9e4 | |||
| b502c128f9 | |||
| c81a029e20 | |||
| 469d94cea1 | |||
| d6aabb959e | |||
| 3a970816f5 | |||
| 65c1608b92 | |||
| 2493c075c0 | |||
| 4ea06de2bc | |||
| 1aa6a1710c | |||
| dff000dbb2 | |||
| 617738b785 | |||
| 9db3b68b13 | |||
| 1795db35be | |||
| 8bb5ce69e2 | |||
| 5087afcca6 | |||
| 11b52357ba | |||
| e8361668e5 | |||
| f659fa9cf2 | |||
| 23e340be27 | |||
| adc2498044 | |||
| eb7f783b18 | |||
| 5040002c54 | |||
| dc9415ff79 | |||
| 4822de764a | |||
| 51995a3e02 | |||
| 265bba2365 | |||
| 9985a58b56 | |||
| b41df3fe02 | |||
| 08fe04b7b9 | |||
| 79317632b6 | |||
| 0d87036b92 | |||
| 1734007476 | |||
| 6ef2be9625 | |||
| 48020f5284 | |||
| 1a6ac6a37f | |||
| bbf6953dc5 | |||
| a2158a768e | |||
| 0af1b7567b | |||
| 4d08983ae3 |
@@ -18,6 +18,10 @@ from flask_jwt_extended import (
|
||||
|
||||
def create_app():
|
||||
"""Factory function to create a Flask app instance"""
|
||||
# import oracledb
|
||||
|
||||
# oracledb.init_oracle_client(lib_dir=None)
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# Load configuration
|
||||
|
||||
@@ -3,5 +3,6 @@ from enum import Enum
|
||||
class LoanStatus(str, Enum):
|
||||
PENDING = "pending"
|
||||
ACTIVE = "active"
|
||||
ACTIVE_PARTIAL = "active_partial"
|
||||
START_REPAY = "start_repay"
|
||||
REPAID = "repaid"
|
||||
@@ -21,7 +21,7 @@ class SimbrellaIntegration:
|
||||
"customerId": customer_id,
|
||||
"accountId": account_id,
|
||||
"transactionId": str(transaction_id),
|
||||
"fbnTransactionId": f"FBN{transaction_id}",
|
||||
"fbnTransactionId": str(transaction_id),
|
||||
"countryCode": "NG",
|
||||
"channel": "USSD"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from flask import session, jsonify
|
||||
from app.models.loan import Loan
|
||||
from app.models.transaction_offers import TransactionOffer
|
||||
from app.utils.logger import logger
|
||||
from app.api.services.base_service import BaseService
|
||||
from app.api.schemas.eligibility_check import EligibilityCheckSchema
|
||||
@@ -81,7 +80,7 @@ class EligibilityCheckService(BaseService):
|
||||
|
||||
return ResponseHelper.error(result_description=f"RACCheck failed")
|
||||
|
||||
rack_checks_response = response['racResponse']
|
||||
rack_checks_response = response['data']['racResponse']
|
||||
|
||||
rac_check = RACCheck.add_rac_check(
|
||||
customer_id = customer_id,
|
||||
@@ -187,10 +186,10 @@ class EligibilityCheckService(BaseService):
|
||||
logger.error(f"Offer not found for offer_id: {offer_id} (customer_id: {customer_id})")
|
||||
return False
|
||||
|
||||
daily_count = TransactionOffer.get_daily_loan_count(customer_id, offer_id)
|
||||
daily_count = Loan.get_daily_loan_count(customer_id, offer.product_id)
|
||||
|
||||
|
||||
logger.error(f"daily_count: {daily_count}, Max: {offer.max_daily_loans}")
|
||||
logger.info(f"daily_count: {daily_count}, Max: {offer.max_daily_loans}")
|
||||
|
||||
if offer.max_daily_loans is not None and daily_count >= offer.max_daily_loans:
|
||||
return False
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from decimal import Decimal
|
||||
from app.models import Offer, TransactionOffer
|
||||
from app.models.loan import Loan
|
||||
import random
|
||||
@@ -5,7 +6,10 @@ import logging
|
||||
|
||||
from app.config import Config
|
||||
|
||||
RAC_CHECK_RULES = Config.rac_true_rules
|
||||
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:
|
||||
@@ -36,10 +40,91 @@ class OfferAnalysis:
|
||||
original_transaction = transaction_id
|
||||
|
||||
return transaction_offer, offer, eligible_amount, original_transaction
|
||||
|
||||
@staticmethod
|
||||
def _analyze_rack_checks(rack_response):
|
||||
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 RUKES {str(RAC_CHECK_RULES)}", 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:
|
||||
if failed_true_rules:
|
||||
logger.warning(f"RAC analysis failed: TRUE rules failed: {failed_true_rules}")
|
||||
if failed_false_rules:
|
||||
logger.warning(f"RAC analysis failed: FALSE rules failed: {failed_false_rules}")
|
||||
if not salaries:
|
||||
logger.warning("RAC analysis failed: No salary records found in RAC response.")
|
||||
raise ValueError("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,
|
||||
@@ -72,15 +157,13 @@ class OfferAnalysis:
|
||||
# if we have active offers - we have to feed off it
|
||||
logger.info(f"**RACK ANALYSIS** {customer_id}")
|
||||
# Analyze Rack Checks
|
||||
OfferAnalysis._analyze_rack_checks(rack_checks_response) #--> We need detail analysis
|
||||
# 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}")
|
||||
|
||||
new_eligible_amount = 0
|
||||
|
||||
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}")
|
||||
@@ -132,12 +215,18 @@ class OfferAnalysis:
|
||||
|
||||
|
||||
for offer in offers:
|
||||
# Get approved amount
|
||||
random_float = random.random() # temporary to play data
|
||||
|
||||
new_eligible_amount = OfferAnalysis._analyze_rack_checks(rack_checks_response, offer)
|
||||
|
||||
approved_amount = new_eligible_amount if new_eligible_amount > 0 else min(offer.max_amount, offer.max_amount * random_float)
|
||||
|
||||
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,
|
||||
|
||||
@@ -67,7 +67,11 @@ class RepaymentService(BaseService):
|
||||
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,
|
||||
|
||||
@@ -3,6 +3,7 @@ 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
|
||||
@@ -57,12 +58,20 @@ class SelectOfferService(BaseService):
|
||||
# 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 < offer.min_amount:
|
||||
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 > offer.max_amount:
|
||||
return ResponseHelper.error(result_description="The amount is greater than the maximum 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.")
|
||||
|
||||
|
||||
|
||||
|
||||
+37
-19
@@ -16,10 +16,16 @@ class Config:
|
||||
DATABASE_PASSWORD = os.environ.get("DATABASE_PASSWORD")
|
||||
DATABASE_HOST = os.environ.get("DATABASE_HOST")
|
||||
DATABASE_PORT = os.environ.get("DATABASE_PORT", 10532)
|
||||
DATABASE_NAME = os.environ.get("DATABASE_NAME")
|
||||
DATABASE_NAME = os.environ.get("DATABASE_NAME", "firstadvancedev")
|
||||
DATABASE_SID = os.environ.get("DATABASE_SID", "FREE")
|
||||
DNS = f"(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST={DATABASE_HOST})(PORT={DATABASE_PORT}))(CONNECT_DATA=(SID={DATABASE_SID})))"
|
||||
|
||||
|
||||
# Database Connection
|
||||
SQLALCHEMY_DATABASE_URI = f"postgresql+psycopg2://{DATABASE_USER}:{DATABASE_PASSWORD}@{DATABASE_HOST}:{DATABASE_PORT}/{DATABASE_NAME}"
|
||||
# SQLALCHEMY_DATABASE_URI = f"postgresql+psycopg2://{DATABASE_USER}:{DATABASE_PASSWORD}@{DATABASE_HOST}:{DATABASE_PORT}/{DATABASE_NAME}"
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = (f"oracle+oracledb://{DATABASE_USER}:{DATABASE_PASSWORD}@{DNS}")
|
||||
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
|
||||
|
||||
@@ -34,7 +40,7 @@ class Config:
|
||||
|
||||
# SIMBRELLA_ENDPOINT_RAC_CHECKS = os.getenv("SIMBRELLA_ENDPOINT_RAC_CHECKS", "RACCheck")
|
||||
VALID_APP_ID = os.getenv("SIMBRELLA_APP_ID", "app1")
|
||||
VALID_API_KEY = os.getenv("SIMBRELLA_API_KEY", "testtest-api-key-12345")
|
||||
VALID_API_KEY = os.getenv("SIMBRELLA_API_KEY", "test-api-key-12345")
|
||||
SIMBRELLA_BASE_URL = os.getenv("SIMBRELLA_BASE_URL", "http://127.0.0.1:6337")
|
||||
SIMBRELLA_ENDPOINT_RAC_CHECKS = os.getenv("SIMBRELLA_ENDPOINT_RAC_CHECKS","api/rac-check")
|
||||
|
||||
@@ -49,25 +55,37 @@ class Config:
|
||||
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"
|
||||
"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"
|
||||
]
|
||||
|
||||
rac_false_rules = [
|
||||
|
||||
]
|
||||
|
||||
rac_salary_payments = [
|
||||
"salarypaymenT_1",
|
||||
"salarypaymenT_2",
|
||||
"salarypaymenT_3",
|
||||
"salarypaymenT_4",
|
||||
"salarypaymenT_5",
|
||||
"salarypaymenT_6"
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
settings = Config()
|
||||
|
||||
@@ -10,6 +10,7 @@ from .rac_checks import RACCheck
|
||||
from .loan_repayment_schedule import LoanRepaymentSchedule
|
||||
from .transaction_offers import TransactionOffer
|
||||
from .repayments_data import RepaymentsData
|
||||
from .salary import Salary
|
||||
|
||||
|
||||
__all__ = ['Customer', 'Account', 'Loan', 'Transaction', 'Repayment', 'LoanCharge', 'Offer', 'Charge', 'RACCheck', 'LoanRepaymentSchedule', 'TransactionOffer', 'RepaymentsData']
|
||||
__all__ = ['Customer', 'Account', 'Loan', 'Transaction', 'Repayment', 'LoanCharge', 'Offer', 'Charge', 'RACCheck', 'LoanRepaymentSchedule', 'TransactionOffer', 'RepaymentsData', 'Salary']
|
||||
+22
-1
@@ -1,4 +1,4 @@
|
||||
from datetime import datetime, timezone
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from itertools import product
|
||||
from app.extensions import db
|
||||
from app.models.customer import Customer
|
||||
@@ -35,6 +35,7 @@ class Loan(db.Model):
|
||||
continuous_fee = db.Column(db.Float, default=0)
|
||||
upfront_fee = db.Column(db.Float, nullable=True, default=0.0)
|
||||
repayment_amount = db.Column(db.Float, nullable=True, default=0.0)
|
||||
balance = db.Column(db.Float, nullable=True, default=0.0)
|
||||
installment_amount = db.Column(db.Float, nullable=True, default=0.0)
|
||||
status = db.Column(db.String(20), default='pending')
|
||||
tenor = db.Column(db.Integer, nullable=True)
|
||||
@@ -112,6 +113,7 @@ class Loan(db.Model):
|
||||
current_loan_amount = initial_loan_amount,
|
||||
upfront_fee = upfront_fee,
|
||||
repayment_amount = repayment_amount,
|
||||
balance = repayment_amount,
|
||||
installment_amount = installment_amount,
|
||||
due_date=due_date,
|
||||
tenor = tenor,
|
||||
@@ -215,6 +217,25 @@ class Loan(db.Model):
|
||||
# Update loan status and the updated_at timestamp
|
||||
loan.status = status
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_daily_loan_count(cls, customer_id, product_id):
|
||||
"""
|
||||
Returns the count of loans created today for a customer.
|
||||
"""
|
||||
|
||||
start_of_day = datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
end_of_day = start_of_day + timedelta(days=1)
|
||||
|
||||
return cls.query.filter_by(
|
||||
customer_id=customer_id,
|
||||
product_id=product_id,
|
||||
).filter(
|
||||
cls.created_at >= start_of_day,
|
||||
cls.created_at < end_of_day
|
||||
).count()
|
||||
|
||||
|
||||
def to_dict(self):
|
||||
"""
|
||||
Convert the Loan object to a dictionary format for JSON serialization.
|
||||
|
||||
+2
-2
@@ -7,8 +7,8 @@ from sqlalchemy.sql import func
|
||||
class Offer(db.Model):
|
||||
__tablename__ = 'offers'
|
||||
|
||||
id = db.Column(db.String, primary_key=True)
|
||||
product_id = db.Column(db.String, nullable=False)
|
||||
id = db.Column(db.String(50), primary_key=True)
|
||||
product_id = db.Column(db.String(50), nullable=False)
|
||||
min_amount = db.Column(db.Float, nullable=False)
|
||||
max_amount = db.Column(db.Float, nullable=False)
|
||||
tenor = db.Column(db.Integer, nullable=False)
|
||||
|
||||
@@ -3,6 +3,7 @@ from app.extensions import db
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from uuid import uuid4
|
||||
import json
|
||||
from sqlalchemy.types import JSON
|
||||
from sqlalchemy.sql import func
|
||||
|
||||
@@ -11,11 +12,22 @@ class RACCheck(db.Model):
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
||||
transaction_id = db.Column(db.String(50), nullable=False)
|
||||
customer_id = db.Column(db.String, nullable=False)
|
||||
account_id = db.Column(db.String, nullable=False)
|
||||
rac_response = db.Column(db.JSON, nullable=False)
|
||||
customer_id = db.Column(db.String(50), nullable=False)
|
||||
account_id = db.Column(db.String(50), nullable=False)
|
||||
rac_response = db.Column(db.Text, nullable=False)
|
||||
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())
|
||||
|
||||
|
||||
@property
|
||||
def rac_response_data(self):
|
||||
return json.loads(self.rac_response)
|
||||
|
||||
@rac_response_data.setter
|
||||
def rac_response_data(self, value):
|
||||
self.rac_response = json.dumps(value)
|
||||
|
||||
|
||||
@classmethod
|
||||
def add_rac_check(cls, customer_id, account_id, transaction_id, data = None):
|
||||
|
||||
@@ -25,10 +37,11 @@ class RACCheck(db.Model):
|
||||
customer_id = customer_id,
|
||||
account_id = account_id,
|
||||
transaction_id = transaction_id,
|
||||
rac_response = data,
|
||||
created_at=datetime.now(timezone.utc),
|
||||
updated_at=datetime.now(timezone.utc)
|
||||
)
|
||||
|
||||
rac_check.rac_response_data = data or {}
|
||||
|
||||
try:
|
||||
db.session.add(rac_check)
|
||||
@@ -66,7 +79,7 @@ class RACCheck(db.Model):
|
||||
"transactionId": str(self.transaction_id),
|
||||
"customerId": self.customer_id,
|
||||
"accountId": self.account_id,
|
||||
"racResponse": self.rac_response,
|
||||
"racResponse": self.rac_response_data,
|
||||
"createdAt": self.created_at.isoformat(),
|
||||
"updatedAt": self.updated_at.isoformat() if self.updated_at else None
|
||||
}
|
||||
|
||||
+27
-3
@@ -21,12 +21,16 @@ 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_date = db.Column(db.DateTime, default=datetime.now(timezone.utc))
|
||||
repay_date = db.Column(db.DateTime, nullable=True)
|
||||
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_date = db.Column(db.DateTime, default=datetime.now(timezone.utc))
|
||||
verify_date = db.Column(db.DateTime, nullable=True)
|
||||
verify_result = db.Column(db.String(10), nullable=True)
|
||||
verify_description = db.Column(db.String(100), nullable=True)
|
||||
initiated_by = db.Column(db.String(50), nullable=True)
|
||||
salary_amount = db.Column(db.Float, nullable=True, default=0.0)
|
||||
|
||||
@classmethod
|
||||
def create_repayment(cls, customer_id, loan, transaction_id):
|
||||
@@ -42,7 +46,8 @@ class Repayment(db.Model):
|
||||
product_id=loan.product_id,
|
||||
transaction_id = transaction_id,
|
||||
created_at=datetime.now(timezone.utc),
|
||||
updated_at=datetime.now(timezone.utc)
|
||||
updated_at=datetime.now(timezone.utc),
|
||||
initiated_by='USER_INITIATED'
|
||||
)
|
||||
|
||||
try:
|
||||
@@ -51,6 +56,25 @@ class Repayment(db.Model):
|
||||
raise ValueError(f"Database integrity error: {err}")
|
||||
|
||||
return repayment
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"loan_id": self.loan_id,
|
||||
"customer_id": self.customer_id,
|
||||
"product_id": self.product_id,
|
||||
"created_at": self.created_at.isoformat() if self.created_at else None,
|
||||
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
|
||||
"transaction_id": self.transaction_id,
|
||||
"repay_date": self.repay_date.isoformat() if self.repay_date else None,
|
||||
"repay_result": self.repay_result,
|
||||
"repay_description": self.repay_description,
|
||||
"verify_date": self.verify_date.isoformat() if self.verify_date else None,
|
||||
"verify_result": self.verify_result,
|
||||
"verify_description": self.verify_description,
|
||||
"initiated_by": self.initiated_by,
|
||||
"salary_amount": self.salary_amount
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Repayment {self.id}>'
|
||||
|
||||
@@ -1,24 +1,36 @@
|
||||
from datetime import datetime, timezone
|
||||
from app.extensions import db
|
||||
|
||||
|
||||
class RepaymentsData(db.Model):
|
||||
__tablename__ = 'repayments_data'
|
||||
__tablename__ = "repayments_data"
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
||||
transaction_id = db.Column(db.String(50), nullable=False)
|
||||
fbn_transaction_id = db.Column(db.String(50), nullable=True)
|
||||
customer_id = db.Column(db.String(50), nullable=True)
|
||||
account_id = db.Column(db.String(50), nullable=True)
|
||||
repayment_amount = db.Column(db.Float, nullable=True, default=0.0)
|
||||
amount_collected = db.Column(db.Float, nullable=True, default=0.0)
|
||||
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)
|
||||
balance = db.Column(db.Float, nullable=True, default=0.0)
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"transaction_id": self.transaction_id,
|
||||
"fbn_transaction_id": self.fbn_transaction_id,
|
||||
"customer_id": self.customer_id,
|
||||
"account_id": self.account_id,
|
||||
"repayment_amount": self.repayment_amount,
|
||||
"amount_collected": self.amount_collected,
|
||||
"added_date": self.added_date.isoformat() if self.added_date else None,
|
||||
"response_code": self.response_code,
|
||||
"response_descr": self.response_descr,
|
||||
"balance": self.balance,
|
||||
}
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return f"<RepaymentsData id={self.id}, transaction_id={self.transaction_id}>"
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
from datetime import datetime, timezone
|
||||
from app.extensions import db
|
||||
from sqlalchemy.sql import func
|
||||
|
||||
class Salary(db.Model):
|
||||
__tablename__ = 'salaries'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
||||
customer_id = db.Column(db.String(50), nullable=False)
|
||||
account_id = db.Column(db.String(50), nullable=True)
|
||||
status = db.Column(db.String(20), default='active')
|
||||
amount = db.Column(db.Float, nullable=False, default=0.0)
|
||||
salary_date = db.Column(db.DateTime(timezone=False), server_default=func.now())
|
||||
created_at = db.Column(db.DateTime(timezone=False), server_default=func.now())
|
||||
updated_at = db.Column(db.DateTime(timezone=False), server_default=func.now(), onupdate=func.now())
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"customer_id": self.customer_id,
|
||||
"account_id": self.account_id,
|
||||
"status": self.status,
|
||||
"amount": self.amount,
|
||||
"salary_date": self.salary_date.isoformat() if self.salary_date else None,
|
||||
"created_at": self.created_at.isoformat() if self.created_at else None,
|
||||
"updated_at": self.updated_at.isoformat() if self.updated_at else None
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Salary {self.id} - {self.amount}>'
|
||||
|
||||
@@ -17,8 +17,10 @@ class Transaction(db.Model):
|
||||
customer_id = db.Column(db.String(50), nullable=True)
|
||||
type = db.Column(db.String(50), nullable=False)
|
||||
channel = db.Column(db.String(50), nullable=False)
|
||||
phone_number = db.Column(db.String(50), nullable=True)
|
||||
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())
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Transaction {self.id}>'
|
||||
|
||||
|
||||
@@ -76,23 +76,6 @@ class TransactionOffer(db.Model):
|
||||
"""
|
||||
return cls.query.filter_by(customer_id=customer_id).count()
|
||||
|
||||
@classmethod
|
||||
def get_daily_loan_count(cls, customer_id, offer_id):
|
||||
"""
|
||||
Returns the count of loans created today for a customer.
|
||||
"""
|
||||
|
||||
start_of_day = datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
end_of_day = start_of_day + timedelta(days=1)
|
||||
|
||||
return cls.query.filter_by(
|
||||
customer_id=customer_id,
|
||||
offer_id=offer_id
|
||||
).filter(
|
||||
cls.created_at >= start_of_day,
|
||||
cls.created_at < end_of_day
|
||||
).count()
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_latest_transaction_offer(cls, customer_id):
|
||||
@@ -102,6 +85,13 @@ class TransactionOffer(db.Model):
|
||||
return cls.query.filter_by(customer_id=customer_id) \
|
||||
.order_by(cls.created_at.desc()) \
|
||||
.first()
|
||||
|
||||
@classmethod
|
||||
def get_transaction_offer(cls, transaction_offer_id):
|
||||
"""
|
||||
Returns a transaction offer by its ID.
|
||||
"""
|
||||
return cls.query.get(transaction_offer_id)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 05b5494ad406
|
||||
Revises: 33e09efd85e3
|
||||
Create Date: 2025-07-06 09:28:26.264927
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "05b5494ad406"
|
||||
down_revision = "33e09efd85e3"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column("loans", sa.Column("balance", sa.Float(), nullable=True))
|
||||
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column("loans", "balance")
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,35 +0,0 @@
|
||||
"""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 ###
|
||||
@@ -1,32 +0,0 @@
|
||||
"""Migration on Thu Apr 10 21:50:01 UTC 2025
|
||||
|
||||
Revision ID: 1340e7e578b9
|
||||
Revises: b8f6fd76ead8
|
||||
Create Date: 2025-04-10 21:50:32.113149
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '1340e7e578b9'
|
||||
down_revision = 'b8f6fd76ead8'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('transactions', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('ref_model', sa.String(length=50), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('transactions', schema=None) as batch_op:
|
||||
batch_op.drop_column('ref_model')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,32 +0,0 @@
|
||||
"""Migration on Sat May 10 09:54:34 UTC 2025
|
||||
|
||||
Revision ID: 173ea45db189
|
||||
Revises: 3105abd795d4
|
||||
Create Date: 2025-05-10 09:54:39.380499
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '173ea45db189'
|
||||
down_revision = '3105abd795d4'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('transaction_offers', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('original_transaction', sa.String(length=50), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('transaction_offers', schema=None) as batch_op:
|
||||
batch_op.drop_column('original_transaction')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,53 +0,0 @@
|
||||
"""Migration on Thu Apr 24 17:42:25 UTC 2025
|
||||
|
||||
Revision ID: 1b2339f43824
|
||||
Revises: de9ad96ba34e
|
||||
Create Date: 2025-04-24 17:43:09.589626
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '1b2339f43824'
|
||||
down_revision = 'de9ad96ba34e'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('rac_checks',
|
||||
sa.Column('id', sa.String(), nullable=False),
|
||||
sa.Column('transaction_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('customer_id', sa.String(), nullable=False),
|
||||
sa.Column('account_id', sa.String(), nullable=False),
|
||||
sa.Column('rac_response', sa.JSON(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
with op.batch_alter_table('loan_charges', schema=None) as batch_op:
|
||||
batch_op.alter_column('amount',
|
||||
existing_type=sa.NUMERIC(precision=10, scale=2),
|
||||
type_=sa.Float(),
|
||||
existing_nullable=True)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('loans', schema=None) as batch_op:
|
||||
batch_op.drop_column('installment_amount')
|
||||
batch_op.drop_column('repayment_amount')
|
||||
batch_op.drop_column('upfront_fee')
|
||||
|
||||
with op.batch_alter_table('loan_charges', schema=None) as batch_op:
|
||||
batch_op.alter_column('amount',
|
||||
existing_type=sa.Float(),
|
||||
type_=sa.NUMERIC(precision=10, scale=2),
|
||||
existing_nullable=True)
|
||||
|
||||
op.drop_table('rac_checks')
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,32 +0,0 @@
|
||||
"""Migration on Wed Apr 16 18:35:18 UTC 2025
|
||||
|
||||
Revision ID: 287ecb02d3d7
|
||||
Revises: a4847b997191
|
||||
Create Date: 2025-04-16 18:36:04.632791
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '287ecb02d3d7'
|
||||
down_revision = 'a4847b997191'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('loan_charges', schema=None) as batch_op:
|
||||
batch_op.drop_column('transaction_id')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('loan_charges', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('transaction_id', sa.VARCHAR(length=50), autoincrement=False, nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,42 +0,0 @@
|
||||
"""Migration on Fri Apr 25 15:01:00 UTC 2025
|
||||
|
||||
Revision ID: 2a45dd99c9cb
|
||||
Revises: 2cf0c177ca02
|
||||
Create Date: 2025-04-25 15:01:51.129681
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '2a45dd99c9cb'
|
||||
down_revision = '2cf0c177ca02'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('loan_repayment_schedules', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('product_id', sa.String(length=20), nullable=True))
|
||||
batch_op.add_column(sa.Column('installment_amount', sa.Float(), nullable=True))
|
||||
batch_op.add_column(sa.Column('total_repayment_amount', sa.Float(), nullable=True))
|
||||
batch_op.drop_column('principal_amount')
|
||||
batch_op.drop_column('interest_amount')
|
||||
batch_op.drop_column('total_installment')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('loan_repayment_schedules', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('total_installment', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=True))
|
||||
batch_op.add_column(sa.Column('interest_amount', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=True))
|
||||
batch_op.add_column(sa.Column('principal_amount', sa.DOUBLE_PRECISION(precision=53), autoincrement=False, nullable=True))
|
||||
batch_op.drop_column('total_repayment_amount')
|
||||
batch_op.drop_column('installment_amount')
|
||||
batch_op.drop_column('product_id')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,41 +0,0 @@
|
||||
"""Migration on Fri Apr 25 14:02:01 UTC 2025
|
||||
|
||||
Revision ID: 2cf0c177ca02
|
||||
Revises: 1b2339f43824
|
||||
Create Date: 2025-04-25 14:02:42.244146
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '2cf0c177ca02'
|
||||
down_revision = '1b2339f43824'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('loan_repayment_schedules',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('loan_id', sa.Integer(), nullable=False),
|
||||
sa.Column('installment_number', sa.Integer(), nullable=False),
|
||||
sa.Column('due_date', sa.DateTime(), nullable=False),
|
||||
sa.Column('principal_amount', sa.Float(), nullable=True),
|
||||
sa.Column('interest_amount', sa.Float(), nullable=True),
|
||||
sa.Column('total_installment', sa.Float(), nullable=True),
|
||||
sa.Column('paid', sa.Boolean(), nullable=True),
|
||||
sa.Column('paid_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('loan_repayment_schedules')
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,250 +0,0 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 2eee4157505f
|
||||
Revises: 565bc3d0ba6e
|
||||
Create Date: 2025-05-16 13:24:41.914400
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '2eee4157505f'
|
||||
down_revision = '565bc3d0ba6e'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('accounts', schema=None) as batch_op:
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True)
|
||||
|
||||
with op.batch_alter_table('charges', schema=None) as batch_op:
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True)
|
||||
|
||||
with op.batch_alter_table('customers', schema=None) as batch_op:
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True)
|
||||
|
||||
with op.batch_alter_table('loan_charges', schema=None) as batch_op:
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True)
|
||||
|
||||
with op.batch_alter_table('loan_repayment_schedules', schema=None) as batch_op:
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True)
|
||||
|
||||
with op.batch_alter_table('loans', schema=None) as batch_op:
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True)
|
||||
|
||||
with op.batch_alter_table('offers', schema=None) as batch_op:
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True)
|
||||
|
||||
with op.batch_alter_table('rac_checks', schema=None) as batch_op:
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True)
|
||||
|
||||
with op.batch_alter_table('repayments', schema=None) as batch_op:
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True)
|
||||
|
||||
with op.batch_alter_table('transaction_offers', schema=None) as batch_op:
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True)
|
||||
|
||||
with op.batch_alter_table('transactions', schema=None) as batch_op:
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True,
|
||||
existing_server_default=sa.text('now()'))
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=postgresql.TIMESTAMP(),
|
||||
type_=sa.DateTime(timezone=True),
|
||||
existing_nullable=True,
|
||||
existing_server_default=sa.text('now()'))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('transactions', schema=None) as batch_op:
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True,
|
||||
existing_server_default=sa.text('now()'))
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True,
|
||||
existing_server_default=sa.text('now()'))
|
||||
|
||||
with op.batch_alter_table('transaction_offers', schema=None) as batch_op:
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True)
|
||||
|
||||
with op.batch_alter_table('repayments', schema=None) as batch_op:
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True)
|
||||
|
||||
with op.batch_alter_table('rac_checks', schema=None) as batch_op:
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True)
|
||||
|
||||
with op.batch_alter_table('offers', schema=None) as batch_op:
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True)
|
||||
|
||||
with op.batch_alter_table('loans', schema=None) as batch_op:
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True)
|
||||
|
||||
with op.batch_alter_table('loan_repayment_schedules', schema=None) as batch_op:
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True)
|
||||
|
||||
with op.batch_alter_table('loan_charges', schema=None) as batch_op:
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True)
|
||||
|
||||
with op.batch_alter_table('customers', schema=None) as batch_op:
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True)
|
||||
|
||||
with op.batch_alter_table('charges', schema=None) as batch_op:
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True)
|
||||
|
||||
with op.batch_alter_table('accounts', schema=None) as batch_op:
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=sa.DateTime(timezone=True),
|
||||
type_=postgresql.TIMESTAMP(),
|
||||
existing_nullable=True)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,52 +0,0 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 3105abd795d4
|
||||
Revises: 95a52be203c4
|
||||
Create Date: 2025-05-07 11:44:18.483694
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '3105abd795d4'
|
||||
down_revision = '95a52be203c4'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('rac_checks', schema=None) as batch_op:
|
||||
# Step 1: Drop the default value
|
||||
batch_op.alter_column('id',
|
||||
server_default=None,
|
||||
existing_type=sa.VARCHAR(),
|
||||
existing_nullable=False
|
||||
)
|
||||
|
||||
with op.batch_alter_table('rac_checks', schema=None) as batch_op:
|
||||
# Step 2: Change the column type
|
||||
batch_op.alter_column('id',
|
||||
existing_type=sa.VARCHAR(),
|
||||
type_=sa.Integer(),
|
||||
existing_nullable=False,
|
||||
autoincrement=True,
|
||||
postgresql_using='id::integer'
|
||||
)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('rac_checks', schema=None) as batch_op:
|
||||
batch_op.alter_column('id',
|
||||
existing_type=sa.Integer(),
|
||||
type_=sa.VARCHAR(),
|
||||
existing_nullable=False,
|
||||
autoincrement=True,
|
||||
existing_server_default=sa.text("''::character varying"))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,280 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 33e09efd85e3
|
||||
Revises:
|
||||
Create Date: 2025-07-03 14:07:14.424548
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import literal_column
|
||||
from sqlalchemy import text
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '33e09efd85e3'
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
sequences_and_triggers = [
|
||||
("transactions", "transactions_seq", "trg_transactions_id"),
|
||||
("transaction_offers", "transaction_offers_seq", "trg_transaction_offers_id"),
|
||||
("salaries", "salaries_seq", "trg_salaries_id"),
|
||||
("repayments_data", "repayments_data_seq", "trg_repayments_data_id"),
|
||||
("repayments", "repayments_seq", "trg_repayments_id"),
|
||||
("rac_checks", "rac_checks_seq", "trg_rac_checks_id"),
|
||||
("loans", "loans_seq", "trg_loans_id"),
|
||||
(
|
||||
"loan_repayment_schedules",
|
||||
"loan_repayment_schedules_seq",
|
||||
"trg_loan_repayment_schedules_id",
|
||||
),
|
||||
("loan_charges", "loan_charges_seq", "trg_loan_charges_id"),
|
||||
("charges", "charges_seq", "trg_charges_id"),
|
||||
]
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('accounts',
|
||||
sa.Column('id', sa.String(length=50), nullable=False),
|
||||
sa.Column('customer_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('account_type', sa.String(length=50), nullable=True),
|
||||
sa.Column('status', sa.String(length=20), nullable=True),
|
||||
sa.Column('lien_amount', sa.Float(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('charges',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('offer_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('code', sa.String(length=50), nullable=False),
|
||||
sa.Column('percent', sa.Float(), nullable=True),
|
||||
sa.Column('description', sa.Text(), nullable=True),
|
||||
sa.Column('due', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('customers',
|
||||
sa.Column('id', sa.String(length=50), nullable=False),
|
||||
sa.Column('msisdn', sa.String(length=20), nullable=False),
|
||||
sa.Column('country_code', sa.String(length=3), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('msisdn')
|
||||
)
|
||||
op.create_table('loan_charges',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('loan_id', sa.Integer(), nullable=False),
|
||||
sa.Column('transaction_id', sa.String(length=50), nullable=True),
|
||||
sa.Column('code', sa.String(length=50), nullable=False),
|
||||
sa.Column('amount', sa.Float(), nullable=True),
|
||||
sa.Column('percent', sa.Float(), nullable=True),
|
||||
sa.Column('description', sa.Text(), nullable=True),
|
||||
sa.Column('due', sa.Integer(), nullable=False),
|
||||
sa.Column('due_date', sa.DateTime(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('loan_repayment_schedules',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('loan_id', sa.Integer(), nullable=False),
|
||||
sa.Column('transaction_id', sa.String(length=50), nullable=True),
|
||||
sa.Column('product_id', sa.String(length=20), nullable=True),
|
||||
sa.Column('installment_number', sa.Integer(), nullable=False),
|
||||
sa.Column('due_date', sa.DateTime(), nullable=False),
|
||||
sa.Column('installment_amount', sa.Float(), nullable=True),
|
||||
sa.Column('total_repayment_amount', sa.Float(), nullable=True),
|
||||
sa.Column('paid', sa.Boolean(), nullable=True),
|
||||
sa.Column('paid_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('loans',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('customer_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('transaction_id', sa.String(length=50), nullable=True),
|
||||
sa.Column('original_transaction', sa.String(length=50), nullable=True),
|
||||
sa.Column('account_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('offer_id', sa.String(length=20), nullable=False),
|
||||
sa.Column('product_id', sa.String(length=20), nullable=True),
|
||||
sa.Column('collection_type', sa.String(length=20), nullable=True),
|
||||
sa.Column('current_loan_amount', sa.Float(), nullable=True),
|
||||
sa.Column('initial_loan_amount', sa.Float(), nullable=False),
|
||||
sa.Column('default_penalty_fee', sa.Float(), nullable=True),
|
||||
sa.Column('continuous_fee', sa.Float(), nullable=True),
|
||||
sa.Column('upfront_fee', sa.Float(), nullable=True),
|
||||
sa.Column('repayment_amount', sa.Float(), nullable=True),
|
||||
sa.Column('installment_amount', sa.Float(), nullable=True),
|
||||
sa.Column('status', sa.String(length=20), nullable=True),
|
||||
sa.Column('tenor', sa.Integer(), nullable=True),
|
||||
sa.Column('due_date', sa.DateTime(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.Column('eligible_amount', sa.Float(), nullable=True),
|
||||
sa.Column('disburse_date', sa.DateTime(), nullable=True),
|
||||
sa.Column('disburse_verify', sa.DateTime(), nullable=True),
|
||||
sa.Column('reference', sa.String(length=50), nullable=True),
|
||||
sa.Column('disburse_result', sa.String(length=10), nullable=True),
|
||||
sa.Column('disburse_description', sa.String(length=100), nullable=True),
|
||||
sa.Column('verify_result', sa.String(length=10), nullable=True),
|
||||
sa.Column('verify_description', sa.String(length=100), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('offers',
|
||||
sa.Column('id', sa.String(length=50), nullable=False),
|
||||
sa.Column('product_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('min_amount', sa.Float(), nullable=False),
|
||||
sa.Column('max_amount', sa.Float(), nullable=False),
|
||||
sa.Column('tenor', sa.Integer(), nullable=False),
|
||||
sa.Column('schedule', sa.Integer(), nullable=True),
|
||||
sa.Column('interest_rate', sa.Float(), nullable=True),
|
||||
sa.Column('management_rate', sa.Float(), nullable=True),
|
||||
sa.Column('insurance_rate', sa.Float(), nullable=True),
|
||||
sa.Column('vat_rate', sa.Float(), nullable=True),
|
||||
sa.Column('list_order', sa.Integer(), nullable=True),
|
||||
sa.Column('max_daily_loans', sa.Integer(), nullable=True),
|
||||
sa.Column('max_active_loans', sa.Integer(), nullable=True),
|
||||
sa.Column('max_life_loans', sa.Integer(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('rac_checks',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('transaction_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('customer_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('account_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('rac_response', sa.Text(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('repayments',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('loan_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('customer_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('product_id', sa.String(length=20), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.Column('transaction_id', sa.String(length=50), nullable=True),
|
||||
sa.Column('repay_date', sa.DateTime(), nullable=True),
|
||||
sa.Column('repay_result', sa.String(length=10), nullable=True),
|
||||
sa.Column('repay_description', sa.String(length=100), nullable=True),
|
||||
sa.Column('verify_date', sa.DateTime(), nullable=True),
|
||||
sa.Column('verify_result', sa.String(length=10), nullable=True),
|
||||
sa.Column('verify_description', sa.String(length=100), nullable=True),
|
||||
sa.Column('initiated_by', sa.String(length=50), nullable=True),
|
||||
sa.Column('salary_amount', sa.Float(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
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('fbn_transaction_id', sa.String(length=50), nullable=True),
|
||||
sa.Column('customer_id', sa.String(length=50), nullable=True),
|
||||
sa.Column('account_id', sa.String(length=50), nullable=True),
|
||||
sa.Column('repayment_amount', sa.Float(), nullable=True),
|
||||
sa.Column('amount_collected', sa.Float(), nullable=True),
|
||||
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.Column('balance', sa.Float(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('salaries',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('customer_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('account_id', sa.String(length=50), nullable=True),
|
||||
sa.Column('status', sa.String(length=20), nullable=True),
|
||||
sa.Column('amount', sa.Float(), nullable=False),
|
||||
sa.Column('salary_date', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('transaction_offers',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('customer_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('transaction_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('original_transaction', sa.String(length=50), nullable=True),
|
||||
sa.Column('offer_id', sa.String(length=20), nullable=False),
|
||||
sa.Column('product_id', sa.String(length=20), nullable=True),
|
||||
sa.Column('min_amount', sa.Float(), nullable=False),
|
||||
sa.Column('max_amount', sa.Float(), nullable=False),
|
||||
sa.Column('eligible_amount', sa.Float(), nullable=True),
|
||||
sa.Column('tenor', sa.Integer(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('transactions',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('transaction_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('account_id', sa.String(length=50), nullable=True),
|
||||
sa.Column('customer_id', sa.String(length=50), nullable=True),
|
||||
sa.Column('type', sa.String(length=50), nullable=False),
|
||||
sa.Column('channel', sa.String(length=50), nullable=False),
|
||||
sa.Column('phone_number', sa.String(length=50), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
|
||||
for table, seq, trg in sequences_and_triggers:
|
||||
op.execute(
|
||||
text(
|
||||
f"""
|
||||
BEGIN
|
||||
EXECUTE IMMEDIATE 'CREATE SEQUENCE {seq} START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE';
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
IF SQLCODE != -955 THEN RAISE; END IF;
|
||||
END;
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
op.execute(
|
||||
text(
|
||||
f"""
|
||||
CREATE OR REPLACE TRIGGER {trg}
|
||||
BEFORE INSERT ON {table}
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
IF ||':'||'NEW.id IS NULL THEN
|
||||
SELECT {seq}.NEXTVAL INTO '||':'||'NEW.id FROM dual;
|
||||
END IF;
|
||||
END;
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('transactions')
|
||||
op.drop_table('transaction_offers')
|
||||
op.drop_table('salaries')
|
||||
op.drop_table('repayments_data')
|
||||
op.drop_table('repayments')
|
||||
op.drop_table('rac_checks')
|
||||
op.drop_table('offers')
|
||||
op.drop_table('loans')
|
||||
op.drop_table('loan_repayment_schedules')
|
||||
op.drop_table('loan_charges')
|
||||
op.drop_table('customers')
|
||||
op.drop_table('charges')
|
||||
op.drop_table('accounts')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
for table, seq, trg in sequences_and_triggers:
|
||||
op.execute(text(f"DROP TRIGGER {trg}"))
|
||||
op.execute(text(f"DROP SEQUENCE {seq}"))
|
||||
@@ -1,32 +0,0 @@
|
||||
"""Migration for mloan table
|
||||
|
||||
Revision ID: 38acee611d55
|
||||
Revises: f1e83a993034
|
||||
Create Date: 2025-04-30 09:55:30.552838
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '38acee611d55'
|
||||
down_revision = 'f1e83a993034'
|
||||
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('tenor', sa.Integer(), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('loans', schema=None) as batch_op:
|
||||
batch_op.drop_column('tenor')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,54 +0,0 @@
|
||||
"""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 ###
|
||||
@@ -1,34 +0,0 @@
|
||||
"""Migration on Sat May 10 12:54:52 UTC 2025
|
||||
|
||||
Revision ID: 565bc3d0ba6e
|
||||
Revises: 173ea45db189
|
||||
Create Date: 2025-05-10 12:54:56.683215
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '565bc3d0ba6e'
|
||||
down_revision = '173ea45db189'
|
||||
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_date', sa.DateTime(), nullable=True))
|
||||
batch_op.add_column(sa.Column('disburse_verify', sa.DateTime(), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('loans', schema=None) as batch_op:
|
||||
batch_op.drop_column('disburse_verify')
|
||||
batch_op.drop_column('disburse_date')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,32 +0,0 @@
|
||||
"""Migration on Fri Apr 11 14:15:19 UTC 2025
|
||||
|
||||
Revision ID: 610b7e9d15a6
|
||||
Revises: 9bb0367eb486
|
||||
Create Date: 2025-04-11 14:16:12.533227
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '610b7e9d15a6'
|
||||
down_revision = '9bb0367eb486'
|
||||
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('transaction_id', sa.String(length=50), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('loans', schema=None) as batch_op:
|
||||
batch_op.drop_column('transaction_id')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,32 +0,0 @@
|
||||
"""Migration on Mon Apr 14 15:15:05 UTC 2025
|
||||
|
||||
Revision ID: 783a023a477f
|
||||
Revises: f6cd1bfc8832
|
||||
Create Date: 2025-04-14 15:15:36.991148
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '783a023a477f'
|
||||
down_revision = 'f6cd1bfc8832'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('transactions', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('customer_id', sa.String(length=50), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('transactions', schema=None) as batch_op:
|
||||
batch_op.drop_column('customer_id')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,41 +0,0 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 86e701febdda
|
||||
Revises: eb99c7fb9e09
|
||||
Create Date: 2025-04-29 07:59:33.305967
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '86e701febdda'
|
||||
down_revision = 'eb99c7fb9e09'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('transaction_offers',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('customer_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('transaction_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('offer_id', sa.String(length=20), nullable=False),
|
||||
sa.Column('product_id', sa.String(length=20), nullable=True),
|
||||
sa.Column('min_amount', sa.Float(), nullable=False),
|
||||
sa.Column('max_amount', sa.Float(), nullable=False),
|
||||
sa.Column('eligible_amount', sa.Float(), nullable=True),
|
||||
sa.Column('tenor', sa.Integer(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('transaction_offers')
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,38 +0,0 @@
|
||||
"""Migration on Sat Apr 26 12:50:46 UTC 2025
|
||||
|
||||
Revision ID: 89759cebb9c6
|
||||
Revises: 2a45dd99c9cb
|
||||
Create Date: 2025-04-26 12:50:49.771355
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '89759cebb9c6'
|
||||
down_revision = '2a45dd99c9cb'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('offers', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('interest_rate', sa.Float(), nullable=True))
|
||||
batch_op.add_column(sa.Column('management_rate', sa.Float(), nullable=True))
|
||||
batch_op.add_column(sa.Column('insurance_rate', sa.Float(), nullable=True))
|
||||
batch_op.add_column(sa.Column('vat_rate', sa.Float(), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('offers', schema=None) as batch_op:
|
||||
batch_op.drop_column('vat_rate')
|
||||
batch_op.drop_column('insurance_rate')
|
||||
batch_op.drop_column('management_rate')
|
||||
batch_op.drop_column('interest_rate')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,32 +0,0 @@
|
||||
"""Migration on Sat May 3 21:53:29 UTC 2025
|
||||
|
||||
Revision ID: 95a52be203c4
|
||||
Revises: 38acee611d55
|
||||
Create Date: 2025-05-03 21:53:32.154029
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '95a52be203c4'
|
||||
down_revision = '38acee611d55'
|
||||
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('eligible_amount', sa.Float(), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('loans', schema=None) as batch_op:
|
||||
batch_op.drop_column('eligible_amount')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,40 +0,0 @@
|
||||
"""Migration on Fri Apr 11 12:48:01 UTC 2025
|
||||
|
||||
Revision ID: 9bb0367eb486
|
||||
Revises: fd447d78b161
|
||||
Create Date: 2025-04-11 12:48:36.145311
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '9bb0367eb486'
|
||||
down_revision = 'fd447d78b161'
|
||||
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('product_id', sa.String(length=20), nullable=True))
|
||||
batch_op.add_column(sa.Column('current_loan_amount', sa.Float(), nullable=True))
|
||||
batch_op.add_column(sa.Column('default_penalty_fee', sa.Float(), nullable=True))
|
||||
batch_op.add_column(sa.Column('continuous_fee', sa.Float(), nullable=True))
|
||||
batch_op.add_column(sa.Column('due_date', sa.DateTime(), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('loans', schema=None) as batch_op:
|
||||
batch_op.drop_column('due_date')
|
||||
batch_op.drop_column('continuous_fee')
|
||||
batch_op.drop_column('default_penalty_fee')
|
||||
batch_op.drop_column('current_loan_amount')
|
||||
batch_op.drop_column('product_id')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,57 +0,0 @@
|
||||
"""Migration on Wed Apr 16 17:42:49 UTC 2025
|
||||
|
||||
Revision ID: a4847b997191
|
||||
Revises: 783a023a477f
|
||||
Create Date: 2025-04-16 17:43:22.509659
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'a4847b997191'
|
||||
down_revision = '783a023a477f'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('loan_charges',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('loan_id', sa.Integer(), nullable=False),
|
||||
sa.Column('transaction_id', sa.String(length=50), nullable=True),
|
||||
sa.Column('code', sa.String(length=50), nullable=False),
|
||||
sa.Column('amount', sa.Float(), nullable=True),
|
||||
sa.Column('percent', sa.Float(), nullable=True),
|
||||
sa.Column('description', sa.Text(), nullable=True),
|
||||
sa.Column('due', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('offers',
|
||||
sa.Column('id', sa.String(), nullable=False),
|
||||
sa.Column('product_id', sa.String(), nullable=False),
|
||||
sa.Column('min_amount', sa.Float(), nullable=False),
|
||||
sa.Column('max_amount', sa.Float(), nullable=False),
|
||||
sa.Column('tenor', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
with op.batch_alter_table('loans', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('product_id', sa.String(length=20), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('loans', schema=None) as batch_op:
|
||||
batch_op.drop_column('product_id')
|
||||
|
||||
op.drop_table('offers')
|
||||
op.drop_table('loan_charges')
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,32 +0,0 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: b3a5e10bc77e
|
||||
Revises: e8dd9b841ad7
|
||||
Create Date: 2025-05-27 01:52:48.538333
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'b3a5e10bc77e'
|
||||
down_revision = 'e8dd9b841ad7'
|
||||
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('reference', sa.String(length=50), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('loans', schema=None) as batch_op:
|
||||
batch_op.drop_column('reference')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,86 +0,0 @@
|
||||
"""Migration on Thu Apr 10 16:21:45 UTC 2025
|
||||
|
||||
Revision ID: b8f6fd76ead8
|
||||
Revises:
|
||||
Create Date: 2025-04-10 16:22:15.946157
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'b8f6fd76ead8'
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('repayments',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('loan_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('customer_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('product_id', sa.String(length=20), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
with op.batch_alter_table('loans', schema=None) as batch_op:
|
||||
batch_op.alter_column('id',
|
||||
existing_type=sa.VARCHAR(length=50),
|
||||
type_=sa.Integer(),
|
||||
existing_nullable=False,
|
||||
autoincrement=True,
|
||||
existing_server_default=sa.text("nextval('loan_id_seq'::regclass)"))
|
||||
|
||||
with op.batch_alter_table('transactions', schema=None) as batch_op:
|
||||
batch_op.alter_column('channel',
|
||||
existing_type=sa.VARCHAR(length=8),
|
||||
type_=sa.String(length=50),
|
||||
existing_nullable=False)
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=postgresql.TIMESTAMP(timezone=True),
|
||||
type_=sa.DateTime(),
|
||||
existing_nullable=True,
|
||||
existing_server_default=sa.text('now()'))
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=postgresql.TIMESTAMP(timezone=True),
|
||||
type_=sa.DateTime(),
|
||||
existing_nullable=True,
|
||||
existing_server_default=sa.text('now()'))
|
||||
batch_op.drop_constraint('transactions_id_key', type_='unique')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('transactions', schema=None) as batch_op:
|
||||
batch_op.create_unique_constraint('transactions_id_key', ['id'])
|
||||
batch_op.alter_column('updated_at',
|
||||
existing_type=sa.DateTime(),
|
||||
type_=postgresql.TIMESTAMP(timezone=True),
|
||||
existing_nullable=True,
|
||||
existing_server_default=sa.text('now()'))
|
||||
batch_op.alter_column('created_at',
|
||||
existing_type=sa.DateTime(),
|
||||
type_=postgresql.TIMESTAMP(timezone=True),
|
||||
existing_nullable=True,
|
||||
existing_server_default=sa.text('now()'))
|
||||
batch_op.alter_column('channel',
|
||||
existing_type=sa.String(length=50),
|
||||
type_=sa.VARCHAR(length=8),
|
||||
existing_nullable=False)
|
||||
|
||||
with op.batch_alter_table('loans', schema=None) as batch_op:
|
||||
batch_op.alter_column('id',
|
||||
existing_type=sa.Integer(),
|
||||
type_=sa.VARCHAR(length=50),
|
||||
existing_nullable=False,
|
||||
autoincrement=True,
|
||||
existing_server_default=sa.text("nextval('loan_id_seq'::regclass)"))
|
||||
|
||||
op.drop_table('repayments')
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,38 +0,0 @@
|
||||
"""Migration on Thu Apr 17 14:15:36 UTC 2025
|
||||
|
||||
Revision ID: de9ad96ba34e
|
||||
Revises: ec8d97f9b584
|
||||
Create Date: 2025-04-17 14:16:16.537466
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'de9ad96ba34e'
|
||||
down_revision = 'ec8d97f9b584'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('charges',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('offer_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('code', sa.String(length=50), nullable=False),
|
||||
sa.Column('percent', sa.Float(), nullable=True),
|
||||
sa.Column('description', sa.Text(), nullable=True),
|
||||
sa.Column('due', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('charges')
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,36 +0,0 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: e8dd9b841ad7
|
||||
Revises: 2eee4157505f
|
||||
Create Date: 2025-05-19 11:46:19.204637
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'e8dd9b841ad7'
|
||||
down_revision = '2eee4157505f'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('offers', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('max_daily_loans', sa.Integer(), nullable=True))
|
||||
batch_op.add_column(sa.Column('max_active_loans', sa.Integer(), nullable=True))
|
||||
batch_op.add_column(sa.Column('max_life_loans', sa.Integer(), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('offers', schema=None) as batch_op:
|
||||
batch_op.drop_column('max_life_loans')
|
||||
batch_op.drop_column('max_active_loans')
|
||||
batch_op.drop_column('max_daily_loans')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,32 +0,0 @@
|
||||
"""Migration on Sat Apr 26 19:02:17 UTC 2025
|
||||
|
||||
Revision ID: eb99c7fb9e09
|
||||
Revises: 89759cebb9c6
|
||||
Create Date: 2025-04-26 19:02:20.443678
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'eb99c7fb9e09'
|
||||
down_revision = '89759cebb9c6'
|
||||
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('original_transaction', sa.String(length=50), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('loans', schema=None) as batch_op:
|
||||
batch_op.drop_column('original_transaction')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,34 +0,0 @@
|
||||
"""Migration on Thu Apr 17 10:40:05 UTC 2025
|
||||
|
||||
Revision ID: ec8d97f9b584
|
||||
Revises: 287ecb02d3d7
|
||||
Create Date: 2025-04-17 10:40:34.751272
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'ec8d97f9b584'
|
||||
down_revision = '287ecb02d3d7'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('loan_charges', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('transaction_id', sa.String(length=50), nullable=True))
|
||||
batch_op.add_column(sa.Column('due_date', sa.DateTime(), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('loan_charges', schema=None) as batch_op:
|
||||
batch_op.drop_column('due_date')
|
||||
batch_op.drop_column('transaction_id')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,32 +0,0 @@
|
||||
"""Migration on Tue Apr 29 20:43:35 UTC 2025
|
||||
|
||||
Revision ID: f1e83a993034
|
||||
Revises: 86e701febdda
|
||||
Create Date: 2025-04-29 20:43:38.595543
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'f1e83a993034'
|
||||
down_revision = '86e701febdda'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('loan_repayment_schedules', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('transaction_id', sa.String(length=50), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('loan_repayment_schedules', schema=None) as batch_op:
|
||||
batch_op.drop_column('transaction_id')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,34 +0,0 @@
|
||||
"""Migration on Fri Apr 11 14:34:36 UTC 2025
|
||||
|
||||
Revision ID: f6cd1bfc8832
|
||||
Revises: 610b7e9d15a6
|
||||
Create Date: 2025-04-11 14:35:07.093967
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'f6cd1bfc8832'
|
||||
down_revision = '610b7e9d15a6'
|
||||
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('collection_type', sa.String(length=20), nullable=True))
|
||||
batch_op.drop_column('product_id')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('loans', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('product_id', sa.VARCHAR(length=20), autoincrement=False, nullable=True))
|
||||
batch_op.drop_column('collection_type')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -1,38 +0,0 @@
|
||||
"""Migration on Fri Apr 11 12:02:45 UTC 2025
|
||||
|
||||
Revision ID: fd447d78b161
|
||||
Revises: 1340e7e578b9
|
||||
Create Date: 2025-04-11 12:03:28.346671
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'fd447d78b161'
|
||||
down_revision = '1340e7e578b9'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('transactions', schema=None) as batch_op:
|
||||
batch_op.alter_column('account_id',
|
||||
existing_type=sa.VARCHAR(length=50),
|
||||
nullable=True)
|
||||
batch_op.drop_column('ref_model')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('transactions', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('ref_model', sa.VARCHAR(length=50), autoincrement=False, nullable=True))
|
||||
batch_op.alter_column('account_id',
|
||||
existing_type=sa.VARCHAR(length=50),
|
||||
nullable=False)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@@ -6,6 +6,7 @@ flask-sqlalchemy
|
||||
flask-migrate
|
||||
psycopg2-binary
|
||||
alembic
|
||||
oracledb
|
||||
|
||||
# Schema for validations
|
||||
Flask-Marshmallow==0.15.0
|
||||
|
||||
Reference in New Issue
Block a user