Monthly Email Notification

I have created function to send monthly email notification and It is triggered by sheduler events

My code

import frappe
from frappe.utils import nowdate, getdate

class RentPayment:

@staticmethod
def send_rent_reminders():
    today = getdate(nowdate())
    current_month = today.strftime("%B")
    current_year = today.strftime("%Y")
    print(today)
    print(current_month)
    print(current_year)

    # Query unpaid rent records for the current month
    unpaid_rent_records = frappe.get_all("Rent Payment", 
        filters={
            "month": current_month,
            "status": "Unpaid"
        },
        fields=["name", "tenant", "email_id", "payment_amount", "payment_date", "contract_details"]
    )

    records_to_notify = []
    
    # Loop through unpaid records and check linked Contract Details for notifications
    for rent in unpaid_rent_records:
        contract_details = rent.get("contract_details")
        
        # Fetch the notify field from Contract Details
        if contract_details:
            contract = frappe.get_doc("Contract Details", contract_details)
            
            # Only notify if the contract has notifications enabled
            if contract.notify:
                records_to_notify.append(rent)

    # Loop through filtered records and send email reminders
    for rent in records_to_notify:
        tenant = rent.get("tenant")
        email = rent.get("email_id")
        amount = rent.get("payment_amount")
        shop = rent.get("shop")  # Assuming shop is also fetched if needed

        if not email or "@" not in email:
            frappe.log_error(f"Invalid email for tenant {tenant}: {email}", "Email Validation Error")
            continue

        subject = f"Rent Payment Reminder for {shop} - {current_month} {current_year}"
        message = f"Dear {tenant},<br><br>"
        message += f"This is a reminder that your rent of {amount} is due for the month of {current_month} {current_year}."
        message += "<br>Please make the payment at your earliest convenience.<br><br>Thank you!"

        try:
            frappe.sendmail(recipients=email, subject=subject, message=message)
            frappe.logger().info(f"Sent rent reminder to {tenant} at {email}")
        except Exception as e:
            frappe.log_error(f"Failed to send email to {tenant} at {email}: {str(e)}", "Email Sending Error")

    frappe.db.commit()

In hooks.py file i have creaed shedular event

scheduler_events = {
“monthly”: [
“airplane_mode.airport_shop_management.doctype.rent_payment.rent_payment.RentPayment.send_rent_reminders”
]
}

When i was testing this in console
i got some error
then i used bench migrate command
after migrate my payment doctype is deleted i can’t see in doctype list
it is autometically deleted from there

I also check emai accounts
And during testing accounts emaill were sending successfully
But using sheduler events i did’t get single email

You can check log of functions whihc is ran through schedular in Scheduled Job Type Doctype
image

If payment is a custom doctype and you haven’t exported/created it in your custom app, you will lose your changes.