From db0ef88e8755926a3edba85bc9b6f2b8813ea1bd Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Mon, 10 Nov 2025 14:58:34 +0530 Subject: [PATCH] Added the mailling after creating the payment requests --- .../payment_request_conversion.py | 150 +++++++++++++++--- 1 file changed, 129 insertions(+), 21 deletions(-) diff --git a/mailling/recurring-template/payment_request_conversion.py b/mailling/recurring-template/payment_request_conversion.py index ccbd202..e929ea3 100644 --- a/mailling/recurring-template/payment_request_conversion.py +++ b/mailling/recurring-template/payment_request_conversion.py @@ -2,16 +2,25 @@ import json import requests import mysql.connector import logging +import smtplib +from email.message import EmailMessage + + +# Configure logging with timestamp, level, and message format +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(levelname)s - %(message)s" +) -# Configure logging -logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') def login_api(): """Authenticate and get JWT token from login API.""" payload = {"username": API_USERNAME, "password": API_PASSWORD} headers = {"Content-Type": "application/json"} try: - response = requests.post(f"{BASE_URL}/auth/login", json=payload, headers=headers) + response = requests.post( + f"{BASE_URL}/auth/login", json=payload, headers=headers + ) response.raise_for_status() data = response.json().get("data", {}) jwt = data.get("token") @@ -25,11 +34,14 @@ def login_api(): logging.error(f"Login API error: {e}") return None + def select_tenant(jwt): """Select tenant and retrieve a new JWT token.""" headers = {"Authorization": f"Bearer {jwt}", "Content-Type": "application/json"} try: - response = requests.post(f"{BASE_URL}/auth/select-tenant/{API_TENANT}", headers=headers) + response = requests.post( + f"{BASE_URL}/auth/select-tenant/{API_TENANT}", headers=headers + ) response.raise_for_status() data = response.json().get("data", {}) jwt = data.get("token") @@ -43,16 +55,20 @@ def select_tenant(jwt): logging.error(f"Select tenant error: {e}") return None + def get_recurring_templates(cursor): - """Retrieve recurring payment templates that need processing.""" + """Retrieve recurring payment templates scheduled for today which are active.""" try: - val = ("da462422-13b2-45cc-a175-910a225f6fc8",) query = """ SELECT Id FROM RecurringPayments - WHERE StatusId = %s - AND (LatestPRGeneratedAt IS NULL OR DATE(LatestPRGeneratedAt) <> CURDATE()) + WHERE StatusId = %s + AND NextStrikeDate IS NOT NULL + AND DATE(NextStrikeDate) = CURDATE() + AND DATE(EndDate) >= CURDATE() + AND IsActive IS TRUE """ + val = ("da462422-13b2-45cc-a175-910a225f6fc8",) cursor.execute(query, val) result = cursor.fetchall() id_list = [row[0] for row in result] @@ -62,11 +78,12 @@ def get_recurring_templates(cursor): logging.error(f"MySQL query error: {e}") return [] + def convert_recurring_templates(jwt, recurring_template_ids): - """Convert each recurring template to a payment request via API.""" + """Convert recurring payment templates to payment requests via API.""" if not recurring_template_ids: logging.info("No recurring templates to process.") - return + return [] payload = {"recurringTemplateIds": recurring_template_ids} headers = {"Authorization": f"Bearer {jwt}", "Content-Type": "application/json"} @@ -74,28 +91,116 @@ def convert_recurring_templates(jwt, recurring_template_ids): try: response = requests.post( f"{BASE_URL}/expense/recurring-payment/convert/payment-request", - json=payload, headers=headers + json=payload, + headers=headers, ) response.raise_for_status() message = response.json().get("message", "Conversion successful.") + data = response.json().get("data", []) logging.info(f"Conversion response: {message}") + return data except requests.HTTPError as http_err: status_code = http_err.response.status_code try: - error_message = http_err.response.json().get("message", http_err.response.text) + error_message = http_err.response.json().get( + "message", http_err.response.text + ) except Exception: error_message = http_err.response.text logging.error(f"HTTP error {status_code}: {error_message}") + return [] except requests.RequestException as e: logging.error(f"Request error: {e}") + return [] except Exception as e: logging.error(f"Unexpected error during conversion: {e}") + return [] + + +def get_frequency(frequency_code): + """Translate frequency code to human-readable frequency string.""" + mapping = { + 0: "Monthly", + 1: "Quarterly", + 2: "Half-Yearly", + 3: "Yearly", + 4: "Daily", + 5: "Weekly", + } + return mapping.get(frequency_code, "Unknown") + + +def send_email_notifications(email_config, recurring_payment_data): + """Send email notifications for generated payment requests.""" + SMPTSERVER = email_config.get("SMPTSERVER") + PORT = email_config.get("PORT") + SENDER_EMAIL = email_config.get("SENDER_EMAIL") + SENDER_PASSWORD = email_config.get("SENDER_PASSWORD") + + if not recurring_payment_data: + logging.info("No payment request data to send emails for.") + return + + try: + with smtplib.SMTP(SMPTSERVER, PORT) as smtp: + smtp.ehlo() + smtp.starttls() + smtp.ehlo() + smtp.login(SENDER_EMAIL, SENDER_PASSWORD) + + for item in recurring_payment_data: + recurringPayment = item.get("recurringPayment", {}) + emails = item.get("emails") + + if not emails: + logging.warning("No recipient emails found for one of the payment requests.") + continue + + next_strike_date = recurringPayment.get("nextStrikeDate", "") + formatted_next_strike_date = next_strike_date[:10] if next_strike_date else "N/A" + + frequency = get_frequency(recurringPayment.get("frequency")) + + # Compose email content + content = f""" +Here are the details of the payment: + +- Title: {recurringPayment.get('title', 'N/A')} +- Description: {recurringPayment.get('description', 'N/A')} +- Payee: {recurringPayment.get('payee', 'N/A')} +- Amount: {recurringPayment.get('currency', {}).get('symbol', '')} {recurringPayment.get('amount', 'N/A')} ({recurringPayment.get('currency', {}).get('currencyName', 'N/A')}) +- Currency Code: {recurringPayment.get('currency', {}).get('currencyCode', 'N/A')} +- Recurring Frequency: {frequency} +- Next Strike Date: {formatted_next_strike_date} +- Strike Date: {recurringPayment.get('strikeDate', '')[:10] if recurringPayment.get('strikeDate') else 'N/A'} +- End Date: {recurringPayment.get('endDate', '')[:10] if recurringPayment.get('endDate') else 'N/A'} +- Expense Category: {recurringPayment.get('expenseCategory', {}).get('name', 'N/A')} +- Status: {recurringPayment.get('status', {}).get('name', 'N/A')} + +Please ensure timely processing. +""" + # Setup EmailMessage + msg = EmailMessage() + msg["Subject"] = f"Payment Request Generated from Recurring Payment Template \"{recurringPayment.get('title', '')}\"" + msg["From"] = SENDER_EMAIL + msg["To"] = emails + msg.set_content(content) + + try: + smtp.send_message(msg) + logging.info(f"Email sent successfully to {emails}.") + except Exception as email_err: + logging.error(f"Failed to send email to {emails}: {email_err}") + + except Exception as e: + logging.error(f"SMTP connection or login error: {e}") + # --- Main execution logic --- if __name__ == "__main__": import sys - # Load configuration + # Load configuration from JSON file GLOBAL_CONFIG_PATH = "config.json" try: with open(GLOBAL_CONFIG_PATH, "r", encoding="utf-8") as f: @@ -105,7 +210,7 @@ if __name__ == "__main__": logging.critical(f"Failed to load config: {e}") sys.exit(1) - # Extract config parameters + # Extract essential API config parameters API_CONFIG = config.get("API", {}) BASE_URL = API_CONFIG.get("BASE_URL") API_USERNAME = API_CONFIG.get("USERNAME") @@ -119,9 +224,8 @@ if __name__ == "__main__": host=MYSQL_CONFIG.get("DB_HOST"), user=MYSQL_CONFIG.get("DB_USER"), password=MYSQL_CONFIG.get("DB_PASSWORD"), - database=MYSQL_CONFIG.get("DB_NAME") + database=MYSQL_CONFIG.get("DB_NAME"), ) - # Create cursor mycursor = mydb.cursor() logging.info("Database connection established.") except mysql.connector.Error as e: @@ -134,19 +238,23 @@ if __name__ == "__main__": logging.critical("Login failed, aborting.") sys.exit(1) - # Select tenant, get tenant-specific token + # Select tenant and get tenant-specific JWT token jwt_token = select_tenant(token) if not jwt_token: logging.critical("Tenant selection failed, aborting.") sys.exit(1) - # Fetch recurring payment templates to process + # Fetch recurring payment template IDs for processing recurring_ids = get_recurring_templates(mycursor) - # Close DB resources + # Close DB resources gracefully mycursor.close() mydb.close() logging.info("Database connection closed.") - # Process the templates - convert_recurring_templates(jwt_token, recurring_ids) + # Convert recurring templates to payment requests via API + response_data = convert_recurring_templates(jwt_token, recurring_ids) + + # Send email notifications for generated payment requests + EMAIL_CONFIG = config.get("SMPT", {}) + send_email_notifications(EMAIL_CONFIG, response_data)