Frappe Server ScriptPython

How to Send Email from a Server Script on Button Click in Frappe?

Send Email on Button Click

Learn how to create a Frappe server script to send customized emails to a list of recipients from a child table when a custom button is clicked in a DocType.

This server-side script demonstrates a common business requirement in Frappe/ERPNext: sending bulk, personalized emails based on data within a document, triggered by a user action like a button click. This pattern is highly effective for sending event invitations, status notifications, or alerts to a list of stakeholders defined in a child table.

The code iterates through a child table to gather recipient emails, constructs a dynamic subject line, and utilizes Frappe's built-in `frappe.sendmail` utility to dispatch the emails.

python
1# In [doctype_name].py
2import frappe
3from frappe.model.document import Document
4
5class YourDocType(Document):
6 @frappe.whitelist()
7 def send_internal_invitation(self):
8 subject = "Invitation: '" + self.title + "' on date: " + self.get_formatted('date')
9
10 # 'meeting_company' is the fieldname of the child table
11 for row in self.meeting_company:
12 frappe.sendmail(
13 recipients=[row.user_id], # 'user_id' is a field in the child table holding the email
14 sender=frappe.session.user,
15 subject=subject,
16 message=self.invitation_message, # 'invitation_message' is a field in the parent DocType
17 reference_doctype=self.doctype,
18 reference_name=self.name
19 )
20
21 self.status = "Invitation Sent"
22 self.save()
23 frappe.db.commit()
24 frappe.msgprint("Invitation Sent to Company Representatives")

Understanding This Code

What It Does

This server-side Python method sends an email to each user listed in a child table of a DocType. It's triggered by a custom button on the DocType form.

When To Use

Use this script when you need to perform a bulk action, like sending notifications, to multiple related records (stored in a child table) with a single click from the parent document form.

Prerequisites

  • A parent DocType with a custom button.
  • A child table within the parent DocType containing a field for the recipient's email address (e.g., a field of type 'Data' with option 'Email', or a Link to User).
  • The method must be whitelisted using `@frappe.whitelist()`.

Key Concepts

Important ideas to understand in this code

@frappe.whitelist()

A security decorator in Frappe that marks a Python method as callable from the client-side (e.g., through a UI button click or an API call). Any method intended to be triggered from the user interface must be whitelisted.

Learn more

frappe.sendmail

A high-level Frappe API for sending emails. It simplifies the process by handling email queueing and using the system's default email settings. It takes parameters like recipients, subject, message, and optional references.

Learn more

Child Table Iteration

Child Tables in Frappe are stored as a list of document objects on the parent document. You can iterate through this list using a standard Python 'for' loop to access the data in each row of the child table.

Learn more

self.save() and frappe.db.commit()

`self.save()` saves the document's current state within the ongoing database transaction. `frappe.db.commit()` permanently writes that transaction to the database. Committing ensures the status change is immediately visible to all users.

Learn more

Step-by-Step Tutorial

Follow along to understand how this code works

1

Define the Server-Side Method

In your custom app, locate your DocType's Python file (e.g., `my_app/doctype/my_doctype/my_doctype.py`). Add the method inside the DocType's class and make sure to decorate it with `@frappe.whitelist()`.

python
import frappe
from frappe.model.document import Document

class YourDocType(Document):
    @frappe.whitelist()
    def send_internal_invitation(self):
        # ... implementation from above ...
Next Step
2

Create a Custom Button in the DocType

Navigate to 'DocType List', open your DocType, and scroll down to the 'Actions' section to add a new button. Give it a descriptive 'Label', like 'Send Invitation'.

javascript
// This is a UI configuration step, not code.
// 1. Go to 'DocType List' and select your DocType.
// 2. In the settings, scroll to the 'Actions' or 'Buttons' grid.
// 3. Add a new row and set the 'Label' for your button.
Next Step
3

Link the Button to the Method

To link the button to your server script, you need to create a simple Client Script that calls the whitelisted method.

javascript
// Create a new Client Script for your DocType
frappe.ui.form.on('YourDocType', {
    refresh: function(frm) {
        // Add the button if it doesn't exist
        frm.add_custom_button(__('Send Invitation'), function() {
            // Call the server-side method
            frappe.call({
                method: 'your_app.doctype.your_doctype.your_doctype.send_internal_invitation',
                args: {
                    doc_name: frm.doc.name
                },
                callback: function(r) {
                    if (!r.exc) {
                        frm.reload_doc();
                    }
                }
            });
        });
    }
});

Common Issues & Solutions

Troubleshoot problems you might encounter