diff --git a/app/api/integrations/simbrella.py b/app/api/integrations/simbrella.py index 3455aef..101664e 100644 --- a/app/api/integrations/simbrella.py +++ b/app/api/integrations/simbrella.py @@ -8,6 +8,7 @@ import logging class SimbrellaIntegration: BASE_URL = settings.SIMBRELLA_BASE_URL ENDPOINT_RAC_CHECKS = settings.SIMBRELLA_ENDPOINT_RAC_CHECKS + HEALTH_ENDPOINT = settings.SIMBRELLA_HEALTH @staticmethod def rac_check(customer_id, account_id, transaction_id): @@ -42,4 +43,21 @@ class SimbrellaIntegration: except Exception as e: logger.error(f"RACCheck API call failed: {str(e)}", exc_info=True) raise Exception(f"RACCheck API call failed: {str(e)}") + + @staticmethod + def health_check(): + """ + Health check for Simbrella Service + """ + + url = f"{SimbrellaIntegration.BASE_URL}/{SimbrellaIntegration.HEALTH_ENDPOINT}" + logger.info(f"Simbrella Health Check URL: {url}") + + try: + response = httpx.get(url, timeout=10.0) + logger.info(f"Simbrella Health Check Response: {response.text}") + return response + except Exception as e: + logger.error(f"Simbrella Health Check API call failed: {str(e)}", exc_info=True) + raise diff --git a/app/api/routes/routes.py b/app/api/routes/routes.py index b1ecc67..999d9b2 100644 --- a/app/api/routes/routes.py +++ b/app/api/routes/routes.py @@ -1,5 +1,7 @@ +from re import S from sqlite3 import DatabaseError from app.api.integrations.events_service import EventServiceIntegration +from app.api.integrations.simbrella import SimbrellaIntegration from flask import Blueprint, request, jsonify, send_from_directory from app.api.services import ( EligibilityCheckService, @@ -125,6 +127,7 @@ def health_check(): response = {} db_status = "Connection Successful" events_service_status = "Connection Successful" + emulator_status = "Connection Successful" errors = [] status = "ok" @@ -162,11 +165,26 @@ def health_check(): status = "failed" errors.append(f"Events Service connection failed: {str(e)}") + # Check Emulator health + try: + emulator_response = SimbrellaIntegration.health_check() + + if emulator_response.status_code != 200: + emulator_status = "Connection Failed" + status = "failed" + errors.append(f"Emulator response: {emulator_response.text}") + + except Exception as e: + emulator_status = "Connection Failed" + status = "failed" + errors.append(f"Emulator connection failed: {str(e)}") + response = { "status": status, "db_status": db_status, "events_service_status": events_service_status, + "emulator_status": emulator_status, "db_uri": db_uri, "errors": errors or None } diff --git a/app/api/services/loan_status.py b/app/api/services/loan_status.py index a9f2ad5..331c895 100644 --- a/app/api/services/loan_status.py +++ b/app/api/services/loan_status.py @@ -3,15 +3,15 @@ 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 +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 from app.api.helpers.response_helper import ResponseHelper class LoanStatusService(BaseService): - TRANSACTION_TYPE = TransactionType.LOAN_STATUS + TRANSACTION_TYPE = TransactionType.LOAN_STATUS @staticmethod def process_request(data): @@ -27,31 +27,64 @@ class LoanStatusService(BaseService): try: with db.session.begin(): # Validate data - validated_data = LoanStatusService.validate_data(data, LoanStatusSchema()) + validated_data = LoanStatusService.validate_data( + data, LoanStatusSchema() + ) - customer_id = validated_data.get('customerId') + customer_id = validated_data.get("customerId") logger.info(f"Looking for customer *** {customer_id}") customer = Customer.get_customer_with_loan_list(customer_id) - - transactionId = validated_data.get('transactionId') - account_id = validated_data.get('accountId') - if(LoanStatusService.validate_account_ownership(account_id = account_id, customer_id = customer_id)): + transactionId = validated_data.get("transactionId") + account_id = validated_data.get("accountId") + + if LoanStatusService.validate_account_ownership( + account_id=account_id, customer_id=customer_id + ): # Get loans - loans = [loan.to_dict() for loan in customer.loans if loan.status == LoanStatus.ACTIVE] - transaction = LoanStatusService.log_transaction(validated_data = validated_data) + customer_loans = customer.loans + loans = [ + loan.to_dict() + for loan in customer_loans + ] + + transaction = LoanStatusService.log_transaction( + validated_data=validated_data + ) if not transaction: logger.error(f"Failed to log transaction") - return ResponseHelper.error(result_description="Failed to log transaction.") - else: - return ResponseHelper.error(result_description="Invalid Customer or Account") - - total_debt_amount = sum( - loan.get("currentLoanAmount") or 0 - for loan in loans + return ResponseHelper.error( + result_description="Failed to log transaction." + ) + else: + return ResponseHelper.error( + result_description="Invalid Customer or Account" ) + # CONFIRM IF THE TOTAL DEBT IF FOR ONLY ACTIVE LOANS OR ALL LOANS + total_debt_amount = sum( + loan.get("currentLoanAmount") or 0 for loan in loans + ) + + total_outstanding_amount = sum( + loan.balance + for loan in customer_loans + if loan.status in [LoanStatus.ACTIVE, LoanStatus.START_REPAY, LoanStatus.ACTIVE_PARTIAL] + ) + + total_active_loan_amount = sum( + loan.repayment_amount + for loan in customer_loans + if loan.status in [LoanStatus.ACTIVE, LoanStatus.START_REPAY, LoanStatus.ACTIVE_PARTIAL] + ) + + total_settled_amount = sum( + loan.repayment_amount + for loan in customer_loans + if loan.status == LoanStatus.REPAID + ) + # Simulated processing logic response_data = { "customerId": customer_id, @@ -59,6 +92,11 @@ class LoanStatusService(BaseService): "transactionId": transactionId, "loans": loans, "totalDebtAmount": total_debt_amount, + "summary": { + "totalSettledAmount": total_settled_amount, + "totalOutstandingAmount": total_outstanding_amount, + "totalActiveLoanAmount": total_active_loan_amount, + } } db.session.commit() @@ -68,9 +106,11 @@ class LoanStatusService(BaseService): logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}") db.session.rollback() - return ResponseHelper.unprocessable_entity(result_description="Validation exception") - - except ValueError as err: + return ResponseHelper.unprocessable_entity( + result_description="Validation exception" + ) + + except ValueError as err: logger.error(f"{getattr(err, 'messages', str(err))}") db.session.rollback() return ResponseHelper.error(result_description=str(err)) @@ -78,4 +118,4 @@ class LoanStatusService(BaseService): except Exception as e: logger.error(f"An error occurred: {str(e)}", exc_info=True) db.session.rollback() - return ResponseHelper.internal_server_error() \ No newline at end of file + return ResponseHelper.internal_server_error() diff --git a/app/api/services/select_offer.py b/app/api/services/select_offer.py index e2908e3..c49a2a3 100644 --- a/app/api/services/select_offer.py +++ b/app/api/services/select_offer.py @@ -38,6 +38,7 @@ class SelectOfferService(BaseService): transaction_id = validated_data.get("transactionId") request_id = validated_data.get("requestId") + offer_id = int(transaction_offer_id[5:]) # The last part is int #"offerId": "SAL30001129", diff --git a/app/config.py b/app/config.py index 594be39..17af0a7 100644 --- a/app/config.py +++ b/app/config.py @@ -44,8 +44,14 @@ 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", "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") + SIMBRELLA_HEALTH = os.getenv("SIMBRELLA_ENDPOINT_RAC_CHECKS","api/system-health-check") + + + EVENTS_SERVICE_BASE_URL = os.getenv("EVENTS_SERVICE_BASE_URL","https://event-core.simbrellang.net") ENDPOINT_DIRECT_LOAN = os.getenv("ENDPOINT_DIRECT_LOAN","/autocall/direct/loan") ENDPOINT_DIRECT_REPAYMENT = os.getenv("ENDPOINT_DIRECT_REPAYMENT","/autocall/direct/repayment") diff --git a/app/swagger/digifi_swagger.json b/app/swagger/digifi_swagger.json index 69d7d19..ab4fa0b 100644 --- a/app/swagger/digifi_swagger.json +++ b/app/swagger/digifi_swagger.json @@ -103,7 +103,9 @@ "example": { "status": "ok", "db_status": "Connection Successful", - "events_service_status": "healthy", + "events_service_status":"Connection Successful", + "emulator_status":"Connection Successful", + "db_uri": "postgresql://user:****@localhost:5432/digifi_db", "error": [] } } @@ -116,7 +118,9 @@ "example": { "status": "failed", "db_status": "Connection Failed", - "events_service_status": "unhealthy", + "events_service_status":"Connection Failed", + "emulator_status":"Connection Failed", + "db_uri": "Unavailable", "error":["could not connect to server: Connection refused"] } } diff --git a/app/swagger/schemas/LoanStatusResponse.json b/app/swagger/schemas/LoanStatusResponse.json index 230c960..44d4d86 100644 --- a/app/swagger/schemas/LoanStatusResponse.json +++ b/app/swagger/schemas/LoanStatusResponse.json @@ -102,6 +102,29 @@ "type": "number", "format": "float", "example": 30000.0 + }, + "summary": { + "type": "object", + "properties": { + "totalOutstandingAmount": { + "type": "number", + "format": "float", + "example": 114450.0, + "description": "Total amount still owed across all unpaid loans." + }, + "totalActiveLoanAmount": { + "type": "number", + "format": "float", + "example": 40000.0, + "description": "Total principal amount of currently active loans." + }, + "totalSettledAmount": { + "type": "number", + "format": "float", + "example": 80000.0, + "description": "Total amount that has been fully repaid." + } + } } }, "xml": {