diff --git a/app/api/services/base_service.py b/app/api/services/base_service.py index aa678e0..8616fd8 100644 --- a/app/api/services/base_service.py +++ b/app/api/services/base_service.py @@ -49,10 +49,11 @@ class BaseService: Create a new transaction. """ return Transaction.create_transaction( - transaction_id =validated_data.get("transactionId"), - account_id=validated_data.get("accountId"), - type=cls.TRANSACTION_TYPE, - channel=validated_data.get("channel"), + transaction_id = validated_data.get("transactionId"), + ref_id = validated_data.get("refId") or validated_data.get("accountId"), + ref_model = validated_data.get("refModel", "account"), + type = cls.TRANSACTION_TYPE, + channel = validated_data.get("channel"), ) @classmethod diff --git a/app/api/services/customer_consent.py b/app/api/services/customer_consent.py index 2d1048a..a6b4f07 100644 --- a/app/api/services/customer_consent.py +++ b/app/api/services/customer_consent.py @@ -4,7 +4,8 @@ from marshmallow import ValidationError from app.utils.logger import logger from app.api.schemas.customer_consent import CustomerConsentSchema from app.api.services.base_service import BaseService -from app.api.enums import TransactionType +from app.api.enums import TransactionType +from app.extensions import db class CustomerConsentService(BaseService): @@ -22,36 +23,39 @@ class CustomerConsentService(BaseService): dict: A standardized response. """ try: + with db.session.begin(): + validated_data = CustomerConsentService.validate_data(data, CustomerConsentSchema()) + account_id = validated_data.get('accountId') + customer_id = validated_data.get('customerId') - validated_data = CustomerConsentService.validate_data(data, CustomerConsentSchema()) - account_id = validated_data.get('accountId') - customer_id = validated_data.get('customerId') + if(CustomerConsentService.validate_account_ownership(account_id = account_id, customer_id = customer_id)): + + transaction = CustomerConsentService.log_transaction(validated_data = validated_data) - if(CustomerConsentService.validate_account_ownership(account_id = account_id, customer_id = customer_id)): - transaction = CustomerConsentService.log_transaction(validated_data = validated_data) - - if not transaction: - logger.error(f"Failed to log transaction") + if not transaction: + logger.error(f"Failed to log transaction") + return jsonify({ + "message": "Failed to log transaction." + }), 400 + else: return jsonify({ - "message": "Failed to log transaction." - }), 400 - else: - return jsonify({ - "message": "Invalid Customer or Account" - }), 400 + "message": "Invalid Customer or Account" + }), 400 - - # Simulated processing logic - response_data = { - "resultCode": "00", - "resultDescription": "Request is received" - } + + # Simulated processing logic + response_data = { + "resultCode": "00", + "resultDescription": "Request is received" + } - return response_data - + db.session.commit() + return response_data + except ValidationError as err: logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}") + db.session.rollback() return jsonify({ "message": "Validation exception" @@ -59,6 +63,7 @@ class CustomerConsentService(BaseService): except ValueError as err: logger.error(f"{getattr(err, 'messages', str(err))}") + db.session.rollback() return jsonify({ "message": str(err) @@ -66,6 +71,7 @@ class CustomerConsentService(BaseService): except Exception as e: logger.error(f"An error occurred: {str(e)}", exc_info=True) + db.session.rollback() return jsonify({ "message": "Internal Server Error" }) , 500 \ No newline at end of file diff --git a/app/api/services/eligibility_check.py b/app/api/services/eligibility_check.py index d7dff72..256ddf3 100644 --- a/app/api/services/eligibility_check.py +++ b/app/api/services/eligibility_check.py @@ -5,6 +5,7 @@ from app.api.schemas.eligibility_check import EligibilityCheckSchema from marshmallow import ValidationError from app.api.enums import TransactionType from app.api.integrations import SimbrellaIntegration +from app.extensions import db class EligibilityCheckService(BaseService): TRANSACTION_TYPE = TransactionType.ELIGIBILITY_CHECK @@ -21,71 +22,75 @@ class EligibilityCheckService(BaseService): dict: A standardized response. """ try: + with db.session.begin(): - validated_data = EligibilityCheckService.validate_data(data, EligibilityCheckSchema()) - account_id = validated_data.get('accountId') - customer_id = validated_data.get('customerId') - transactionId = validated_data.get('transactionId') - msisdn = validated_data.get('msisdn') + validated_data = EligibilityCheckService.validate_data(data, EligibilityCheckSchema()) + account_id = validated_data.get('accountId') + customer_id = validated_data.get('customerId') + transactionId = validated_data.get('transactionId') + msisdn = validated_data.get('msisdn') - customer = EligibilityCheckService.get_or_create_customer(validated_data = validated_data) + customer = EligibilityCheckService.get_or_create_customer(validated_data = validated_data) - if (EligibilityCheckService.validate_account_ownership(account_id = account_id, customer_id = customer_id)): - transaction = EligibilityCheckService.log_transaction(validated_data = validated_data) + if (EligibilityCheckService.validate_account_ownership(account_id = account_id, customer_id = customer_id)): + + transaction = EligibilityCheckService.log_transaction(validated_data = validated_data) - if not transaction: - logger.error(f"Failed to log transaction") + if not transaction: + logger.error(f"Failed to log transaction") + return jsonify({ + "message": "Failed to log transaction." + }), 400 + + else: return jsonify({ - "message": "Failed to log transaction." - }), 400 - else: - return jsonify({ - "message": "Invalid Customer or Account" - }), 400 - - # Call RACCheck - response = SimbrellaIntegration.rac_check( - customer_id = customer_id, - account_id = account_id, - transaction_id = transaction.id, - ) - logger.error(f"This is Response Returned ****** : {str(response)}") + "message": "Invalid Customer or Account" + }), 400 + + # Call RACCheck + response = SimbrellaIntegration.rac_check( + customer_id = customer_id, + account_id = account_id, + transaction_id = transaction.id, + ) + logger.error(f"This is Response Returned ****** : {str(response)}") - # this chck for error is not valid - logger.error(f"Check for ERROR is not valid ****** FIX THIS !!!!!") - #if "error" in response or response.get("status") != 200: - # return jsonify({"message": "RACCheck failed"}), 400 + # this chck for error is not valid + logger.error(f"Check for ERROR is not valid ****** FIX THIS !!!!!") + #if "error" in response or response.get("status") != 200: + # return jsonify({"message": "RACCheck failed"}), 400 - offers = [ - { - "offerId": "SAL90", - "productId": "2030", - "minAmount": 5000, - "maxAmount": 100000, - "tenor": 30 - }, - { - "offerId": "SAL30", - "productId": "2090", - "minAmount": 3000, - "maxAmount": 500000, - "tenor": 90 - } - ] + offers = [ + { + "offerId": "SAL90", + "productId": "2030", + "minAmount": 5000, + "maxAmount": 100000, + "tenor": 30 + }, + { + "offerId": "SAL30", + "productId": "2090", + "minAmount": 3000, + "maxAmount": 500000, + "tenor": 90 + } + ] - # Simulate processing - response_data = { - "customerId": customer_id, - "transactionId": transactionId, - "countryCode": "NG", - "msisdn": msisdn, - "eligibleOffers": offers, - "resultDescription": "Successful", - "resultCode": "00", - "accountId": account_id - } + # Simulate processing + response_data = { + "customerId": customer_id, + "transactionId": transactionId, + "countryCode": "NG", + "msisdn": msisdn, + "eligibleOffers": offers, + "resultDescription": "Successful", + "resultCode": "00", + "accountId": account_id + } - return response_data + return response_data + except ValidationError as err: logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}") diff --git a/app/api/services/loan_status.py b/app/api/services/loan_status.py index cdd161c..f80b7f2 100644 --- a/app/api/services/loan_status.py +++ b/app/api/services/loan_status.py @@ -1,9 +1,11 @@ from flask import request, jsonify from marshmallow import ValidationError +from app.models import Customer from app.utils.logger import logger from app.api.schemas.loan_status import LoanStatusSchema from app.api.services.base_service import BaseService -from app.api.enums import TransactionType +from app.api.enums import TransactionType +from app.extensions import db class LoanStatusService(BaseService): @@ -21,12 +23,23 @@ class LoanStatusService(BaseService): dict: A standardized response. """ try: - validated_data = LoanStatusService.validate_data(data, LoanStatusSchema()) - customer_id = validated_data.get('customerId') - customer = LoanStatusService.get_or_create_customer(validated_data) - account = customer.accounts[0] + with db.session.begin(): + # Validate data + validated_data = LoanStatusService.validate_data(data, LoanStatusSchema()) + + + customer_id = validated_data.get('customerId') + customer = Customer.get_customer(customer_id) + 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" + - if (LoanStatusService.validate_account_ownership(account_id = account.id, customer_id = customer_id)): transaction = LoanStatusService.log_transaction(validated_data = validated_data) if not transaction: @@ -34,41 +47,38 @@ class LoanStatusService(BaseService): return jsonify({ "message": "Failed to log transaction." }), 400 - else: - return jsonify({ - "message": "Invalid Customer or Account" - }), 400 - + - loans = [ - { - "debtId": "123456789", - "loanDate": "2019-10-18 14:26:21.063", - "dueDate": "2019-11-20 14:26:21.063", - "currentLoanAmount": 8500, - "initialLoanAmount": 10000, - "defaultPenaltyFee": 0, - "continuousFee": 0, - "productId": "101" + # loans = [ + # { + # "debtId": "123456789", + # "loanDate": "2019-10-18 14:26:21.063", + # "dueDate": "2019-11-20 14:26:21.063", + # "currentLoanAmount": 8500, + # "initialLoanAmount": 10000, + # "defaultPenaltyFee": 0, + # "continuousFee": 0, + # "productId": "101" + # } + # ] + + # Simulated processing logic + response_data = { + "customerId": customer_id, + "transactionId": transactionId, + "loans": loans, + "totalDebtAmount": 8500, + "resultCode": "00", + "resultDescription": "Successful" } - ] - # Simulated processing logic - response_data = { - "customerId": "CN621868", - "transactionId": "Tr201712RK9232P115", - "loans": loans, - "totalDebtAmount": 8500, - "resultCode": "00", - "resultDescription": "Successful" - } - - - return response_data + db.session.commit() + return response_data except ValidationError as err: logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}") + db.session.rollback() return jsonify({ "message": "Validation exception" @@ -76,6 +86,7 @@ class LoanStatusService(BaseService): except ValueError as err: logger.error(f"{getattr(err, 'messages', str(err))}") + db.session.rollback() return jsonify({ "message": str(err) @@ -83,6 +94,7 @@ class LoanStatusService(BaseService): except Exception as e: logger.error(f"An error occurred: {str(e)}", exc_info=True) + db.session.rollback() return jsonify({ "message": "Internal Server Error" }) , 500 \ No newline at end of file diff --git a/app/api/services/notification_callback.py b/app/api/services/notification_callback.py index b3a1c8b..6257b2b 100644 --- a/app/api/services/notification_callback.py +++ b/app/api/services/notification_callback.py @@ -3,7 +3,8 @@ from marshmallow import ValidationError from app.api.services.base_service import BaseService from app.api.enums import TransactionType from app.utils.logger import logger -from app.api.schemas.notification_callback import NotificationCallbackSchema +from app.api.schemas.notification_callback import NotificationCallbackSchema +from app.extensions import db class NotificationCallbackService(BaseService): TRANSACTION_TYPE = TransactionType.NOTIFICATION_CALLBACK diff --git a/app/api/services/provide_loan.py b/app/api/services/provide_loan.py index e268a92..b496265 100644 --- a/app/api/services/provide_loan.py +++ b/app/api/services/provide_loan.py @@ -8,6 +8,7 @@ from app.api.schemas.provide_loan import ProvideLoanSchema from threading import Thread from app.models.loan import Loan from app.api.enums import LoanStatus +from app.extensions import db class ProvideLoanService(BaseService): TRANSACTION_TYPE = TransactionType.PROVIDE_LOAN @@ -25,67 +26,75 @@ class ProvideLoanService(BaseService): dict: A standardized response. """ try: - validated_data = ProvideLoanService.validate_data(data, ProvideLoanSchema()) - account_id = validated_data.get('accountId') - customer_id = validated_data.get('customerId') - request_id = validated_data.get('requestId') - transaction_id = validated_data.get('transactionId') + with db.session.begin(): + validated_data = ProvideLoanService.validate_data(data, ProvideLoanSchema()) + account_id = validated_data.get('accountId') + customer_id = validated_data.get('customerId') + request_id = validated_data.get('requestId') + transaction_id = validated_data.get('transactionId') - if (ProvideLoanService.validate_account_ownership(account_id = account_id, customer_id = customer_id)): + if (ProvideLoanService.validate_account_ownership(account_id = account_id, customer_id = customer_id)): - # 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 - ) - - if not loan: - logger.error(f"Failed to save loan details") - return jsonify({ - "message": "Failed to save loan details." - }), 400 - # Log Transaction - transaction = ProvideLoanService.log_transaction(validated_data = validated_data) + # 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 + ) - if not transaction: - logger.error(f"Failed to log transaction") - return jsonify({ - "message": "Failed to log transaction." - }), 400 + if not loan: + logger.error(f"Failed to save loan details") + return jsonify({ + "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) + + if not transaction: + logger.error(f"Failed to log transaction") + return jsonify({ + "message": "Failed to log transaction." + }), 400 - - else: - return jsonify({ - "message": "Invalid Customer or Account" - }), 400 - - - response_data = { - "requestId": request_id, - "transactionId": transaction_id, - "customerId": customer_id, - "accountId": account_id, - "msisdn": "3451342", - "resultCode": "00", - "resultDescription": "Successful" - } + + else: + return jsonify({ + "message": "Invalid Customer or Account" + }), 400 + + + response_data = { + "requestId": request_id, + "transactionId": transaction_id, + "customerId": customer_id, + "accountId": account_id, + "msisdn": "3451342", + "resultCode": "00", + "resultDescription": "Successful" + } - # KafkaIntegration.send_loan_request(loan_data = response_data, request_id = request_id) - # Call Kafka in a background thread - thread = Thread(target=ProvideLoanService.async_send_to_kafka, args=(response_data, request_id, "PROCESS_PAYMENT")) - thread.start() + # KafkaIntegration.send_loan_request(loan_data = response_data, request_id = request_id) + # Call Kafka in a background thread + thread = Thread(target=ProvideLoanService.async_send_to_kafka, args=(response_data, request_id, "PROCESS_PAYMENT")) + thread.start() - return response_data + db.session.commit() + return response_data except ValidationError as err: logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}") + db.session.rollback() return jsonify({ "message": "Validation exception" @@ -93,6 +102,7 @@ class ProvideLoanService(BaseService): except ValueError as err: logger.error(f"{getattr(err, 'messages', str(err))}") + db.session.rollback() return jsonify({ "message": str(err) @@ -100,6 +110,7 @@ class ProvideLoanService(BaseService): except Exception as e: logger.error(f"An error occurred: {str(e)}", exc_info=True) + db.session.rollback() return jsonify({ "message": "Internal Server Error" }) , 500 diff --git a/app/api/services/repayment.py b/app/api/services/repayment.py index d92f8b8..33be586 100644 --- a/app/api/services/repayment.py +++ b/app/api/services/repayment.py @@ -7,7 +7,8 @@ from app.utils.logger import logger from app.api.schemas.repayment import RepaymentSchema from app.api.services.base_service import BaseService from app.api.enums import TransactionType -from threading import Thread +from threading import Thread +from app.extensions import db class RepaymentService(BaseService): TRANSACTION_TYPE = TransactionType.REPAYMENT @@ -24,22 +25,20 @@ class RepaymentService(BaseService): dict: A standardized response. """ try: - validated_data = RepaymentService.validate_data(data, RepaymentSchema()) - customer_id = validated_data.get('customerId') - customer = RepaymentService.get_or_create_customer(validated_data) - account = customer.accounts[0] - validated_data['accountId'] = account.id - request_id = validated_data.get('requestId') - loan_id = validated_data.get('debtId') + with db.session.begin(): + validated_data = RepaymentService.validate_data(data, RepaymentSchema()) + customer_id = validated_data.get('customerId') + request_id = validated_data.get('requestId') + loan_id = validated_data.get('debtId') + product_id = validated_data.get('productId') - - if (RepaymentService.validate_account_ownership(account_id = account.id, customer_id = customer_id)): - - # Save the repayment details + + + # Save the repayment details repayment = Repayment.create_repayment( customer_id = customer_id, loan_id = loan_id, - product_id = validated_data.get('productId') + product_id = product_id ) @@ -49,6 +48,11 @@ class RepaymentService(BaseService): "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) @@ -59,34 +63,33 @@ class RepaymentService(BaseService): return jsonify({ "message": "Failed to log transaction." }), 400 - else: - return jsonify({ - "message": "Invalid Customer or Account" - }), 400 - # Simulated processing logic - response_data = { - "customerId": "CN621868", - "productId": "101", - "debtId": "273194670", - "resultCode": "00", - "resultDescription": "Successful" - } - # return ResponseHelper.success( - # data=response_data, - # message="Repayment processed successfully" - # ) + # Simulated processing logic + response_data = { + "customerId": customer_id, + "productId": product_id, + "debtId": loan_id, + "resultCode": "00", + "resultDescription": "Successful" + } - # Call Kafka in a background thread - thread = Thread(target=RepaymentService.async_send_to_kafka, args=(response_data, request_id, "LOAN_REPAYMENT")) - thread.start() + # return ResponseHelper.success( + # data=response_data, + # message="Repayment processed successfully" + # ) - return response_data + # Call Kafka in a background thread + thread = Thread(target=RepaymentService.async_send_to_kafka, args=(response_data, request_id, "LOAN_REPAYMENT")) + thread.start() + + db.session.commit() + return response_data except ValidationError as err: logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}") + db.session.rollback() return jsonify({ "message": "Validation exception" @@ -94,6 +97,7 @@ class RepaymentService(BaseService): except ValueError as err: logger.error(f"{getattr(err, 'messages', str(err))}") + db.session.rollback() return jsonify({ "message": str(err) @@ -101,6 +105,7 @@ class RepaymentService(BaseService): except Exception as e: logger.error(f"An error occurred: {str(e)}", exc_info=True) + db.session.rollback() return jsonify({ "message": "Internal Server Error" }) , 500 diff --git a/app/api/services/select_offer.py b/app/api/services/select_offer.py index 1f07cd5..6a0a914 100644 --- a/app/api/services/select_offer.py +++ b/app/api/services/select_offer.py @@ -1,12 +1,14 @@ from flask import request, jsonify from marshmallow import ValidationError from app.api.services.base_service import BaseService -from app.api.enums import TransactionType +from app.api.enums import TransactionType from app.utils.logger import logger from app.api.schemas.select_offer import SelectOfferSchema +from app.extensions import db + class SelectOfferService(BaseService): - TRANSACTION_TYPE = TransactionType.SELECT_OFFER + TRANSACTION_TYPE = TransactionType.SELECT_OFFER @staticmethod def process_request(data): @@ -20,74 +22,72 @@ class SelectOfferService(BaseService): dict: A standardized response. """ try: - validated_data = SelectOfferService.validate_data(data, SelectOfferSchema()) - account_id = validated_data.get('accountId') - customer_id = validated_data.get('customerId') + with db.session.begin(): + validated_data = SelectOfferService.validate_data( + data, SelectOfferSchema() + ) + account_id = validated_data.get("accountId") + customer_id = validated_data.get("customerId") - if (SelectOfferService.validate_account_ownership(account_id = account_id, customer_id = customer_id)): - transaction = SelectOfferService.log_transaction(validated_data = validated_data) + if SelectOfferService.validate_account_ownership( + account_id=account_id, customer_id=customer_id + ): + transaction = SelectOfferService.log_transaction( + validated_data=validated_data + ) - if not transaction: - logger.error(f"Failed to log transaction") - return jsonify({ - "message": "Failed to log transaction." - }), 400 - else: - return jsonify({ - "message": "Invalid Customer or Account" - }), 400 - - offers = [ + if not transaction: + logger.error(f"Failed to log transaction") + return jsonify({"message": "Failed to log transaction."}), 400 + else: + return jsonify({"message": "Invalid Customer or Account"}), 400 + + offers = [ { - "offerId": "14451", - "productId": "2030", - "amount": 10000.0, - "upfrontPayment": 1000.0, - "interestRate": 3.0, - "managementRate": 1.0, - "managementFee": 1.0, - "insuranceRate": 1.0, - "insuranceFee": 100.0, - "VATRate": 7.5, - "VATAmount": 100.0, - "recommendedRepaymentDates": ["2022-11-30"], - "installmentAmount": 11000.0, - "totalRepaymentAmount": 11000.0 + "offerId": "14451", + "productId": "2030", + "amount": 10000.0, + "upfrontPayment": 1000.0, + "interestRate": 3.0, + "managementRate": 1.0, + "managementFee": 1.0, + "insuranceRate": 1.0, + "insuranceFee": 100.0, + "VATRate": 7.5, + "VATAmount": 100.0, + "recommendedRepaymentDates": ["2022-11-30"], + "installmentAmount": 11000.0, + "totalRepaymentAmount": 11000.0, } ] - # Business logic - selecting an offer - response_data = { - "outstandingDebtAmount": 0, - "requestId": "202111170001371256908", - "transactionId": transaction.id, - "customerId": customer_id, - "accountId": account_id, - "loan": offers, - "resultCode": "00", - "resultDescription": "Successful" + # Business logic - selecting an offer + response_data = { + "outstandingDebtAmount": 0, + "requestId": "202111170001371256908", + "transactionId": transaction.id, + "customerId": customer_id, + "accountId": account_id, + "loan": offers, + "resultCode": "00", + "resultDescription": "Successful", } - - return response_data + db.session.commit() + return response_data except ValidationError as err: logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}") + db.session.rollback() + return jsonify({"message": "Validation exception"}), 422 - return jsonify({ - "message": "Validation exception" - }) , 422 - - except ValueError as err: + except ValueError as err: logger.error(f"{getattr(err, 'messages', str(err))}") - - return jsonify({ - "message": str(err) - }) , 400 + db.session.rollback() + return jsonify({"message": str(err)}), 400 except Exception as e: logger.error(f"An error occurred: {str(e)}", exc_info=True) - return jsonify({ - "message": "Internal Server Error" - }) , 500 \ No newline at end of file + db.session.rollback() + return jsonify({"message": "Internal Server Error"}), 500 diff --git a/app/models/account.py b/app/models/account.py index af4501e..e8a90ce 100644 --- a/app/models/account.py +++ b/app/models/account.py @@ -31,9 +31,7 @@ class Account(db.Model): try: db.session.add(account) - db.session.commit() except IntegrityError as err: - db.session.rollback() raise ValueError(f"Database integrity error: {err}") return account diff --git a/app/models/customer.py b/app/models/customer.py index e0fb316..7692f5a 100644 --- a/app/models/customer.py +++ b/app/models/customer.py @@ -20,6 +20,13 @@ class Customer(db.Model): back_populates="customer", ) + loans = relationship( + "Loan", + primaryjoin="Customer.id == Loan.customer_id", + foreign_keys="Loan.customer_id", + back_populates="customer", + ) + @classmethod def is_valid_customer(cls, customer_id): customer = cls.query.filter_by(id=customer_id).first() @@ -44,11 +51,20 @@ class Customer(db.Model): account_type=account_type ) - db.session.commit() except IntegrityError as err: - db.session.rollback() raise ValueError(f"Database integrity error: {err}") return customer + + @classmethod + def get_customer(cls, customer_id): + """ + Get customer by ID. + """ + customer = cls.query.filter_by(id=customer_id).first() + + if not customer: + raise ValueError(f"Customer does not exist") + return customer def __repr__(self): return f'' diff --git a/app/models/loan.py b/app/models/loan.py index 0fec3d2..33256e4 100644 --- a/app/models/loan.py +++ b/app/models/loan.py @@ -3,6 +3,8 @@ from app.extensions import db from app.models.customer import Customer from app.models.account import Account from sqlalchemy.exc import IntegrityError +from sqlalchemy.orm import relationship +from app.models import Customer class Loan(db.Model): @@ -21,6 +23,12 @@ class Loan(db.Model): created_at = db.Column(db.DateTime, default=datetime.now(timezone.utc)) updated_at = db.Column(db.DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc)) + customer = relationship( + "Customer", + primaryjoin="Customer.id == Loan.customer_id", + foreign_keys=[customer_id], + back_populates="loans", + ) @classmethod def create_loan(cls, customer_id, account_id, offer_id, principal_amount, status='pending'): @@ -47,9 +55,7 @@ class Loan(db.Model): try: db.session.add(loan) - db.session.commit() except IntegrityError as err: - db.session.rollback() raise ValueError(f"Database integrity error: {err}") return loan @@ -92,8 +98,21 @@ class Loan(db.Model): # Update loan status and the updated_at timestamp loan.status = status - - db.session.commit() + + def to_dict(self): + """ + 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, + '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 + } def __repr__(self): return f'' \ No newline at end of file diff --git a/app/models/repayment.py b/app/models/repayment.py index 06b872c..c313131 100644 --- a/app/models/repayment.py +++ b/app/models/repayment.py @@ -38,15 +38,13 @@ class Repayment(db.Model): repayment = cls( customer_id=customer_id, - loan_id=loan.id, + loan_id=loan_id, product_id=product_id, ) try: db.session.add(repayment) - db.session.commit() except IntegrityError as err: - db.session.rollback() raise ValueError(f"Database integrity error: {err}") return repayment diff --git a/app/models/transaction.py b/app/models/transaction.py index 5bde2db..1775d2c 100644 --- a/app/models/transaction.py +++ b/app/models/transaction.py @@ -10,9 +10,9 @@ class Transaction(db.Model): primary_key=True, autoincrement=True, ) - #id = db.Column(db.Int, primary_key=True) transaction_id = db.Column(db.String(50), nullable=False) - account_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') 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, account_id, type, channel): + def create_transaction(cls, transaction_id, ref_id, ref_model, type, channel): # if cls.query.filter_by(transaction_id=transaction_id).first(): # raise ValueError("Duplicate Transaction") @@ -33,17 +33,16 @@ class Transaction(db.Model): transaction = cls( - transaction_id=transaction_id, - account_id=account_id, - type=type, - channel=channel + transaction_id = transaction_id, + ref_id = ref_id, + ref_model = ref_model, + type = type, + channel = channel ) try: db.session.add(transaction) - db.session.commit() except IntegrityError as err: - db.session.rollback() raise ValueError(f"Database integrity error: {err}") return transaction diff --git a/migrations/versions/1340e7e578b9_migration_on_thu_apr_10_21_50_01_utc_.py b/migrations/versions/1340e7e578b9_migration_on_thu_apr_10_21_50_01_utc_.py new file mode 100644 index 0000000..92e0eef --- /dev/null +++ b/migrations/versions/1340e7e578b9_migration_on_thu_apr_10_21_50_01_utc_.py @@ -0,0 +1,32 @@ +"""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 ###