From 1b973322c9e5c12fb3de070cc3401c1dff269ca8 Mon Sep 17 00:00:00 2001 From: VivianDee <115420678+VivianDee@users.noreply.github.com> Date: Thu, 10 Apr 2025 12:20:57 +0100 Subject: [PATCH 1/6] [add]: Loan details and repayment fix --- .vscode/settings.json | 48 ++++++++++++++++---------------- app/api/services/provide_loan.py | 17 ++++++++++- app/api/services/repayment.py | 6 ++-- app/api/services/select_offer.py | 2 +- app/models/customer.py | 6 ++-- app/models/loan.py | 34 ++++++++++++++++++++-- 6 files changed, 80 insertions(+), 33 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index b79483b..bc7c726 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,24 +1,24 @@ -{ - "editor.lineNumbers": "off", - "editor.padding.top": 3, - "editor.padding.bottom": 3, - "editor.formatOnSave": true, - "editor.formatOnPaste": true, - "editor.fontSize": 14, - "editor.lineHeight": 4.5, - "editor.suggestFontSize": 15, - // "editor.suggestLineHeight": 4, - "breadcrumbs.enabled": false, - "workbench.tips.enabled": false, - "workbench.statusBar.visible": false, - // "workbench.editor.showTabs": "single", - "git.enableSmartCommit": true, - "workbench.editor.editorActionsLocation": "hidden", - // "workbench.activityBar.location": "hidden", - "workbench.editor.enablePreviewFromQuickOpen": false, - "editor.lightbulb.enabled": "off", - "editor.selectionHighlight": false, - "editor.overviewRulerBorder": false, - "editor.renderLineHighlight": "none", - "editor.occurrencesHighlight": "off" -} +// { +// "editor.lineNumbers": "off", +// "editor.padding.top": 3, +// "editor.padding.bottom": 3, +// "editor.formatOnSave": true, +// "editor.formatOnPaste": true, +// "editor.fontSize": 14, +// "editor.lineHeight": 4.5, +// "editor.suggestFontSize": 15, +// // "editor.suggestLineHeight": 4, +// "breadcrumbs.enabled": false, +// "workbench.tips.enabled": false, +// "workbench.statusBar.visible": false, +// // "workbench.editor.showTabs": "single", +// "git.enableSmartCommit": true, +// "workbench.editor.editorActionsLocation": "hidden", +// // "workbench.activityBar.location": "hidden", +// "workbench.editor.enablePreviewFromQuickOpen": false, +// "editor.lightbulb.enabled": "off", +// "editor.selectionHighlight": false, +// "editor.overviewRulerBorder": false, +// "editor.renderLineHighlight": "none", +// "editor.occurrencesHighlight": "off" +// } diff --git a/app/api/services/provide_loan.py b/app/api/services/provide_loan.py index dfc5a96..9833661 100644 --- a/app/api/services/provide_loan.py +++ b/app/api/services/provide_loan.py @@ -7,6 +7,7 @@ from app.utils.logger import logger from app.api.schemas.provide_loan import ProvideLoanSchema from app.api.integrations import KafkaIntegration from threading import Thread +from app.models.loan import Loan class ProvideLoanService(BaseService): @@ -32,8 +33,23 @@ class ProvideLoanService(BaseService): transaction_id = validated_data.get('transactionId') if (ProvideLoanService.validate_account_ownership(account_id = account_id, customer_id = customer_id)): + + # Save the loan details here + loan_id = f"loan_{transaction.id}" + + loan = Loan.create_loan( + id=loan_id, + customer_id=customer_id, + account_id=account_id, + product_id=validated_data.get('productId'), + principal_amount=validated_data.get('requestedAmount'), + status="active" + ) + transaction = ProvideLoanService.log_transaction(validated_data = validated_data) + + if not transaction: logger.error(f"Failed to log transaction") return jsonify({ @@ -89,4 +105,3 @@ class ProvideLoanService(BaseService): KafkaIntegration.send_loan_request(loan_data = loan_data, request_id = request_id) KafkaIntegration.flush() - diff --git a/app/api/services/repayment.py b/app/api/services/repayment.py index 7a53452..737e22f 100644 --- a/app/api/services/repayment.py +++ b/app/api/services/repayment.py @@ -21,10 +21,12 @@ class RepaymentService(BaseService): """ try: validated_data = RepaymentService.validate_data(data, RepaymentSchema()) - account_id = validated_data.get('accountId') customer_id = validated_data.get('customerId') + customer = RepaymentService.get_or_create_customer(validated_data) + account = customer.accounts[0] - if (RepaymentService.validate_account_ownership(account_id = account_id, customer_id = customer_id)): + + if (RepaymentService.validate_account_ownership(account_id = account.id, customer_id = customer_id)): transaction = RepaymentService.log_transaction(validated_data = validated_data) if not transaction: diff --git a/app/api/services/select_offer.py b/app/api/services/select_offer.py index 151b635..1f07cd5 100644 --- a/app/api/services/select_offer.py +++ b/app/api/services/select_offer.py @@ -3,7 +3,7 @@ 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.select_offer import SelectOfferSchema +from app.api.schemas.select_offer import SelectOfferSchema class SelectOfferService(BaseService): TRANSACTION_TYPE = TransactionType.SELECT_OFFER diff --git a/app/models/customer.py b/app/models/customer.py index 2cb9c5d..e6418c1 100644 --- a/app/models/customer.py +++ b/app/models/customer.py @@ -20,11 +20,11 @@ class Customer(db.Model): ) @classmethod - def is_eligible(cls, customer_id): + def is_valid_customer(cls, customer_id): customer = cls.query.filter_by(id=customer_id).first() if not customer: - return False, "Customer not found" - return True, "Customer is eligible" + return False + return True @classmethod def create_customer(cls, id, msisdn, country_code, account_id, account_type='savings'): diff --git a/app/models/loan.py b/app/models/loan.py index 6fa73a1..edec13f 100644 --- a/app/models/loan.py +++ b/app/models/loan.py @@ -1,5 +1,7 @@ from datetime import datetime, timezone from app.extensions import db +from app.models.customer import Customer +from app.models.account import Account class Loan(db.Model): @@ -15,6 +17,34 @@ class Loan(db.Model): updated_at = db.Column(db.DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc)) + @classmethod + def create_loan(cls, id, customer_id, account_id, product_id, principal_amount, 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") + + # Create and save the loan + loan = cls( + id=id, + customer_id=customer_id, + account_id=account_id, + product_id=product_id, + principal_amount=principal_amount, + status=status + ) + + db.session.add(loan) + db.session.commit() + return loan + + @classmethod def has_active_loans(cls, customer_id): active_loans = cls.query.filter_by( @@ -23,8 +53,8 @@ class Loan(db.Model): ).count() if active_loans > 0: - return False, "Customer has active loans" - return True, "No active loans" + return False + return True def __repr__(self): From 2c34c16c34afa34157c7cc6401f6c99901ac504d Mon Sep 17 00:00:00 2001 From: VivianDee <115420678+VivianDee@users.noreply.github.com> Date: Thu, 10 Apr 2025 12:27:57 +0100 Subject: [PATCH 2/6] [fix]: Missing account id --- app/api/services/repayment.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/api/services/repayment.py b/app/api/services/repayment.py index 737e22f..e1500bf 100644 --- a/app/api/services/repayment.py +++ b/app/api/services/repayment.py @@ -24,6 +24,7 @@ class RepaymentService(BaseService): customer_id = validated_data.get('customerId') customer = RepaymentService.get_or_create_customer(validated_data) account = customer.accounts[0] + validated_data['accountId'] = account.id if (RepaymentService.validate_account_ownership(account_id = account.id, customer_id = customer_id)): From 87f1fa2152d82e81f7c1ab634dae8c36287fb332 Mon Sep 17 00:00:00 2001 From: VivianDee <115420678+VivianDee@users.noreply.github.com> Date: Thu, 10 Apr 2025 12:35:46 +0100 Subject: [PATCH 3/6] [fix]: save loan --- app/api/schemas/provide_loan.py | 4 ++-- app/api/services/provide_loan.py | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/api/schemas/provide_loan.py b/app/api/schemas/provide_loan.py index 4d85a3b..43c1044 100644 --- a/app/api/schemas/provide_loan.py +++ b/app/api/schemas/provide_loan.py @@ -8,8 +8,8 @@ class ProvideLoanSchema(Schema): customerId = fields.Str(required=True) accountId = fields.Str(required=True) msisdn = fields.Str(required=False) - # productId = fields.Str(required=True) - # lienAmount = fields.Float(required=True) + productId = fields.Str(required=True) + lienAmount = fields.Float(required=True) requestedAmount = fields.Float(required=True) collectionType = fields.Int(required=True) offerId = fields.Int(required=True) diff --git a/app/api/services/provide_loan.py b/app/api/services/provide_loan.py index 9833661..ad30b6b 100644 --- a/app/api/services/provide_loan.py +++ b/app/api/services/provide_loan.py @@ -34,6 +34,8 @@ class ProvideLoanService(BaseService): if (ProvideLoanService.validate_account_ownership(account_id = account_id, customer_id = customer_id)): + transaction = ProvideLoanService.log_transaction(validated_data = validated_data) + # Save the loan details here loan_id = f"loan_{transaction.id}" @@ -46,7 +48,7 @@ class ProvideLoanService(BaseService): status="active" ) - transaction = ProvideLoanService.log_transaction(validated_data = validated_data) + From f13ca508c272176c4603c531abcc55a16e8da2bc Mon Sep 17 00:00:00 2001 From: VivianDee <115420678+VivianDee@users.noreply.github.com> Date: Thu, 10 Apr 2025 12:40:19 +0100 Subject: [PATCH 4/6] Update provide_loan.py --- app/api/services/provide_loan.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/app/api/services/provide_loan.py b/app/api/services/provide_loan.py index ad30b6b..58c02cf 100644 --- a/app/api/services/provide_loan.py +++ b/app/api/services/provide_loan.py @@ -35,7 +35,13 @@ class ProvideLoanService(BaseService): if (ProvideLoanService.validate_account_ownership(account_id = account_id, customer_id = customer_id)): 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 + # Save the loan details here loan_id = f"loan_{transaction.id}" @@ -48,15 +54,13 @@ class ProvideLoanService(BaseService): status="active" ) - - - - - if not transaction: - logger.error(f"Failed to log transaction") + if not loan: + logger.error(f"Failed to save loan details") return jsonify({ - "message": "Failed to log transaction." + "message": "Failed to save loan details." }), 400 + + else: return jsonify({ "message": "Invalid Customer or Account" From b22d2f358ebda33fdc58c456c7baf87251bf0a8b Mon Sep 17 00:00:00 2001 From: VivianDee <115420678+VivianDee@users.noreply.github.com> Date: Thu, 10 Apr 2025 12:58:30 +0100 Subject: [PATCH 5/6] [fix]: Removed out product and lien amount --- app/api/schemas/provide_loan.py | 4 ++-- app/api/services/provide_loan.py | 4 ++-- app/models/loan.py | 7 ++++--- app/swagger/schemas/ProvideLoanRequest.json | 9 --------- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/app/api/schemas/provide_loan.py b/app/api/schemas/provide_loan.py index 43c1044..5959e29 100644 --- a/app/api/schemas/provide_loan.py +++ b/app/api/schemas/provide_loan.py @@ -8,8 +8,8 @@ class ProvideLoanSchema(Schema): customerId = fields.Str(required=True) accountId = fields.Str(required=True) msisdn = fields.Str(required=False) - productId = fields.Str(required=True) - lienAmount = fields.Float(required=True) + # productId = fields.Str(required=True) + # lienAmount = fields.Float(required=True) requestedAmount = fields.Float(required=True) collectionType = fields.Int(required=True) offerId = fields.Int(required=True) diff --git a/app/api/services/provide_loan.py b/app/api/services/provide_loan.py index 58c02cf..ab48cca 100644 --- a/app/api/services/provide_loan.py +++ b/app/api/services/provide_loan.py @@ -42,14 +42,14 @@ class ProvideLoanService(BaseService): "message": "Failed to log transaction." }), 400 - # Save the loan details here + # Save the loan details loan_id = f"loan_{transaction.id}" loan = Loan.create_loan( id=loan_id, customer_id=customer_id, account_id=account_id, - product_id=validated_data.get('productId'), + offer_id=validated_data.get('offerId'), principal_amount=validated_data.get('requestedAmount'), status="active" ) diff --git a/app/models/loan.py b/app/models/loan.py index edec13f..0721bd2 100644 --- a/app/models/loan.py +++ b/app/models/loan.py @@ -10,7 +10,7 @@ class Loan(db.Model): id = db.Column(db.String(50), primary_key=True) customer_id = db.Column(db.String(50), nullable=False) account_id = db.Column(db.String(50), nullable=False) - product_id = db.Column(db.String(20), nullable=False) + offer_id = db.Column(db.String(20), nullable=False) principal_amount = db.Column(db.Float, nullable=False) status = db.Column(db.String(20), default='pending') created_at = db.Column(db.DateTime, default=datetime.now(timezone.utc)) @@ -18,7 +18,7 @@ class Loan(db.Model): @classmethod - def create_loan(cls, id, customer_id, account_id, product_id, principal_amount, status='pending'): + def create_loan(cls, id, customer_id, account_id, offer_id, principal_amount, status='pending'): # Check if customer exists is_valid = Customer.is_valid_customer(customer_id) @@ -30,12 +30,13 @@ class Loan(db.Model): # if has_active_loans: # raise ValueError("Customer has active loans") + # Create and save the loan loan = cls( id=id, customer_id=customer_id, account_id=account_id, - product_id=product_id, + offer_id=offer_id, principal_amount=principal_amount, status=status ) diff --git a/app/swagger/schemas/ProvideLoanRequest.json b/app/swagger/schemas/ProvideLoanRequest.json index c390945..721d8b9 100644 --- a/app/swagger/schemas/ProvideLoanRequest.json +++ b/app/swagger/schemas/ProvideLoanRequest.json @@ -21,15 +21,6 @@ "type": "string", "example": "3451342" }, - "productId": { - "type": "string", - "example": "101" - }, - "lienAmount": { - "type": "number", - "format": "decimal", - "example": 400 - }, "requestedAmount": { "type": "number", "format": "decimal", From 7026c8378b848b5a0d18e644ddd78de1dfc23973 Mon Sep 17 00:00:00 2001 From: VivianDee <115420678+VivianDee@users.noreply.github.com> Date: Thu, 10 Apr 2025 13:23:47 +0100 Subject: [PATCH 6/6] [fix]: Loan id --- app/api/services/provide_loan.py | 3 +-- app/models/loan.py | 9 ++++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/api/services/provide_loan.py b/app/api/services/provide_loan.py index ab48cca..214e58e 100644 --- a/app/api/services/provide_loan.py +++ b/app/api/services/provide_loan.py @@ -43,10 +43,9 @@ class ProvideLoanService(BaseService): }), 400 # Save the loan details - loan_id = f"loan_{transaction.id}" + loan_id = f"loan_{transaction_id}" loan = Loan.create_loan( - id=loan_id, customer_id=customer_id, account_id=account_id, offer_id=validated_data.get('offerId'), diff --git a/app/models/loan.py b/app/models/loan.py index 0721bd2..81c86fd 100644 --- a/app/models/loan.py +++ b/app/models/loan.py @@ -7,7 +7,11 @@ from app.models.account import Account class Loan(db.Model): __tablename__ = 'loans' - id = db.Column(db.String(50), primary_key=True) + 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=False) offer_id = db.Column(db.String(20), nullable=False) @@ -18,7 +22,7 @@ class Loan(db.Model): @classmethod - def create_loan(cls, id, customer_id, account_id, offer_id, principal_amount, status='pending'): + def create_loan(cls, customer_id, account_id, offer_id, principal_amount, status='pending'): # Check if customer exists is_valid = Customer.is_valid_customer(customer_id) @@ -33,7 +37,6 @@ class Loan(db.Model): # Create and save the loan loan = cls( - id=id, customer_id=customer_id, account_id=account_id, offer_id=offer_id,