[fix] Use Bulk Email status as Delivery Status of a Communication

This commit is contained in:
Anand Doshi 2016-04-18 16:03:35 +05:30
parent 54f9632206
commit c427de108c
10 changed files with 1075 additions and 970 deletions

View file

@ -318,7 +318,7 @@ def sendmail(recipients=(), sender="", subject="No Subject", message="No Message
unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None,
attachments=None, content=None, doctype=None, name=None, reply_to=None,
cc=(), show_as_cc=(), message_id=None, in_reply_to=None, as_bulk=False, send_after=None, expose_recipients=False,
bulk_priority=1):
bulk_priority=1, communication=None):
"""Send email using user's default **Email Account** or global default **Email Account**.
@ -339,6 +339,7 @@ def sendmail(recipients=(), sender="", subject="No Subject", message="No Message
:param in_reply_to: Used to send the Message-Id of a received email back as In-Reply-To.
:param send_after: Send after the given datetime.
:param expose_recipients: Display all recipients in the footer message - "This email was sent to"
:param communication: Communication link to be set in Bulk Email record
"""
if bulk or as_bulk:
@ -348,7 +349,7 @@ def sendmail(recipients=(), sender="", subject="No Subject", message="No Message
reference_doctype = doctype or reference_doctype, reference_name = name or reference_name,
unsubscribe_method=unsubscribe_method, unsubscribe_params=unsubscribe_params, unsubscribe_message=unsubscribe_message,
attachments=attachments, reply_to=reply_to, cc=cc, show_as_cc=show_as_cc, message_id=message_id, in_reply_to=in_reply_to,
send_after=send_after, expose_recipients=expose_recipients, bulk_priority=bulk_priority)
send_after=send_after, expose_recipients=expose_recipients, bulk_priority=bulk_priority, communication=communication)
else:
import frappe.email
if as_markdown:

File diff suppressed because it is too large Load diff

View file

@ -10,6 +10,7 @@ from frappe.model.db_schema import add_column
from frappe.core.doctype.communication.comment import validate_comment, notify_mentions, update_comment_in_doc
from frappe.core.doctype.communication.email import validate_email, notify, _notify, update_parent_status
from email.utils import parseaddr
from collections import Counter
exclude_from_linked_with = True
@ -174,6 +175,35 @@ class Communication(Document):
_notify(self, print_html, print_format, attachments, recipients, cc)
def set_delivery_status(self, commit=False):
'''Look into the status of Bulk Email linked to this Communication and set the Delivery Status of this Communication'''
delivery_status = None
status_counts = Counter(frappe.db.sql_list('''select status from `tabBulk Email` where communication=%s''', self.name))
if status_counts.get('Not Sent') or status_counts.get('Sending'):
delivery_status = 'Sending'
elif status_counts.get('Error'):
delivery_status = 'Error'
elif status_counts.get('Expired'):
delivery_status = 'Expired'
elif status_counts.get('Sent'):
delivery_status = 'Sent'
if delivery_status:
self.db_set('delivery_status', delivery_status)
frappe.publish_realtime('update_communication', self.as_dict(),
doctype=self.reference_doctype, docname=self.reference_name, after_commit=True)
# for list views and forms
self.notify_update()
if commit:
frappe.db.commit()
def on_doctype_update():
"""Add index in `tabCommunication` for `(reference_doctype, reference_name)`"""
frappe.db.add_index("Communication", ["reference_doctype", "reference_name"])

View file

@ -131,7 +131,8 @@ def _notify(doc, print_html=None, print_format=None, attachments=None,
attachments=doc.attachments,
message_id=doc.name,
unsubscribe_message=_("Leave this conversation"),
bulk=True
bulk=True,
communication=doc.name
)
def update_parent_status(doc):

View file

@ -17,7 +17,7 @@ class BulkLimitCrossedError(frappe.ValidationError): pass
def send(recipients=None, sender=None, subject=None, message=None, reference_doctype=None,
reference_name=None, unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None,
attachments=None, reply_to=None, cc=(), show_as_cc=(), message_id=None, in_reply_to=None, send_after=None,
expose_recipients=False, bulk_priority=1):
expose_recipients=False, bulk_priority=1, communication=None):
"""Add email to sending queue (Bulk Email)
:param recipients: List of recipients.
@ -34,6 +34,7 @@ def send(recipients=None, sender=None, subject=None, message=None, reference_doc
:param message_id: Used for threading. If a reply is received to this email, Message-Id is sent back as In-Reply-To in received email.
:param in_reply_to: Used to send the Message-Id of a received email back as In-Reply-To.
:param send_after: Send this email after the given datetime. If value is in integer, then `send_after` will be the automatically set to no of days from current date.
:param communication: Communication link to be set in Bulk Email record
"""
if not unsubscribe_method:
unsubscribe_method = "/api/method/frappe.email.bulk.unsubscribe"
@ -101,12 +102,15 @@ def send(recipients=None, sender=None, subject=None, message=None, reference_doc
# add to queue
add(email, sender, subject, email_content, email_text_context, reference_doctype,
reference_name, attachments, reply_to, cc, message_id, in_reply_to, send_after, bulk_priority, email_account=email_account)
reference_name, attachments, reply_to, cc, message_id, in_reply_to, send_after, bulk_priority,
email_account=email_account, communication=communication)
def add(email, sender, subject, formatted, text_content=None,
reference_doctype=None, reference_name=None, attachments=None, reply_to=None,
cc=(), message_id=None, in_reply_to=None, send_after=None, bulk_priority=1, email_account=None):
cc=(), message_id=None, in_reply_to=None, send_after=None, bulk_priority=1,
email_account=None, communication=None):
"""add to bulk mail queue"""
e = frappe.new_doc('Bulk Email')
e.recipient = email
e.priority = bulk_priority
@ -130,12 +134,13 @@ def add(email, sender, subject, formatted, text_content=None,
e.reference_doctype = reference_doctype
e.reference_name = reference_name
e.communication = communication
e.send_after = send_after
e.insert(ignore_permissions=True)
def check_bulk_limit(recipients):
# get count of mails sent this month
this_month = frappe.db.sql("""select count(*) from `tabBulk Email` where
this_month = frappe.db.sql("""select count(name) from `tabBulk Email` where
status='Sent' and MONTH(creation)=MONTH(CURDATE())""")[0][0]
# if using settings from site_config.json, check bulk limit
@ -261,6 +266,10 @@ def flush(from_test=False):
frappe.db.sql("""update `tabBulk Email` set status='Sending' where name=%s""",
(email["name"],), auto_commit=auto_commit)
if email.communication:
frappe.get_doc('Communication', email.communication).set_delivery_status(commit=auto_commit)
try:
if not from_test:
smtpserver.setup_email_account(email.reference_doctype)
@ -269,6 +278,9 @@ def flush(from_test=False):
frappe.db.sql("""update `tabBulk Email` set status='Sent' where name=%s""",
(email["name"],), auto_commit=auto_commit)
if email.communication:
frappe.get_doc('Communication', email.communication).set_delivery_status(commit=auto_commit)
except (smtplib.SMTPServerDisconnected,
smtplib.SMTPConnectError,
smtplib.SMTPHeloError,
@ -278,6 +290,9 @@ def flush(from_test=False):
frappe.db.sql("""update `tabBulk Email` set status='Not Sent' where name=%s""",
(email["name"],), auto_commit=auto_commit)
if email.communication:
frappe.get_doc('Communication', email.communication).set_delivery_status(commit=auto_commit)
# no need to attempt further
return
@ -285,6 +300,9 @@ def flush(from_test=False):
frappe.db.sql("""update `tabBulk Email` set status='Error', error=%s
where name=%s""", (unicode(e), email["name"]), auto_commit=auto_commit)
if email.communication:
frappe.get_doc('Communication', email.communication).set_delivery_status(commit=auto_commit)
# NOTE: removing commit here because we pass auto_commit
# finally:
# frappe.db.commit()

View file

@ -183,6 +183,32 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "communication",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Communication",
"length": 0,
"no_copy": 0,
"options": "Communication",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@ -245,8 +271,8 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-02-26 06:44:01.199764",
"modified_by": "anand@erpnext.com",
"modified": "2016-04-18 05:13:07.741981",
"modified_by": "Administrator",
"module": "Email",
"name": "Bulk Email",
"owner": "Administrator",

View file

@ -1,6 +1,8 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt
frappe.provide('frappe.timeline');
frappe.ui.form.Timeline = Class.extend({
init: function(opts) {
$.extend(this, opts);
@ -551,3 +553,71 @@ frappe.ui.form.Timeline = Class.extend({
frappe.ui.setup_like_popover(this.wrapper, ".comment-likes");
}
});
$.extend(frappe.timeline, {
new_communication: function(communication) {
var docinfo = frappe.model.get_docinfo(communication.reference_doctype, communication.reference_name);
if (docinfo && docinfo.communications) {
var communications = docinfo.communications;
var communication_exists = false;
for (var i=0, l=communications.length; i<l; i++) {
if (communications[i].name==communication.name) {
communication_exists = true;
break;
}
}
if (!communication_exists) {
docinfo.communications = communications.concat([communication]);
}
}
if (cur_frm.doctype === communication.reference_doctype && cur_frm.docname === communication.reference_name) {
cur_frm.timeline && cur_frm.timeline.refresh();
}
},
delete_communication: function(communication) {
var docinfo = frappe.model.get_docinfo(communication.reference_doctype, communication.reference_name);
var index = frappe.timeline.index_of_communication(communication, docinfo);
if (index !== -1) {
// remove it from communications list
docinfo.communications.splice(index, 1);
}
if (cur_frm.doctype === communication.reference_doctype && cur_frm.docname === communication.reference_name) {
cur_frm.timeline && cur_frm.timeline.refresh();
}
},
update_communication: function(communication) {
var docinfo = frappe.model.get_docinfo(communication.reference_doctype, communication.reference_name);
var index = frappe.timeline.index_of_communication(communication, docinfo);
if (index !== -1) {
// update
$.extend(docinfo.communications[index], communication);
}
if (cur_frm.doctype === communication.reference_doctype && cur_frm.docname === communication.reference_name) {
cur_frm.timeline && cur_frm.timeline.refresh();
}
},
index_of_communication: function(communication, docinfo) {
var index = -1;
if (docinfo && docinfo.communications) {
var communications = docinfo.communications;
for (var i=0, l=communications.length; i<l; i++) {
if (communications[i].name==communication.name) {
index = i;
break;
}
}
}
return index;
},
})

View file

@ -35,6 +35,8 @@
{% if (data.delivery_status) {
if (in_list(["Sent", "Opened", "Clicked"], data.delivery_status)) {
var indicator_class = "green";
} else if (data.delivery_status === "Sending") {
var indicator_class = "orange";
} else {
var indicator_class = "red";
}

View file

@ -160,53 +160,6 @@ $.extend(frappe.model, {
}
},
new_communication: function(communication) {
var docinfo = frappe.model.get_docinfo(communication.reference_doctype, communication.reference_name);
if (docinfo && docinfo.communications) {
var communications = docinfo.communications;
var communication_exists = false;
for (var i=0, l=communications.length; i<l; i++) {
if (communications[i].name==communication.name) {
communication_exists = true;
break;
}
}
if (!communication_exists) {
docinfo.communications = communications.concat([communication]);
}
}
if (cur_frm.doctype === communication.reference_doctype && cur_frm.docname === communication.reference_name) {
cur_frm.timeline && cur_frm.timeline.refresh();
}
},
delete_communication: function(communication) {
var docinfo = frappe.model.get_docinfo(communication.reference_doctype, communication.reference_name);
if (docinfo && docinfo.communications) {
var communications = docinfo.communications;
var index = -1;
for (var i=0, l=communications.length; i<l; i++) {
if (communications[i].name==communication.name) {
index = i;
break;
}
}
if (index !== -1) {
// remove it from communications list
docinfo.communications.splice(index, 1);
}
}
if (cur_frm.doctype === communication.reference_doctype && cur_frm.docname === communication.reference_name) {
cur_frm.timeline && cur_frm.timeline.refresh();
}
},
get_shared: function(doctype, name) {
return frappe.model.get_docinfo(doctype, name).shared;
},

View file

@ -26,11 +26,15 @@ frappe.views.FormFactory = frappe.views.Factory.extend({
});
frappe.realtime.on("new_communication", function(data) {
frappe.model.new_communication(data);
frappe.timeline.new_communication(data);
});
frappe.realtime.on("delete_communication", function(data) {
frappe.model.delete_communication(data);
frappe.timeline.delete_communication(data);
});
frappe.realtime.on('update_communication', function(data) {
frappe.timeline.update_communication(data);
});
frappe.realtime.on("doc_viewers", function(data) {