From 7d691db7a5a3093375708b1c7b93b6f3c5e2a943 Mon Sep 17 00:00:00 2001 From: VivianDee <115420678+VivianDee@users.noreply.github.com> Date: Wed, 23 Apr 2025 20:59:07 +0100 Subject: [PATCH] [update]: Select Offer --- app/api/integrations/simbrella.py | 6 +-- app/api/services/select_offer.py | 88 +++++++++++++++++++++---------- app/models/rac_checks.py | 12 +++++ requirements.txt | 4 ++ 4 files changed, 77 insertions(+), 33 deletions(-) create mode 100644 app/models/rac_checks.py diff --git a/app/api/integrations/simbrella.py b/app/api/integrations/simbrella.py index e0e2b96..d46e5d4 100644 --- a/app/api/integrations/simbrella.py +++ b/app/api/integrations/simbrella.py @@ -49,10 +49,8 @@ class SimbrellaIntegration: logger.error(f"This is Response: {str(response)}", exc_info=True) return response + 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)}") - # return httpx.Response( - # status_code=200, - # json={} - # ) + diff --git a/app/api/services/select_offer.py b/app/api/services/select_offer.py index 8021ca3..a207f2b 100644 --- a/app/api/services/select_offer.py +++ b/app/api/services/select_offer.py @@ -6,7 +6,8 @@ from app.utils.logger import logger from app.api.schemas.select_offer import SelectOfferSchema from app.extensions import db from app.models import Offer - +from datetime import date +from dateutil.relativedelta import relativedelta class SelectOfferService(BaseService): TRANSACTION_TYPE = TransactionType.SELECT_OFFER @@ -29,8 +30,10 @@ class SelectOfferService(BaseService): ) account_id = validated_data.get("accountId") customer_id = validated_data.get("customerId") - amount = validated_data.get("amount") + amount = validated_data.get("requestedAmount") product_id = validated_data.get("productId") + transaction_id = validated_data.get("transactionId") + request_id = validated_data.get("requestId") if SelectOfferService.validate_account_ownership( account_id=account_id, customer_id=customer_id @@ -60,52 +63,57 @@ class SelectOfferService(BaseService): logger.error(f"{loan_charges}") - fees_and_dues = { - charge.code: { - "rate": charge.percent, - "fee": amount * charge.percent / 100, - "due_days": charge.due, - } - for charge in loan_charges - } + db.session.flush() + interest = SelectOfferService.get_charge_detail(loan_charges, "INTEREST", amount) + management = SelectOfferService.get_charge_detail(loan_charges, "MGTFEE", amount) + insurance = SelectOfferService.get_charge_detail(loan_charges, "INSURANCE", amount) + vat = SelectOfferService.get_charge_detail(loan_charges, "VAT", amount) + + + # Up-front payment: (principal + only those fees due immediately i.e due_days == 0) + upfront_payment = amount + sum( + amount * charge.percent / 100 + for charge in loan_charges + if charge.due == 0 + ) # Total amount (principal + all fees) - total_amount = amount + sum(item["fee"] for item in fees_and_dues.values()) - - # Up-front payment: only those fees due immediately (due_days == 0) - upfront_payment = sum( - item["fee"] - for item in fees_and_dues.values() - if item["due_days"] == 0 + total_amount = amount + sum( + amount * charge.percent / 100 + for charge in loan_charges ) + # Calculate the repayment dates tenor = offer.tenor start_date = date.today() + # Convert tenor to months + months = tenor // 30 + recommended_repayment_dates = [ (start_date + relativedelta(months=i + 1)).isoformat() - for i in range(tenor) + for i in range(months) ] - # Calculate the installment amount + # Calculate the installment amount installment_amount = total_amount / tenor offers = [ { - "offerId": Offer.id, + "offerId": offer.id, "productId": product_id, "amount": amount, "upfrontPayment": upfront_payment, - "interestRate": fees_and_dues["INTEREST"]["rate"], - "managementRate": fees_and_dues["MGTFEE"]["rate"], - "managementFee": fees_and_dues["MGTFEE"]["fee"], - "insuranceRate": fees_and_dues["INSURANCE"]["rate"], - "insuranceFee": fees_and_dues["INSURANCE"]["fee"], - "VATRate": fees_and_dues["VAT"]["rate"], - "VATAmount": fees_and_dues["VAT"]["fee"], + "interestRate": interest["rate"], + "managementRate": management["rate"], + "managementFee": management["fee"], + "insuranceRate": insurance["rate"], + "insuranceFee": insurance["fee"], + "VATRate": vat["rate"], + "VATAmount": vat["fee"], "recommendedRepaymentDates": recommended_repayment_dates, "installmentAmount": installment_amount, "totalRepaymentAmount": total_amount, @@ -115,8 +123,8 @@ class SelectOfferService(BaseService): # Business logic - selecting an offer response_data = { "outstandingDebtAmount": 0, - "requestId": "202111170001371256908", - "transactionId": transaction.id, + "requestId": request_id, + "transactionId": transaction_id, "customerId": customer_id, "accountId": account_id, "loan": offers, @@ -142,3 +150,25 @@ class SelectOfferService(BaseService): logger.error(f"An error occurred: {str(e)}", exc_info=True) db.session.rollback() return jsonify({"message": "Internal Server Error"}), 500 + + + + @staticmethod + def get_charge_detail(charges, code, amount): + """ + Get details for a specific charge code from a list of loan charges. + + Returns default values if not found. + """ + + + for charge in charges: + if charge.code == code: + return { + "rate": charge.percent, + "fee": amount * charge.percent / 100, + "due_days": charge.due, + } + + return {"rate": 0, "fee": 0, "due_days": 0} + diff --git a/app/models/rac_checks.py b/app/models/rac_checks.py new file mode 100644 index 0000000..9e33a84 --- /dev/null +++ b/app/models/rac_checks.py @@ -0,0 +1,12 @@ +from datetime import datetime, timezone +from app.extensions import db + +class RACCheck(Base): + __tablename__ = "rac_checks" + + id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4) + transaction_id = Column(UUID, ForeignKey('transactions.id'), nullable=False) + customer_id = Column(String, nullable=False) + account_id = Column(String, nullable=False) + rac_response = Column(JSON, nullable=False) + created_at = Column(DateTime, default=datetime.utcnow) diff --git a/requirements.txt b/requirements.txt index 95d8b87..124ae97 100644 --- a/requirements.txt +++ b/requirements.txt @@ -35,3 +35,7 @@ flask-jwt-extended # Kafka confluent-kafka==1.9.2 + + +python-dateutil>=2.8.0 + -- 2.34.1