feat: Email Campaign

This commit is contained in:
Rucha Mahabal
2019-07-09 15:14:13 +05:30
parent 162f7d1b50
commit 36963a8e04
4 changed files with 155 additions and 53 deletions

View File

@@ -7,20 +7,23 @@
"field_order": [ "field_order": [
"campaign_section", "campaign_section",
"campaign_name", "campaign_name",
"lead", "email_campaign_for",
"column_break_4",
"start_date", "start_date",
"column_break_4",
"sender",
"recipient",
"end_date",
"status", "status",
"email_schedule_section", "email_schedule_section",
"email_schedule", "email_schedule",
"naming_series", "unsubscribed",
"amended_from" "naming_series"
], ],
"fields": [ "fields": [
{ {
"fieldname": "campaign_section", "fieldname": "campaign_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "CAMPAIGN " "label": "Campaign"
}, },
{ {
"fieldname": "campaign_name", "fieldname": "campaign_name",
@@ -31,19 +34,10 @@
"reqd": 1 "reqd": 1
}, },
{ {
"fieldname": "lead",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Lead",
"options": "Lead",
"reqd": 1
},
{
"default": "Started",
"fieldname": "status", "fieldname": "status",
"fieldtype": "Select", "fieldtype": "Select",
"label": "Status", "label": "Status",
"options": "\nDraft\nSubmitted\nStarted\nIn Progress\nCompleted" "options": "\nScheduled\nIn Progress\nCompleted\nUnsubscribed"
}, },
{ {
"fieldname": "column_break_4", "fieldname": "column_break_4",
@@ -58,7 +52,7 @@
{ {
"fieldname": "email_schedule_section", "fieldname": "email_schedule_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "EMAIL SCHEDULE" "label": "Email Schedule"
}, },
{ {
"fieldname": "email_schedule", "fieldname": "email_schedule",
@@ -75,17 +69,41 @@
"reqd": 1 "reqd": 1
}, },
{ {
"fieldname": "amended_from", "fieldname": "end_date",
"fieldtype": "Link", "fieldtype": "Date",
"label": "Amended From", "label": "End Date",
"no_copy": 1,
"options": "Email Campaign",
"print_hide": 1,
"read_only": 1 "read_only": 1
},
{
"default": "Lead",
"fieldname": "email_campaign_for",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Email Campaign For ",
"options": "\nLead\nContact"
},
{
"fieldname": "recipient",
"fieldtype": "Dynamic Link",
"label": "Recipient",
"options": "email_campaign_for",
"reqd": 1
},
{
"default": "__user",
"fieldname": "sender",
"fieldtype": "Link",
"label": "Sender",
"options": "User"
},
{
"default": "0",
"fieldname": "unsubscribed",
"fieldtype": "Check",
"label": "Unsubscribed"
} }
], ],
"is_submittable": 1, "modified": "2019-07-09 15:07:03.328591",
"modified": "2019-06-30 23:00:24.765312",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "CRM", "module": "CRM",
"name": "Email Campaign", "name": "Email Campaign",

View File

@@ -5,14 +5,18 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import getdate, add_days, nowdate from frappe.utils import getdate, add_days, today, nowdate, cstr
from frappe.model.document import Document from frappe.model.document import Document
from frappe.email.inbox import link_communication_to_document from frappe.core.doctype.communication.email import make
class EmailCampaign(Document): class EmailCampaign(Document):
def validate(self): def validate(self):
self.validate_dates() self.validate_dates()
self.validate_lead() #checking if email is set for lead. Not checking for contact as email is a mandatory field for contact.
if self.email_campaign_for == "Lead":
self.validate_lead()
self.set_end_date()
self.update_status()
def validate_dates(self): def validate_dates(self):
campaign = frappe.get_doc("Campaign", self.campaign_name) campaign = frappe.get_doc("Campaign", self.campaign_name)
@@ -30,37 +34,105 @@ class EmailCampaign(Document):
frappe.throw(_("Email Schedule cannot extend Campaign End Date")) frappe.throw(_("Email Schedule cannot extend Campaign End Date"))
def validate_lead(self): def validate_lead(self):
lead = frappe.get_doc("Lead", self.lead) lead = frappe.get_doc("Lead", self.recipient)
if not lead.get("email_id"): if not lead.get("email_id"):
frappe.throw(_("Please set email id for lead communication")) frappe.throw(_("Please set an email id for lead communication"))
def send(self): def set_end_date(self):
lead = frappe.get_doc("Lead", self.get("lead")) #set the end date as start date + max(send after days) in email schedule
email_schedule = frappe.get_doc("Campaign Email Schedule", self.get("email_schedule")) send_after_days = []
email_template = frappe.get_doc("Email Template", email_schedule.name) for entry in self.get("email_schedule"):
frappe.sendmail( send_after_days.append(entry.send_after_days)
recipients = lead.get("email_id"), self.end_date = add_days(getdate(self.start_date), max(send_after_days))
sender = lead.get("lead_owner"),
subject = email_template.get("subject"),
message = email_template.get("response"),
reference_doctype = self.doctype,
reference_name = self.name
)
def on_submit(self): def update_status(self):
"""Create a new communication linked to the campaign if not created""" start_date = getdate(self.start_date)
if not frappe.db.sql("select subject from tabCommunication where reference_name = %s", self.name): end_date = getdate(self.end_date)
doc = frappe.new_doc("Communication") today_date = getdate(today())
doc.subject = "Email Campaign Communication: " + self.name if self.unsubscribed:
link_communication_to_document(doc, "Email Campaign", self.name, ignore_communication_links = False) self.status = "Unsubscribed"
else:
if start_date > today_date:
self.status = "Scheduled"
elif end_date >= today_date:
self.status = "In Progress"
elif end_date < today_date:
self.status = "Completed"
@frappe.whitelist() #called through hooks to send campaign mails to leads
def send_email_to_leads(): def send_email_to_leads():
email_campaigns = frappe.get_all("Email Campaign", filters = { 'start_date': ("<=", nowdate()) }) email_campaigns = frappe.get_all("Email Campaign", filters = { 'status': ('not in', ['Unsubscribed', 'Completed', 'Scheduled']), 'unsubscribed': 0 })
for campaign in email_campaigns: for campaign in email_campaigns:
email_campaign = frappe.get_doc("Email Campaign", campaign.name) email_campaign = frappe.get_doc("Email Campaign", campaign.name)
for entry in email_campaign.get("email_schedule"): for entry in email_campaign.get("email_schedule"):
scheduled_date = add_days(email_campaign.get('start_date'), entry.get('send_after_days')) scheduled_date = add_days(email_campaign.get('start_date'), entry.get('send_after_days'))
if(scheduled_date == nowdate()): if scheduled_date == getdate(today()):
email_campaign.send() send_mail(entry, email_campaign)
# send_email_to_leads()
def send_mail(entry, email_campaign):
if email_campaign.email_campaign_for == "Lead":
lead = frappe.get_doc("Lead", email_campaign.get("recipient"))
recipient_email = lead.email_id
elif email_campaign.email_campaign_for == "Contact":
recipient = frappe.get_doc("Contact", email_campaign.get("recipient"))
recipient_email = recipient.email_id
email_template = frappe.get_doc("Email Template", entry.get("email_template"))
sender = frappe.get_doc("User", email_campaign.get("sender"))
sender_email = sender.email
# send mail and link communication to document
comm = make(
doctype = "Email Campaign",
name = email_campaign.name,
subject = email_template.get("subject"),
content = email_template.get("response"),
sender = sender_email,
recipients = recipient_email,
communication_medium = "Email",
sent_or_received = "Sent",
send_email = False,
email_template = email_template.name
)
frappe.sendmail(
recipients = recipient_email,
sender = sender_email,
subject = email_template.get("subject"),
content = email_template.get("response"),
reference_doctype = "Email Campaign",
reference_name = email_campaign.name,
unsubscribe_method = "/api/method/erpnext.crm.doctype.email_campaign.email_campaign.unsubscribe_recipient",
unsubscribe_params = {"name": email_campaign.name, "email": recipient_email},
unsubscribe_message = "Stop Getting Email Campaign Mails",
communication = comm.get("name")
)
@frappe.whitelist(allow_guest=True)
def unsubscribe_recipient(name, email):
# unsubsribe from comments and communications
try:
frappe.get_doc({
"doctype": "Email Unsubscribe",
"email": email,
"reference_doctype": "Email Campaign",
"reference_name": name
}).insert(ignore_permissions=True)
except frappe.DuplicateEntryError:
frappe.db.rollback()
else:
frappe.db.commit()
frappe.db.set_value("Email Campaign", name, "unsubscribed", 1)
frappe.db.set_value("Email Campaign", name, "status", "Unsubscribed")
frappe.db.commit()
return_unsubscribed_page(email, name)
def return_unsubscribed_page(email, name):
frappe.respond_as_web_page(_("Unsubscribed"),
_("{0} has left the Email Campaign {1}").format(email, name),
indicator_color='green')
#called through hooks to update email campaign status daily
def set_email_campaign_status():
email_campaigns = frappe.get_all("Email Campaign")
for email_campaign in email_campaigns:
email_campaign.update_status()

View File

@@ -0,0 +1,11 @@
frappe.listview_settings['Email Campaign'] = {
get_indicator: function(doc) {
var colors = {
"Unsubscribed": "red",
"Scheduled": "blue",
"In Progress": "orange",
"Completed": "green"
}
return [__(doc.status), colors[doc.status], "status,=," + doc.status];
}
};

View File

@@ -266,7 +266,8 @@ scheduler_events = {
"erpnext.projects.doctype.project.project.send_project_status_email_to_users", "erpnext.projects.doctype.project.project.send_project_status_email_to_users",
"erpnext.quality_management.doctype.quality_review.quality_review.review", "erpnext.quality_management.doctype.quality_review.quality_review.review",
"erpnext.support.doctype.service_level_agreement.service_level_agreement.check_agreement_status", "erpnext.support.doctype.service_level_agreement.service_level_agreement.check_agreement_status",
"erpnext.crm.doctype.email_campaign.email_campaign.send_email_to_leads" "erpnext.crm.doctype.email_campaign.email_campaign.send_email_to_leads",
"erpnext.crm.doctype.email_campaign.email_campaign.set_email_campaign_status"
], ],
"daily_long": [ "daily_long": [
"erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_latest_price_in_all_boms" "erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_latest_price_in_all_boms"