diff --git a/frappe/email/doctype/notification/notification.js b/frappe/email/doctype/notification/notification.js index 454514f922..059f5518fc 100644 --- a/frappe/email/doctype/notification/notification.js +++ b/frappe/email/doctype/notification/notification.js @@ -19,9 +19,12 @@ frappe.notification = { } frappe.model.with_doctype(frm.doc.document_type, function() { - let get_select_options = function(df) { + let get_select_options = function(df, parent_field) { + // Append parent_field name along with fieldname for child table fields + let select_value = parent_field ? df.fieldname + ',' + parent_field : df.fieldname; + return { - value: df.fieldname, + value: select_value, label: df.fieldname + ' (' + __(df.label) + ')' }; }; @@ -59,9 +62,21 @@ frappe.notification = { let receiver_fields = []; if (frm.doc.channel === 'Email') { receiver_fields = $.map(fields, function(d) { - return d.options == 'Email' || - (d.options == 'User' && d.fieldtype == 'Link') - ? get_select_options(d) : null; + + // Add User and Email fields from child into select dropdown + if (d.fieldtype == 'Table') { + let child_fields = frappe.get_doc('DocType', d.options).fields; + return $.map(child_fields, function(df) { + return df.options == 'Email' || + (df.options == 'User' && df.fieldtype == 'Link') + ? get_select_options(df, d.fieldname) : null; + }); + // Add User and Email fields from parent into select dropdown + } else { + return d.options == 'Email' || + (d.options == 'User' && d.fieldtype == 'Link') + ? get_select_options(d) : null; + } }); } else if (in_list(['WhatsApp', 'SMS'], frm.doc.channel)) { receiver_fields = $.map(fields, function(d) { diff --git a/frappe/email/doctype/notification/notification.json b/frappe/email/doctype/notification/notification.json index 95f218ad73..8918709fa0 100644 --- a/frappe/email/doctype/notification/notification.json +++ b/frappe/email/doctype/notification/notification.json @@ -34,6 +34,7 @@ "set_property_after_alert", "property_value", "column_break_5", + "send_to_all_assignees", "recipients", "message_sb", "message", @@ -216,7 +217,7 @@ "fieldname": "recipients", "fieldtype": "Table", "label": "Recipients", - "mandatory_depends_on": "eval:doc.channel!=='Slack'", + "mandatory_depends_on": "eval:doc.channel!=='Slack' && !doc.send_to_all_assignees", "options": "Notification Recipient" }, { @@ -277,11 +278,19 @@ "fieldname": "send_system_notification", "fieldtype": "Check", "label": "Send System Notification" + }, + { + "default": "0", + "depends_on": "eval:doc.channel == 'Email'", + "fieldname": "send_to_all_assignees", + "fieldtype": "Check", + "label": "Send To All Assignees" } ], "icon": "fa fa-envelope", + "index_web_pages_for_search": 1, "links": [], - "modified": "2020-08-11 19:24:35.479373", + "modified": "2020-09-01 18:36:22.550891", "modified_by": "Administrator", "module": "Email", "name": "Notification", diff --git a/frappe/email/doctype/notification/notification.py b/frappe/email/doctype/notification/notification.py index 2ec208c89d..cd5aacabbd 100644 --- a/frappe/email/doctype/notification/notification.py +++ b/frappe/email/doctype/notification/notification.py @@ -189,6 +189,7 @@ def get_context(context): recipients, cc, bcc = self.get_list_of_recipients(doc, context) if not (recipients or cc or bcc): return + sender = None if self.sender and self.sender_email: sender = formataddr((self.sender, self.sender_email)) @@ -234,13 +235,20 @@ def get_context(context): if not frappe.safe_eval(recipient.condition, None, context): continue if recipient.receiver_by_document_field: - email_ids_value = doc.get(recipient.receiver_by_document_field) - if validate_email_address(email_ids_value): - email_ids = email_ids_value.replace(",", "\n") - recipients = recipients + email_ids.split("\n") + fields = recipient.receiver_by_document_field.split(',') + # fields from child table + if len(fields) > 1: + for d in doc.get(fields[1]): + email_id = d.get(fields[0]) + if validate_email_address(email_id): + recipients.append(email_id) + # field from parent doc + else: + email_ids_value = doc.get(fields[0]) + if validate_email_address(email_ids_value): + email_ids = email_ids_value.replace(",", "\n") + recipients = recipients + email_ids.split("\n") - # else: - # print "invalid email" if recipient.cc and "{" in recipient.cc: recipient.cc = frappe.render_template(recipient.cc, context) @@ -262,6 +270,9 @@ def get_context(context): for email in emails: recipients = recipients + email.split("\n") + if self.send_to_all_assignees: + recipients = recipients + get_assignees(doc) + if not recipients and not cc and not bcc: return None, None, None return list(set(recipients)), list(set(cc)), list(set(bcc)) @@ -405,3 +416,12 @@ def evaluate_alert(doc, alert, event): def get_context(doc): return {"doc": doc, "nowdate": nowdate, "frappe": frappe._dict(utils=frappe.utils)} + +def get_assignees(doc): + assignees = [] + assignees = frappe.get_all('ToDo', filters={'status': 'Open', 'reference_name': doc.name, + 'reference_type': doc.doctype}, fields=['owner']) + + recipients = [d.owner for d in assignees] + + return recipients \ No newline at end of file diff --git a/frappe/email/doctype/notification/test_notification.py b/frappe/email/doctype/notification/test_notification.py index 9bdf09375d..45a1587c1a 100644 --- a/frappe/email/doctype/notification/test_notification.py +++ b/frappe/email/doctype/notification/test_notification.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe, frappe.utils, frappe.utils.scheduler +from frappe.desk.form import assign_to import unittest test_records = frappe.get_test_records('Notification') @@ -13,7 +14,31 @@ test_dependencies = ["User"] class TestNotification(unittest.TestCase): def setUp(self): frappe.db.sql("""delete from `tabEmail Queue`""") - frappe.set_user("test1@example.com") + frappe.set_user("test@example.com") + + if not frappe.db.exists('Notification', {'name': 'ToDo Status Update'}, 'name'): + notification = frappe.new_doc('Notification') + notification.name = 'ToDo Status Update' + notification.subject = 'ToDo Status Update' + notification.document_type = 'ToDo' + notification.event = 'Value Change' + notification.value_changed = 'status' + notification.send_to_all_assignees = 1 + notification.save() + + if not frappe.db.exists('Notification', {'name': 'Contact Status Update'}, 'name'): + notification = frappe.new_doc('Notification') + notification.name = 'Contact Status Update' + notification.subject = 'Contact Status Update' + notification.document_type = 'Contact' + notification.event = 'Value Change' + notification.value_changed = 'status' + notification.message = 'Test Contact Update' + notification.append('recipients', { + 'receiver_by_document_field': 'email_id,email_ids' + }) + notification.save() + def tearDown(self): frappe.set_user("Administrator") @@ -177,3 +202,65 @@ class TestNotification(unittest.TestCase): frappe.db.sql("""delete from `tabUser` where email='test_jinja@example.com'""") frappe.db.sql("""delete from `tabEmail Queue`""") frappe.db.sql("""delete from `tabEmail Queue Recipient`""") + + def test_notification_to_assignee(self): + todo = frappe.new_doc('ToDo') + todo.description = 'Test Notification' + todo.save() + + assign_to.add({ + "assign_to": ["test2@example.com"], + "doctype": todo.doctype, + "name": todo.name, + "description": "Close this Todo" + }) + + assign_to.add({ + "assign_to": ["test1@example.com"], + "doctype": todo.doctype, + "name": todo.name, + "description": "Close this Todo" + }) + + #change status of todo + todo.status = 'Closed' + todo.save() + + email_queue = frappe.get_doc('Email Queue', {'reference_doctype': 'ToDo', + 'reference_name': todo.name}) + + self.assertTrue(email_queue) + + recipients = [d.recipient for d in email_queue.recipients] + self.assertTrue('test2@example.com' in recipients) + self.assertTrue('test1@example.com' in recipients) + + def test_notification_by_child_table_field(self): + contact = frappe.new_doc('Contact') + contact.first_name = 'John Doe' + contact.status = 'Open' + contact.append('email_ids', { + 'email_id': 'test2@example.com', + 'is_primary': 1 + }) + + contact.append('email_ids', { + 'email_id': 'test1@example.com' + }) + + contact.save() + + #change status of contact + contact.status = 'Replied' + contact.save() + + email_queue = frappe.get_doc('Email Queue', {'reference_doctype': 'Contact', + 'reference_name': contact.name}) + + self.assertTrue(email_queue) + + recipients = [d.recipient for d in email_queue.recipients] + self.assertTrue('test2@example.com' in recipients) + self.assertTrue('test1@example.com' in recipients) + + diff --git a/frappe/email/doctype/notification_recipient/notification_recipient.json b/frappe/email/doctype/notification_recipient/notification_recipient.json index 201899cd57..0670320a77 100644 --- a/frappe/email/doctype/notification_recipient/notification_recipient.json +++ b/frappe/email/doctype/notification_recipient/notification_recipient.json @@ -46,9 +46,10 @@ "options": "Role" } ], + "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-02-21 11:18:40.125233", + "modified": "2020-09-01 17:40:27.289105", "modified_by": "Administrator", "module": "Email", "name": "Notification Recipient",