diff --git a/app/api/services/base_service.py b/app/api/services/base_service.py index 8616fd8..4382181 100644 --- a/app/api/services/base_service.py +++ b/app/api/services/base_service.py @@ -50,8 +50,7 @@ class BaseService: """ return Transaction.create_transaction( transaction_id = validated_data.get("transactionId"), - ref_id = validated_data.get("refId") or validated_data.get("accountId"), - ref_model = validated_data.get("refModel", "account"), + account_id = validated_data.get("accountId", None), type = cls.TRANSACTION_TYPE, channel = validated_data.get("channel"), ) diff --git a/app/api/services/loan_status.py b/app/api/services/loan_status.py index f80b7f2..6a2c01a 100644 --- a/app/api/services/loan_status.py +++ b/app/api/services/loan_status.py @@ -1,5 +1,6 @@ from flask import request, jsonify from marshmallow import ValidationError +from app.api.enums.loan_status import LoanStatus from app.models import Customer from app.utils.logger import logger from app.api.schemas.loan_status import LoanStatusSchema @@ -33,12 +34,7 @@ class LoanStatusService(BaseService): transactionId = validated_data.get('transactionId') # Get loans - loans = [loan.to_dict() for loan in customer.loans] - - - validated_data['refId'] = customer.id - validated_data['refModel'] = "customer" - + loans = [loan.to_dict() for loan in customer.loans if loan.status == LoanStatus.ACTIVE] transaction = LoanStatusService.log_transaction(validated_data = validated_data) @@ -62,12 +58,17 @@ class LoanStatusService(BaseService): # } # ] + total_debt_amount = sum( + loan.get("currentLoanAmount") or 0 + for loan in loans + ) + # Simulated processing logic response_data = { "customerId": customer_id, "transactionId": transactionId, "loans": loans, - "totalDebtAmount": 8500, + "totalDebtAmount": total_debt_amount, "resultCode": "00", "resultDescription": "Successful" } diff --git a/app/api/services/provide_loan.py b/app/api/services/provide_loan.py index b496265..15b3607 100644 --- a/app/api/services/provide_loan.py +++ b/app/api/services/provide_loan.py @@ -31,6 +31,7 @@ class ProvideLoanService(BaseService): account_id = validated_data.get('accountId') customer_id = validated_data.get('customerId') request_id = validated_data.get('requestId') + collection_type = validated_data.get('collectionType') transaction_id = validated_data.get('transactionId') if (ProvideLoanService.validate_account_ownership(account_id = account_id, customer_id = customer_id)): @@ -38,11 +39,13 @@ class ProvideLoanService(BaseService): # Save the loan details loan = Loan.create_loan( - customer_id=customer_id, - account_id=account_id, - offer_id=validated_data.get('offerId'), - principal_amount=validated_data.get('requestedAmount'), - status=LoanStatus.ACTIVE + customer_id = customer_id, + account_id = account_id, + offer_id = validated_data.get('offerId'), + collection_type = collection_type, + transaction_id = validated_data.get('transactionId'), + initial_loan_amount = validated_data.get('requestedAmount'), + status= LoanStatus.ACTIVE ) if not loan: @@ -51,10 +54,6 @@ class ProvideLoanService(BaseService): "message": "Failed to save loan details." }), 400 - db.session.flush() - validated_data['refId'] = loan.id - validated_data['refModel'] = "loan" - # Log Transaction transaction = ProvideLoanService.log_transaction(validated_data = validated_data) diff --git a/app/api/services/repayment.py b/app/api/services/repayment.py index 33be586..4d400db 100644 --- a/app/api/services/repayment.py +++ b/app/api/services/repayment.py @@ -47,11 +47,7 @@ class RepaymentService(BaseService): return jsonify({ "message": "Failed to save repayment details." }), 400 - - db.session.flush() - - validated_data['refId'] = repayment.id - validated_data['refModel'] = "repayment" + #Update Loan status Loan.update_status(loan_id = loan_id, status = LoanStatus.REPAID) diff --git a/app/config.py b/app/config.py index b23548a..4a3e597 100644 --- a/app/config.py +++ b/app/config.py @@ -32,7 +32,6 @@ class Config: ) KAFKA_BROKER = 'dev-events.simbrellang.net:9085' - KAFKA_PAYMENT_TOPIC = 'PROCESS_PAYMENT' settings = Config() diff --git a/app/models/loan.py b/app/models/loan.py index 33256e4..ab8e89f 100644 --- a/app/models/loan.py +++ b/app/models/loan.py @@ -16,10 +16,16 @@ class Loan(db.Model): autoincrement=True, ) customer_id = db.Column(db.String(50), nullable=False) + transaction_id = db.Column(db.String(50), nullable=True) account_id = db.Column(db.String(50), nullable=False) offer_id = db.Column(db.String(20), nullable=False) - principal_amount = db.Column(db.Float, nullable=False) + collection_type = db.Column(db.String(20), nullable=True) + current_loan_amount = db.Column(db.Float, nullable=True) + initial_loan_amount = db.Column(db.Float, nullable=False) + default_penalty_fee = db.Column(db.Float, default=0) + continuous_fee = db.Column(db.Float, default=0) status = db.Column(db.String(20), default='pending') + due_date = db.Column(db.DateTime, nullable=True) created_at = db.Column(db.DateTime, default=datetime.now(timezone.utc)) updated_at = db.Column(db.DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc)) @@ -31,26 +37,26 @@ class Loan(db.Model): ) @classmethod - def create_loan(cls, customer_id, account_id, offer_id, principal_amount, status='pending'): + def create_loan(cls, customer_id, account_id, offer_id, initial_loan_amount, collection_type, transaction_id, status='pending'): # Check if customer exists is_valid = Customer.is_valid_customer(customer_id) if not is_valid: raise ValueError("Customer does not exist") - - # # Check for active loans - # has_active_loans = cls.has_active_loans(customer_id) - # if has_active_loans: - # raise ValueError("Customer has active loans") + now = datetime.now(timezone.utc) # Create and save the loan loan = cls( - customer_id=customer_id, - account_id=account_id, - offer_id=offer_id, - principal_amount=principal_amount, - status=status + customer_id = customer_id, + account_id = account_id, + offer_id = offer_id, + collection_type = collection_type, + transaction_id = transaction_id, + initial_loan_amount = initial_loan_amount, + current_loan_amount = initial_loan_amount, + due_date=now, + status = status ) try: @@ -104,14 +110,15 @@ class Loan(db.Model): Convert the Loan object to a dictionary format for JSON serialization. """ return { - 'id': self.id, - 'customer_id': self.customer_id, - 'account_id': self.account_id, - 'offer_id': self.offer_id, - 'principal_amount': self.principal_amount, + 'debtId': self.id, + 'initialLoanAmount': self.initial_loan_amount, + 'currentLoanAmount': self.current_loan_amount, + 'defaultPenaltyFee': self.default_penalty_fee, + 'continuousFee': self.continuous_fee, + 'collectionType': self.collection_type, 'status': self.status, - 'created_at': self.created_at.isoformat() if self.created_at else None, - 'updated_at': self.updated_at.isoformat() if self.updated_at else None + 'dueDate': self.due_date.isoformat() if self.due_date else None, + 'loanDate': self.created_at.isoformat() if self.created_at else None, } def __repr__(self): diff --git a/app/models/transaction.py b/app/models/transaction.py index 1775d2c..9b18479 100644 --- a/app/models/transaction.py +++ b/app/models/transaction.py @@ -1,5 +1,6 @@ from datetime import datetime, timezone from app.extensions import db +from app.models import account from sqlalchemy.exc import IntegrityError from sqlalchemy import and_, or_, not_ @@ -11,8 +12,7 @@ class Transaction(db.Model): autoincrement=True, ) transaction_id = db.Column(db.String(50), nullable=False) - ref_id = db.Column(db.String(50), nullable=False) - ref_model = db.Column(db.String(50), nullable=True, default='account') + account_id = db.Column(db.String(50), nullable=True) type = db.Column(db.String(50), nullable=False) channel = db.Column(db.String(50), nullable=False) created_at = db.Column(db.DateTime, default=datetime.now(timezone.utc)) @@ -22,7 +22,7 @@ class Transaction(db.Model): return f'' @classmethod - def create_transaction(cls, transaction_id, ref_id, ref_model, type, channel): + def create_transaction(cls, transaction_id, account_id, type, channel): # if cls.query.filter_by(transaction_id=transaction_id).first(): # raise ValueError("Duplicate Transaction") @@ -34,8 +34,7 @@ class Transaction(db.Model): transaction = cls( transaction_id = transaction_id, - ref_id = ref_id, - ref_model = ref_model, + account_id = account_id, type = type, channel = channel ) diff --git a/migrations/versions/610b7e9d15a6_migration_on_fri_apr_11_14_15_19_utc_.py b/migrations/versions/610b7e9d15a6_migration_on_fri_apr_11_14_15_19_utc_.py new file mode 100644 index 0000000..ab24390 --- /dev/null +++ b/migrations/versions/610b7e9d15a6_migration_on_fri_apr_11_14_15_19_utc_.py @@ -0,0 +1,32 @@ +"""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 ### diff --git a/migrations/versions/9bb0367eb486_migration_on_fri_apr_11_12_48_01_utc_.py b/migrations/versions/9bb0367eb486_migration_on_fri_apr_11_12_48_01_utc_.py new file mode 100644 index 0000000..be5e457 --- /dev/null +++ b/migrations/versions/9bb0367eb486_migration_on_fri_apr_11_12_48_01_utc_.py @@ -0,0 +1,40 @@ +"""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 ### diff --git a/migrations/versions/f6cd1bfc8832_migration_on_fri_apr_11_14_34_36_utc_.py b/migrations/versions/f6cd1bfc8832_migration_on_fri_apr_11_14_34_36_utc_.py new file mode 100644 index 0000000..498ede0 --- /dev/null +++ b/migrations/versions/f6cd1bfc8832_migration_on_fri_apr_11_14_34_36_utc_.py @@ -0,0 +1,34 @@ +"""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 ### diff --git a/migrations/versions/fd447d78b161_migration_on_fri_apr_11_12_02_45_utc_.py b/migrations/versions/fd447d78b161_migration_on_fri_apr_11_12_02_45_utc_.py new file mode 100644 index 0000000..eff663e --- /dev/null +++ b/migrations/versions/fd447d78b161_migration_on_fri_apr_11_12_02_45_utc_.py @@ -0,0 +1,38 @@ +"""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 ###