from flask import request, jsonify from marshmallow import ValidationError from app.api.helpers.response_helper import ResponseHelper from app.api.services.base_service import BaseService from app.api.enums import TransactionType from app.models.transaction_offers import TransactionOffer 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 @staticmethod def process_request(data): """ Process the SelectOffer request. Args: data (dict): The request data. Returns: dict: A standardized response. """ try: with db.session.begin(): validated_data = SelectOfferService.validate_data( data, SelectOfferSchema() ) account_id = validated_data.get("accountId") customer_id = validated_data.get("customerId") amount = validated_data.get("requestedAmount") product_id = validated_data.get("productId") transaction_offer_id = validated_data.get("offerId") 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", 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 ResponseHelper.error(result_description="Failed to log transaction.") else: return ResponseHelper.error(result_description="Invalid Customer or Account") # Get the offer by product ID offer = Offer.get_offer_by_product_id(product_id) transaction_offer = TransactionOffer.get_transaction_offer(transaction_offer_id=offer_id) if not transaction_offer: logger.error(f"offer {offer_id} not found for customer {customer_id} and transaction {transaction_id}.") return ResponseHelper.error(result_description="Offer not found.") db.session.flush() if amount < transaction_offer.min_amount: logger.error(f"The amount {amount} is less than the minimum allowed offer amount {transaction_offer.min_amount}.") return ResponseHelper.error(result_description="The amount is less than the minimum allowed offer amount.") elif amount > transaction_offer.eligible_amount: logger.error(f"The amount {amount} is greater than the eligible offer amount {transaction_offer.eligible_amount}.") return ResponseHelper.error(result_description="The amount is greater than the eligible offer amount.") charges = SelectOfferService.calculate_charges(offer, amount) upfront_payment = charges["upfront_payment"] total_amount = charges["total_amount"] installment_amount = charges["installment_amount"] interest = charges["interest"] management = charges["management"] insurance = charges["insurance"] vat = charges["vat"] repayment_amount = charges["repayment_amount"] interest_amount = charges["interest_amount"] # Calculate the repayment dates tenor = offer.tenor start_date = date.today() # Convert tenor to months months = offer.schedule # tenor // 30 recommended_repayment_dates = [ (start_date + relativedelta(months=i + 1)).isoformat() for i in range(months) ] offers = [ { "offerId": transaction_offer_id, "productId": product_id, "amount": amount, "upfrontPayment": upfront_payment, "interestRate": offer.interest_rate, "interestFee": interest_amount, "managementRate": offer.management_rate, "managementFee": management["fee"], "insuranceRate": offer.insurance_rate, "insuranceFee": insurance["fee"], "VATRate": offer.vat_rate, "VATAmount": vat["fee"], "recommendedRepaymentDates": recommended_repayment_dates, "repaymentAmount": repayment_amount, "installmentAmount": installment_amount, "totalRepaymentAmount": total_amount, } ] # "offerId": offer.id, # "productId": product_id, # "amount": amount, # "upfrontPayment": upfront_payment, # "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, # # Business logic - selecting an offer response_data = { "outstandingDebtAmount": 0, "requestId": request_id, "transactionId": transaction_id, "customerId": customer_id, "accountId": account_id, "loan": offers, } db.session.commit() return ResponseHelper.success(data=response_data) except ValidationError as err: 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: logger.error(f"{getattr(err, 'messages', str(err))}") db.session.rollback() return ResponseHelper.error(result_description=str(err)) except Exception as e: logger.error(f"An error occurred: {str(e)}", exc_info=True) db.session.rollback() return ResponseHelper.internal_server_error()