From 7cea5390c0726ad8535a811816286e45dd7206c9 Mon Sep 17 00:00:00 2001 From: VivianDee <115420678+VivianDee@users.noreply.github.com> Date: Thu, 10 Apr 2025 17:02:54 +0100 Subject: [PATCH] [add]: loan_repayment event --- .vscode/settings.json | 48 ++++++++++++++++---------------- app/api/integrations/kafka.py | 6 ++-- app/api/services/base_service.py | 6 ++++ app/api/services/provide_loan.py | 7 ++--- app/api/services/repayment.py | 12 +++++++- app/models/loan.py | 10 +++++++ app/models/repayment.py | 45 ++++++++++++++++++++++++++++++ 7 files changed, 101 insertions(+), 33 deletions(-) create mode 100644 app/models/repayment.py 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/integrations/kafka.py b/app/api/integrations/kafka.py index 610ad1e..fa3f8d1 100644 --- a/app/api/integrations/kafka.py +++ b/app/api/integrations/kafka.py @@ -43,9 +43,9 @@ class KafkaIntegration: @staticmethod - def send_loan_request(loan_data, request_id): + def send_loan_request(loan_data, request_id, topic): """ - Send loan request to PROCESS_PAYMENT topic + Send loan request to topic Args: loan_data: Loan request payload as dict @@ -58,7 +58,7 @@ class KafkaIntegration: # Sending loan request message to Kafka producer.produce( - topic="PROCESS_PAYMENT", + topic=topic, key=str(request_id), value=json.dumps(loan_data).encode("utf-8"), callback=KafkaIntegration.delivery_report, diff --git a/app/api/services/base_service.py b/app/api/services/base_service.py index db4bc64..aa678e0 100644 --- a/app/api/services/base_service.py +++ b/app/api/services/base_service.py @@ -3,6 +3,7 @@ from app.api.enums import TransactionType from flask import jsonify from marshmallow import ValidationError import logging +from app.api.integrations import KafkaIntegration logger = logging.getLogger(__name__) @@ -53,3 +54,8 @@ class BaseService: type=cls.TRANSACTION_TYPE, channel=validated_data.get("channel"), ) + + @classmethod + def async_send_to_kafka(cls, loan_data, request_id, topic): + KafkaIntegration.send_loan_request(loan_data = loan_data, request_id = request_id, topic = topic) + KafkaIntegration.flush() diff --git a/app/api/services/provide_loan.py b/app/api/services/provide_loan.py index dfc5a96..dc43cc4 100644 --- a/app/api/services/provide_loan.py +++ b/app/api/services/provide_loan.py @@ -5,7 +5,6 @@ from app.api.services.base_service import BaseService from app.api.enums import TransactionType from app.utils.logger import logger from app.api.schemas.provide_loan import ProvideLoanSchema -from app.api.integrations import KafkaIntegration from threading import Thread @@ -58,7 +57,7 @@ class ProvideLoanService(BaseService): # 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)) + thread = Thread(target=ProvideLoanService.async_send_to_kafka, args=(response_data, request_id, "PROCESS_PAYMENT")) thread.start() return response_data @@ -85,8 +84,6 @@ class ProvideLoanService(BaseService): }) , 500 - def async_send_to_kafka(loan_data, request_id): - 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..556d0a3 100644 --- a/app/api/services/repayment.py +++ b/app/api/services/repayment.py @@ -3,7 +3,8 @@ from marshmallow import ValidationError 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 app.api.enums import TransactionType +from threading import Thread class RepaymentService(BaseService): TRANSACTION_TYPE = TransactionType.REPAYMENT @@ -23,6 +24,11 @@ class RepaymentService(BaseService): 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] + validated_data['accountId'] = account.id + request_id = validated_data.get('requestId') + if (RepaymentService.validate_account_ownership(account_id = account_id, customer_id = customer_id)): transaction = RepaymentService.log_transaction(validated_data = validated_data) @@ -51,6 +57,10 @@ class RepaymentService(BaseService): # message="Repayment processed successfully" # ) + # Call Kafka in a background thread + thread = Thread(target=RepaymentService.async_send_to_kafka, args=(response_data, request_id, "LOAN_REPAYMENT")) + thread.start() + return response_data except ValidationError as err: diff --git a/app/models/loan.py b/app/models/loan.py index 6fa73a1..6779c3c 100644 --- a/app/models/loan.py +++ b/app/models/loan.py @@ -26,6 +26,16 @@ class Loan(db.Model): return False, "Customer has active loans" return True, "No active loans" + @classmethod + def get_customer_loan(cls, loan_id, customer_id): + """ + Check if a loan with the given ID exists and if the loan belongs to the specified customer_id. + """ + loan = cls.query.filter_by(id=loan_id, customer_id=customer_id).first() + if not loan: + raise ValueError(f"Loan with ID {loan_id} does not exist or does not belong to customer {customer_id}.") + return loan + def __repr__(self): return f'' \ No newline at end of file diff --git a/app/models/repayment.py b/app/models/repayment.py new file mode 100644 index 0000000..af62554 --- /dev/null +++ b/app/models/repayment.py @@ -0,0 +1,45 @@ +from datetime import datetime, timezone +from app.extensions import db +from app.models.customer import Customer +from app.models.loan import Loan + + +class Repayment(db.Model): + __tablename__ = 'repayments' + + id = db.Column( + db.Integer, + primary_key=True, + autoincrement=True, + ) + loan_id = db.Column(db.String(50), nullable=False) + customer_id = db.Column(db.String(50), nullable=False) + product_id = db.Column(db.String(20), 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)) + + @classmethod + def create_repayment(cls, customer_id, loan_id, product_id): + + + # Check customer exists + if not Customer.is_valid_customer(customer_id): + raise ValueError("Invalid customer") + + # Check loan exists + loan = Loan.get_customer_loan(loan_id = loan_id, customer_id = customer_id) + if not loan: + raise ValueError("Loan not found for customer") + + repayment = cls( + customer_id=customer_id, + loan_id=loan.id, + product_id=product_id, + ) + + db.session.add(repayment) + db.session.commit() + return repayment + + def __repr__(self): + return f''