diff --git a/app/integrations/simbrella.py b/app/integrations/simbrella.py index 228d628..cadf101 100644 --- a/app/integrations/simbrella.py +++ b/app/integrations/simbrella.py @@ -89,7 +89,7 @@ class SimbrellaClient: logger.info(f"Disbursement response: {response.json()}") result = response.json() LoanService.set_disbursement_result(loan_data['debtId'],result.get('responseCode', ''), result.get('responseMessage', '')) - + return ResponseHelper.success(response.json(), "Successful") except Exception as e: logger.info(f"Failed to call Disbursement endpoint: {e}") return 0 @@ -98,7 +98,7 @@ class SimbrellaClient: @staticmethod def verify_transaction(data): - api_url = f"{SimbrellaClient.BANK_CALL_BASE_URL}/{SimbrellaClient.BANK_CALL_TRANSACTION_VERIFY}" + api_url = f"{SimbrellaClient.BANK_CALL_BASE_URL}{SimbrellaClient.BANK_CALL_TRANSACTION_VERIFY}" sms_url = f"{SimbrellaClient.BANK_CALL_SMS_BASE_URL}/singleSMS" logger.info(f"Calling TransactionVerify api_url==> : {api_url}") @@ -162,7 +162,7 @@ class SimbrellaClient: logger.info(f"SMS Response JSON: {result}") if result.get('isSuccess'): logger.info(f"sms sent successfully") - return ResponseHelper.success(response, "Successful") + return ResponseHelper.success(response.json(), "Successful") logger.info(f"sms failed!") return 1 except requests.RequestException as e: @@ -175,51 +175,66 @@ class SimbrellaClient: @staticmethod def collect_loan(data): - api_url = f"{SimbrellaClient.BANK_CALL_BASE_URL}/{SimbrellaClient.BANK_CALL_COLLECT_LOAN_ENDPOINT}" - logger.info(f"Calling CollectLoan api_url==> : {api_url}") + api_url = f"{SimbrellaClient.BANK_CALL_BASE_URL}{SimbrellaClient.BANK_CALL_COLLECT_LOAN_ENDPOINT}" + logger.info(f"Calling CollectLoan api_url==> : {api_url}") logger.info(f"Calling CollectLoan endpoint with data: {data}") # Check if the repayment exists logger.info(f"Checking if repayment exists") repayment = RepaymentService.get_repayment_by_transaction_id(transaction_id=data['transactionId']) logger.info(f"Repayment Response From Database ** : {repayment}") - loan = LoanService.get_loan_by_debt_id(debt_id=repayment.loan_id) - # If repayment is not found if not repayment: - logger.info(f"Repayment id: {data['transactionId']}, was not found") - return 0 + logger.info(f"Repayment with transactionId: {data['transactionId']}, was not found") + return ResponseHelper.error("Repayment not found") - repayAmount = loan.repayment_amount + repayment_data = repayment.to_dict() + loan = LoanService.get_loan_by_transaction_id(transaction_id=repayment_data['transactionId']) - collectAmount = repayAmount + # If loan is not found + if not loan: + logger.info(f"Loan with debtId: {repayment_data.loan_id}, was not found") + return ResponseHelper.error("Loan not found") + loan_data = loan.to_dict() + + if repayment_data['repayDate'] is not None: + logger.info( + f"Please call verify collection : {data['transactionId']} repayment send for processing at {repayment_data['repayDate']}") + return ResponseHelper.error("Repayment already processed") + + # let us set repay date + RepaymentService.set_repay_date(repayment_data['Id'], repayment_data['customerId']) + repayment = RepaymentService.get_repayment_by_transaction_id(transaction_id=data['transactionId']) + repayment_data = repayment.to_dict() + logger.info(f"Here is your repayment data after setting repay date: {repayment_data}") + debtId = str(loan_data.get('debtId', "")).strip().zfill(6) collect_loan_data = { - "transactionId": repayment.transaction_id, - "fbnTransactionId": loan.reference, - "debtId": repayment.loan_id, - "customerId": repayment.customer_id, - "accountId": loan.account_id, - "productId": repayment.product_id, - "collectAmount": collectAmount, - "penalCharge": 0, - "channel": "USSD", - "collectionMethod": 1, - "lienAmount": 0, - "countryId": "01", - "comment": "Testing CollectionLoanRequest" - } + "transactionId": repayment_data['transactionId'], + "fbnTransactionId": loan_data['transactionId'], + "debtId": debtId, + "customerId": repayment_data['customerId'], + "accountId": loan_data['accountId'], + "productId": repayment_data['productId'], + "collectAmount": loan_data['repaymentAmount'], + "penalCharge": 5, + "channel": "USSD", + "collectionMethod": "1", + "lienAmount": 0, + "countryId": "01", + "comment": "COLLECT LOAN" + } try: logger.info(f"Here is your CollectLoan Request data ***** : {collect_loan_data}") response = requests.post(api_url, json=collect_loan_data, headers=get_headers()) + logger.info(f"CollectLoan response: {response.json()}") - + RepaymentService.set_repay_result(repayment_data['Id'], response.json().get('responseCode', ''), response.json().get('responseMessage', '')) + return ResponseHelper.success(response.json(), "Successful") except Exception as e: logger.info(f"Failed to call CollectLoan endpoint: {e}") - return 0 - - return 1 + return ResponseHelper.error("Failed to call CollectLoan endpoint") @staticmethod diff --git a/app/models/loan.py b/app/models/loan.py index a247ad0..2fc3645 100644 --- a/app/models/loan.py +++ b/app/models/loan.py @@ -74,6 +74,7 @@ class Loan(db.Model): 'defaultPenaltyFee': self.default_penalty_fee, 'continuousFee': self.continuous_fee, 'collectionType': self.collection_type, + 'repaymentAmount':self.repayment_amount, 'status': self.status, 'productId': self.product_id, 'disburseResult': self.disburse_result, diff --git a/app/models/repayment.py b/app/models/repayment.py index bc18452..3ea9db5 100644 --- a/app/models/repayment.py +++ b/app/models/repayment.py @@ -1,5 +1,6 @@ from app.extensions import db from datetime import datetime, timezone +from app.utils.logger import logger class Repayment(db.Model): __tablename__ = "repayments" @@ -15,10 +16,158 @@ class Repayment(db.Model): transaction_id = db.Column(db.String(50), nullable=False) 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)) - + repay_date = db.Column(db.DateTime, nullable=True) + verify_date = db.Column(db.DateTime, nullable=True) + repay_result = db.Column(db.String(10), nullable=True) + repay_description = db.Column(db.String(100), nullable=True) + verify_result = db.Column(db.String(10), nullable=True) + verify_description = db.Column(db.String(100), nullable=True) def __repr__(self): return f'' + + def to_dict(self): + """ + Convert the Repayment object to a dictionary format for JSON serialization. + """ + return { + 'Id': self.id, + "customerId": self.customer_id, + 'loanId': self.loan_id, + 'productId': self.product_id, + 'repayResult': self.repay_result, + 'repayDescription': self.repay_description, + 'verifyResult': self.verify_result, + 'verifyDescription': self.verify_description, + 'transactionId': self.transaction_id, + 'repayDate': self.repay_date.isoformat() if self.repay_date else None, + 'VerifyDate': self.verify_date.isoformat() if self.verify_date else None, + } + + @classmethod def get_repayment_by_transaction_id(cls, transaction_id): - return cls.query.filter_by(transaction_id=transaction_id).first() \ No newline at end of file + return cls.query.filter_by(transaction_id=transaction_id).first() + + @classmethod + def set_repay_date(cls, repayment_id, customer_id): + """ + Update the repay date of the loan with the given loan_id. + """ + # Retrieve repayment + repayment = cls.query.get(repayment_id) + + if not repayment: + raise ValueError(f"repayment with ID {repayment_id} does not exist.") + + # Check if customer_id matches + if repayment.customer_id != customer_id: + raise ValueError(f"Customer ID {customer_id} does not match the repayment's customer ID.") + + current_time = datetime.now() + logger.info(f"What is now ======= ==== ==> : {current_time}") + # Update repayment date + repayment.repay_date = current_time + + # Commit changes to database + try: + logger.info(f"Updating repay date for repayment ID {repayment_id} to {current_time}") + db.session.commit() + except Exception as e: + db.session.rollback() + logger.error(f"Failed to update repay date: {e}") + raise + @classmethod + def set_repay_verify_date(cls, repayment_id, customer_id): + """ + Update the repayment verify date of the loan with the given repayment_id. + """ + # Retrieve repayment + repayment = cls.query.get(repayment_id) + + if not repayment: + raise ValueError(f"repayment with ID {repayment_id} does not exist.") + + # Check if customer_id matches + if repayment.customer_id != customer_id: + raise ValueError(f"Customer ID {customer_id} does not match the repayment's customer ID.") + + current_time = datetime.now() + logger.info(f"What is now ======= ==== ==> : {current_time}") + # Update repayment verify_date + repayment.verify_date = current_time + + # Commit changes to database + try: + logger.info(f"Updating repay verify date for repayment ID {repayment_id} to {current_time}") + db.session.commit() + except Exception as e: + db.session.rollback() + logger.error(f"Failed to update repay verify date: {e}") + raise + + + @classmethod + def set_repay_result(cls, repayment_id, result, description): + """ + Update the repayment result and description of the repayment with the given repayment_id. + """ + # Retrieve loan + repayment = cls.query.get(repayment_id) + + if not repayment: + raise ValueError(f"repayment with ID {repayment_id} does not exist.") + + # Update repayment result and description + repayment.repay_result = result + repayment.repay_description = description + + # Commit changes to database + try: + logger.info(f"Updating repayment result for repayment ID {repayment_id} to {result} with description {description}") + db.session.commit() + except Exception as e: + db.session.rollback() + logger.error(f"Failed to update repayment result: {e}") + raise + @classmethod + def set_verify_date_result(cls, repayment_id, result, description): + """ + Update the verify result and description of the repayment with the given repayment_id. + """ + # Retrieve repayment + repayment = cls.query.get(repayment_id) + + if not repayment: + raise ValueError(f"repayment with ID {repayment_id} does not exist.") + + # Update disburse result and description + repayment.verify_result = result + repayment.verify_description = description + + # Commit changes to database + try: + logger.info(f"Updating verify result for repayment ID {repayment_id} to {result} with description {description}") + db.session.commit() + except Exception as e: + db.session.rollback() + logger.error(f"Failed to update verify result: {e}") + raise + @classmethod + def get_latest_repayment_without_repay_date(cls): + """ + Get the latest repayment without a repay date. + """ + return cls.query.filter( + cls.repay_date.is_(None) + ).order_by(cls.created_at.desc()).first() + + @classmethod + def get_latest_loan_with_repay_date(cls): + """ + Get the latest repayment with a repay date and no verification date. + """ + return cls.query.filter( + cls.repay_date.isnot(None), + cls.verify_date.is_(None) + ).order_by(cls.created_at.desc()).first() \ No newline at end of file diff --git a/app/routes/autocall.py b/app/routes/autocall.py index 68d2dc3..968bea9 100644 --- a/app/routes/autocall.py +++ b/app/routes/autocall.py @@ -6,6 +6,7 @@ from app.utils.auth import get_headers from app.utils.logger import logger from app.integrations.simbrella import SimbrellaClient from app.services.loan import LoanService +from app.services.repayment import RepaymentService autocall_bp = Blueprint("autocall", __name__) @@ -65,17 +66,31 @@ def refresh_verify_collection(): return response - @autocall_bp.route("/refresh-collection", methods=["GET"]) def refresh_collection(): - data = request.get_json() + #data = request.get_json() logger.info(f"Calling Collection ") + #grab the last repayments with repay date is none + repayment = RepaymentService.get_latest_repayment_without_repay_date() + if not repayment: + logger.info(f"No repayment found without disbursement date") + return 0 + logger.info(f"Calling repay loan endpoint with data: {repayment}") + repayment_data = repayment.to_dict() + logger.info(f"here is the dict form of repayment {repayment_data}") + data = { + "transactionId": repayment_data['transactionId'], + "debtId": repayment_data['loanId'], + "customerId": repayment_data['customerId'], + "productId": repayment_data['productId'], + } + logger.info(f"Data being sent to Simbrella: {data}") + logger.info(f"calling simbrella") response = SimbrellaClient.collect_loan(data) return response - @autocall_bp.route("/payment-callback", methods=["POST"]) def payment_callback(): data = request.get_json() diff --git a/app/services/repayment.py b/app/services/repayment.py index b2249ff..f4baece 100644 --- a/app/services/repayment.py +++ b/app/services/repayment.py @@ -7,4 +7,46 @@ class RepaymentService: """ Get the repayment by transaction ID """ - return Repayment.get_repayment_by_transaction_id(transaction_id) \ No newline at end of file + return Repayment.get_repayment_by_transaction_id(transaction_id) + + @classmethod + def set_repay_date(cls, repayment_id, customer_id): + """ + Update the repay status of the repayment with the given repayment_id. + """ + return Repayment.set_repay_date(repayment_id, customer_id) + + @classmethod + def set_repay_verify_date(cls, repayment_id, customer_id): + """ + Update the verify date of the repayment with the given repayment_id. + """ + return Repayment.set_repay_verify_date(repayment_id, customer_id) + + @classmethod + def set_repay_result(cls, repayment_id, result, description): + """ + Update the repay result of the repayment with the given repayment_id. + """ + return Repayment.set_repay_result(repayment_id, result, description) + + @classmethod + def set_verify_date_result(cls, repayment_id, result, description): + """ + Update the verify result of the repayment with the given repayment_id. + """ + return Repayment.set_verify_date_result(repayment_id, result, description) + + @classmethod + def get_latest_repayment_without_repay_date(cls): + """ + Get the latest repayment without a repay date. + """ + return Repayment.get_latest_repayment_without_repay_date() + + @classmethod + def get_latest_loan_with_repay_date(cls): + """ + Get the latest repayment with a repay date and no verification date. + """ + return Repayment.get_latest_loan_with_repay_date() \ No newline at end of file