diff --git a/frappe/desk/doctype/notification_log/notification_log.js b/frappe/desk/doctype/notification_log/notification_log.js index 654b2b2b06..1f381d115b 100644 --- a/frappe/desk/doctype/notification_log/notification_log.js +++ b/frappe/desk/doctype/notification_log/notification_log.js @@ -3,10 +3,43 @@ frappe.ui.form.on('Notification Log', { refresh: function(frm) { - let dt = frm.doc.document_type; - let dn = frm.doc.document_name; - frm.fields_dict.document_name.$input_wrapper - .find('.control-value') - .wrapInner(``); + if (frm.doc.attached_file) { + frm.trigger('set_attachment'); + } else { + frm.get_field('attachment_link').$wrapper.empty(); + } + }, + + open_reference_document: function(frm) { + const dt = frm.doc.document_type; + const dn = frm.doc.document_name; + frappe.set_route('Form', dt, dn); + }, + + set_attachment: function(frm) { + const attachment = JSON.parse(frm.doc.attached_file); + + const $wrapper = frm.get_field('attachment_link').$wrapper; + $wrapper.html(` +
+
+ + ${attachment.name}.pdf +
+
+ `); + + $wrapper.find(".attached-file-link").click(() => { + const w = window.open( + frappe.urllib.get_full_url(`/api/method/frappe.utils.print_format.download_pdf? + doctype=${encodeURIComponent(attachment.doctype)} + &name=${encodeURIComponent(attachment.name)} + &format=${encodeURIComponent(attachment.print_format)} + &lang=${encodeURIComponent(attachment.lang)}`) + ); + if (!w) { + frappe.msgprint(__("Please enable pop-ups")); + } + }); } }); diff --git a/frappe/desk/doctype/notification_log/notification_log.json b/frappe/desk/doctype/notification_log/notification_log.json index ecb746df64..050bf85ead 100644 --- a/frappe/desk/doctype/notification_log/notification_log.json +++ b/frappe/desk/doctype/notification_log/notification_log.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2019-08-26 13:37:34.165254", "doctype": "DocType", "editable_grid": 1, @@ -8,10 +9,12 @@ "for_user", "type", "email_content", - "column_break_4", "document_type", "read", "document_name", + "attached_file", + "attachment_link", + "open_reference_document", "from_user" ], "fields": [ @@ -20,57 +23,65 @@ "fieldtype": "Text", "in_list_view": 1, "label": "Subject", - "read_only": 1 + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "for_user", "fieldtype": "Link", + "hidden": 1, "label": "For User", "options": "User", - "read_only": 1 + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "type", "fieldtype": "Select", + "hidden": 1, "in_list_view": 1, "in_standard_filter": 1, "label": "Type", - "options": "Mention\nEnergy Point\nAssignment\nShare", - "read_only": 1, - "search_index": 1 + "options": "Mention\nEnergy Point\nAssignment\nShare\nAlert", + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "email_content", - "fieldtype": "Text", - "label": "Email Content", - "read_only": 1 - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" + "fieldtype": "Text Editor", + "label": "Message", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "document_type", "fieldtype": "Link", + "hidden": 1, "label": "Document Type", "options": "DocType", - "read_only": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "document_name", "fieldtype": "Data", - "label": "Document Name", - "read_only": 1, - "search_index": 1 + "hidden": 1, + "label": "Document Link", + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "from_user", "fieldtype": "Link", + "hidden": 1, "label": "From User", "options": "User", - "read_only": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -78,26 +89,51 @@ "fieldtype": "Check", "hidden": 1, "ignore_user_permissions": 1, - "label": "Read" + "label": "Read", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "open_reference_document", + "fieldtype": "Button", + "label": "Open Reference Document", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "attached_file", + "fieldtype": "Code", + "hidden": 1, + "label": "Attached File", + "options": "JSON", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "attachment_link", + "fieldtype": "HTML", + "label": "Attachment Link", + "show_days": 1, + "show_seconds": 1 } ], + "hide_toolbar": 1, "in_create": 1, - "modified": "2019-11-12 15:22:35.283678", + "links": [], + "modified": "2020-05-31 22:31:12.886950", "modified_by": "umair@erpnext.com", "module": "Desk", "name": "Notification Log", "owner": "Administrator", "permissions": [ { - "create": 1, "email": 1, "export": 1, "print": 1, "read": 1, "report": 1, "role": "All", - "share": 1, - "write": 1 + "share": 1 } ], "sort_field": "modified", diff --git a/frappe/desk/doctype/notification_log/notification_log.py b/frappe/desk/doctype/notification_log/notification_log.py index 17eb6371b1..211b3ae5e6 100644 --- a/frappe/desk/doctype/notification_log/notification_log.py +++ b/frappe/desk/doctype/notification_log/notification_log.py @@ -48,6 +48,7 @@ def enqueue_create_notification(users, doc): if isinstance(users, frappe.string_types): users = [user.strip() for user in users.split(',') if user.strip()] + users = list(set(users)) frappe.enqueue( 'frappe.desk.doctype.notification_log.notification_log.make_notification_logs', @@ -58,6 +59,7 @@ def enqueue_create_notification(users, doc): def make_notification_logs(doc, users): from frappe.social.doctype.energy_point_settings.energy_point_settings import is_energy_point_enabled + for user in users: if frappe.db.exists('User', user): if is_notifications_enabled(user): @@ -68,7 +70,7 @@ def make_notification_logs(doc, users): _doc.update(doc) _doc.for_user = user _doc.subject = _doc.subject.replace('
', '').replace('
', '') - if _doc.for_user != _doc.from_user or doc.type == 'Energy Point': + if _doc.for_user != _doc.from_user or doc.type == 'Energy Point' or doc.type == 'Alert': _doc.insert(ignore_permissions=True) def send_notification_email(doc): diff --git a/frappe/desk/doctype/notification_settings/notification_settings.json b/frappe/desk/doctype/notification_settings/notification_settings.json index 6af325507b..85f93e156e 100644 --- a/frappe/desk/doctype/notification_settings/notification_settings.json +++ b/frappe/desk/doctype/notification_settings/notification_settings.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "Prompt", "creation": "2019-09-11 22:15:44.851526", "doctype": "DocType", @@ -21,52 +22,68 @@ "default": "1", "fieldname": "enabled", "fieldtype": "Check", - "label": "Enabled" + "label": "Enabled", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "subscribed_documents", "fieldtype": "Table MultiSelect", "label": "Subscribed Documents", - "options": "Notification Subscribed Document" + "options": "Notification Subscribed Document", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_3", "fieldtype": "Section Break", - "label": "Email Settings" + "label": "Email Settings", + "show_days": 1, + "show_seconds": 1 }, { "default": "1", "fieldname": "enable_email_notifications", "fieldtype": "Check", - "label": "Enable Email Notifications" + "label": "Enable Email Notifications", + "show_days": 1, + "show_seconds": 1 }, { "default": "1", "depends_on": "enable_email_notifications", "fieldname": "enable_email_mention", "fieldtype": "Check", - "label": "Mentions" + "label": "Mentions", + "show_days": 1, + "show_seconds": 1 }, { "default": "1", "depends_on": "enable_email_notifications", "fieldname": "enable_email_assignment", "fieldtype": "Check", - "label": "Assignments" + "label": "Assignments", + "show_days": 1, + "show_seconds": 1 }, { "default": "1", "depends_on": "enable_email_notifications", "fieldname": "enable_email_energy_point", "fieldtype": "Check", - "label": "Energy Points" + "label": "Energy Points", + "show_days": 1, + "show_seconds": 1 }, { "default": "1", "depends_on": "enable_email_notifications", "fieldname": "enable_email_share", "fieldtype": "Check", - "label": "Document Share" + "label": "Document Share", + "show_days": 1, + "show_seconds": 1 }, { "default": "__user", @@ -75,18 +92,23 @@ "hidden": 1, "label": "User", "options": "User", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "seen", "fieldtype": "Check", "hidden": 1, - "label": "Seen" + "label": "Seen", + "show_days": 1, + "show_seconds": 1 } ], "in_create": 1, - "modified": "2019-11-19 12:57:59.356786", + "links": [], + "modified": "2020-05-31 22:16:40.798019", "modified_by": "Administrator", "module": "Desk", "name": "Notification Settings", diff --git a/frappe/desk/doctype/notification_settings/notification_settings.py b/frappe/desk/doctype/notification_settings/notification_settings.py index 6b5a13ee27..9b124cd6f4 100644 --- a/frappe/desk/doctype/notification_settings/notification_settings.py +++ b/frappe/desk/doctype/notification_settings/notification_settings.py @@ -28,6 +28,9 @@ def is_email_notifications_enabled_for_type(user, notification_type): if not is_email_notifications_enabled(user): return False + if notification_type == 'Alert': + return False + fieldname = 'enable_email_' + frappe.scrub(notification_type) enabled = frappe.db.get_value('Notification Settings', user, fieldname) if enabled is None: diff --git a/frappe/email/doctype/notification/notification.js b/frappe/email/doctype/notification/notification.js index 44056955f7..02fc8512ca 100644 --- a/frappe/email/doctype/notification/notification.js +++ b/frappe/email/doctype/notification/notification.js @@ -80,7 +80,6 @@ frappe.ui.form.on("Notification", { }); }, refresh: function(frm) { - frm.toggle_reqd("recipients", frm.doc.channel=="Email"); frappe.notification.setup_fieldname_select(frm); frm.get_field("is_standard").toggle(frappe.boot.developer_mode); frm.trigger('event'); diff --git a/frappe/email/doctype/notification/notification.json b/frappe/email/doctype/notification/notification.json index 14eff2251a..d1526f5fe4 100644 --- a/frappe/email/doctype/notification/notification.json +++ b/frappe/email/doctype/notification/notification.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_rename": 1, "autoname": "Prompt", "creation": "2014-07-11 17:18:09.923399", @@ -22,6 +23,7 @@ "days_in_advance", "value_changed", "sender", + "send_system_notification", "sender_email", "section_break_9", "condition", @@ -46,32 +48,43 @@ "default": "1", "fieldname": "enabled", "fieldtype": "Check", - "label": "Enabled" + "label": "Enabled", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_2", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "Email", + "depends_on": "eval: !doc.disable_channel", "fieldname": "channel", "fieldtype": "Select", "label": "Channel", - "options": "Email\nSlack", + "options": "Email\nSlack\nSystem Notification", "reqd": 1, - "set_only_once": 1 + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.channel=='Slack'", "fieldname": "slack_webhook_url", "fieldtype": "Link", "label": "Slack Channel", - "options": "Slack Webhook URL" + "mandatory_depends_on": "eval:doc.channel=='Slack'", + "options": "Slack Webhook URL", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "filters", "fieldtype": "Section Break", - "label": "Filters" + "label": "Filters", + "show_days": 1, + "show_seconds": 1 }, { "description": "To add dynamic subject, use jinja tags like\n\n
{{ doc.name }} Delivered
", @@ -80,7 +93,9 @@ "ignore_xss_filter": 1, "in_list_view": 1, "label": "Subject", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "document_type", @@ -90,13 +105,17 @@ "label": "Document Type", "options": "DocType", "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "is_standard", "fieldtype": "Check", - "label": "Is Standard" + "label": "Is Standard", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "is_standard", @@ -104,11 +123,15 @@ "fieldtype": "Link", "in_standard_filter": 1, "label": "Module", - "options": "Module Def" + "options": "Module Def", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_1", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "event", @@ -117,21 +140,27 @@ "label": "Send Alert On", "options": "\nNew\nSave\nSubmit\nCancel\nDays After\nDays Before\nValue Change\nMethod\nCustom", "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.event=='Method'", "description": "Trigger on valid methods like \"before_insert\", \"after_update\", etc (will depend on the DocType selected)", "fieldname": "method", "fieldtype": "Data", - "label": "Trigger Method" + "label": "Trigger Method", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.event==\"Days After\" || doc.event==\"Days Before\"", "description": "Send alert if date matches this field's value", "fieldname": "date_changed", "fieldtype": "Select", - "label": "Reference Date" + "label": "Reference Date", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -139,31 +168,41 @@ "description": "Send days before or after the reference date", "fieldname": "days_in_advance", "fieldtype": "Int", - "label": "Days Before or After" + "label": "Days Before or After", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.event==\"Value Change\"", "description": "Send alert if this field's value changes", "fieldname": "value_changed", "fieldtype": "Select", - "label": "Value Changed" + "label": "Value Changed", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sender", "fieldtype": "Link", "label": "Sender", - "options": "Email Account" + "options": "Email Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sender_email", "fieldtype": "Data", "label": "Sender Email", "options": "Email", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_9", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "description": "Optional: The alert will be sent if this expression is true", @@ -171,99 +210,143 @@ "fieldtype": "Code", "ignore_xss_filter": 1, "in_list_view": 1, - "label": "Condition" + "label": "Condition", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_6", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "html_7", "fieldtype": "HTML", - "options": "

Condition Examples:

\n
doc.status==\"Open\"
doc.due_date==nowdate()
doc.total > 40000\n
\n" + "options": "

Condition Examples:

\n
doc.status==\"Open\"
doc.due_date==nowdate()
doc.total > 40000\n
\n", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "property_section", "fieldtype": "Section Break", - "label": "Set Property After Alert" + "label": "Set Property After Alert", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "set_property_after_alert", "fieldtype": "Select", - "label": "Set Property After Alert" + "label": "Set Property After Alert", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "property_value", "fieldtype": "Data", - "label": "Value To Be Set" + "label": "Value To Be Set", + "show_days": 1, + "show_seconds": 1 }, { - "depends_on": "eval:doc.channel=='Email'", + "depends_on": "eval:doc.channel!=='Slack'", "fieldname": "column_break_5", "fieldtype": "Section Break", - "label": "Recipients" + "label": "Recipients", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "recipients", "fieldtype": "Table", "label": "Recipients", - "options": "Notification Recipient" + "mandatory_depends_on": "eval:doc.channel!=='Slack'", + "options": "Notification Recipient", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "message_sb", "fieldtype": "Section Break", - "label": "Message" + "label": "Message", + "show_days": 1, + "show_seconds": 1 }, { "default": "Add your message here", "fieldname": "message", "fieldtype": "Code", "ignore_xss_filter": 1, - "label": "Message" + "label": "Message", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.channel=='Email'", "fieldname": "message_examples", "fieldtype": "HTML", "label": "Message Examples", - "options": "
Message Example
\n\n
<h3>Order Overdue</h3>\n\n<p>Transaction {{ doc.name }} has exceeded Due Date. Please take necessary action.</p>\n\n<!-- show last comment -->\n{% if comments %}\nLast comment: {{ comments[-1].comment }} by {{ comments[-1].by }}\n{% endif %}\n\n<h4>Details</h4>\n\n<ul>\n<li>Customer: {{ doc.customer }}\n<li>Amount: {{ doc.grand_total }}\n</ul>\n
" + "options": "
Message Example
\n\n
<h3>Order Overdue</h3>\n\n<p>Transaction {{ doc.name }} has exceeded Due Date. Please take necessary action.</p>\n\n<!-- show last comment -->\n{% if comments %}\nLast comment: {{ comments[-1].comment }} by {{ comments[-1].by }}\n{% endif %}\n\n<h4>Details</h4>\n\n<ul>\n<li>Customer: {{ doc.customer }}\n<li>Amount: {{ doc.grand_total }}\n</ul>\n
", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.channel=='Slack'", "fieldname": "slack_message_examples", "fieldtype": "HTML", "label": "Message Examples", - "options": "
Message Example
\n\n
*Order Overdue*\n\nTransaction {{ doc.name }} has exceeded Due Date. Please take necessary action.\n\n\n{% if comments %}\nLast comment: {{ comments[-1].comment }} by {{ comments[-1].by }}\n{% endif %}\n\n*Details*\n\n\u2022 Customer: {{ doc.customer }}\n\u2022 Amount: {{ doc.grand_total }}\n
" + "options": "
Message Example
\n\n
*Order Overdue*\n\nTransaction {{ doc.name }} has exceeded Due Date. Please take necessary action.\n\n\n{% if comments %}\nLast comment: {{ comments[-1].comment }} by {{ comments[-1].by }}\n{% endif %}\n\n*Details*\n\n\u2022 Customer: {{ doc.customer }}\n\u2022 Amount: {{ doc.grand_total }}\n
", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "view_properties", "fieldtype": "Button", - "label": "View Properties (via Customize Form)" + "label": "View Properties (via Customize Form)", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "attach_print", "fieldname": "column_break_25", "fieldtype": "Section Break", - "label": "Print Settings" + "label": "Print Settings", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "attach_print", "fieldtype": "Check", - "label": "Attach Print" + "label": "Attach Print", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "attach_print", "fieldname": "print_format", "fieldtype": "Link", "label": "Print Format", - "options": "Print Format" + "options": "Print Format", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "0", + "depends_on": "eval: doc.channel !== 'System Notification'", + "description": "If enabled, the notification will show up in the notifications dropdown on the top right corner of the navigation bar.", + "fieldname": "send_system_notification", + "fieldtype": "Check", + "label": "Send System Notification", + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-envelope", - "modified": "2019-07-15 13:17:02.585013", + "links": [], + "modified": "2020-05-29 16:03:10.914526", "modified_by": "Administrator", "module": "Email", "name": "Notification", diff --git a/frappe/email/doctype/notification/notification.py b/frappe/email/doctype/notification/notification.py index 8c011ade65..8e53b50fa2 100644 --- a/frappe/email/doctype/notification/notification.py +++ b/frappe/email/doctype/notification/notification.py @@ -13,6 +13,7 @@ from frappe.utils.jinja import validate_template from frappe.modules.utils import export_module_json, get_doc_module from six import string_types from frappe.integrations.doctype.slack_webhook_url.slack_webhook_url import send_slack_message +from frappe.desk.doctype.notification_log.notification_log import enqueue_create_notification class Notification(Document): def onload(self): @@ -125,6 +126,9 @@ def get_context(context): if self.channel == 'Slack': self.send_a_slack_msg(doc, context) + if self.channel == 'System Notification' or self.send_system_notification: + self.create_system_notification(doc, context) + if self.set_property_after_alert: allow_update = True if doc.docstatus == 1 and not doc.meta.get_field(self.set_property_after_alert).allow_on_submit: @@ -143,6 +147,25 @@ def get_context(context): except Exception: frappe.log_error(title='Document update failed', message=frappe.get_traceback()) + def create_system_notification(self, doc, context): + subject = self.subject + if "{" in subject: + subject = frappe.render_template(self.subject, context) + + attachments = self.get_attachment(doc) + recipients, cc, bcc = self.get_list_of_recipients(doc, context) + users = recipients + cc + bcc + + notification_doc = { + 'type': 'Alert', + 'document_type': doc.doctype, + 'document_name': doc.name, + 'subject': subject, + 'email_content': frappe.render_template(self.message, context), + 'attached_file': attachments and json.dumps(attachments[0]) + } + enqueue_create_notification(users, notification_doc) + def send_an_email(self, doc, context): from email.utils import formataddr subject = self.subject @@ -228,8 +251,7 @@ def get_context(context): # ignoring attachment as draft and cancelled documents are not allowed to print status = "Draft" if doc.docstatus == 0 else "Cancelled" - frappe.throw(_("""Not allowed to attach {0} document, - please enable Allow Print For {0} in Print Settings""".format(status)), + frappe.throw(_("""Not allowed to attach {0} document, please enable Allow Print For {0} in Print Settings""").format(status), title=_("Error in Notification")) else: return [{ diff --git a/frappe/public/js/frappe/ui/notifications/notifications.js b/frappe/public/js/frappe/ui/notifications/notifications.js index 2420d6772e..64d19ce63f 100644 --- a/frappe/public/js/frappe/ui/notifications/notifications.js +++ b/frappe/public/js/frappe/ui/notifications/notifications.js @@ -304,10 +304,7 @@ frappe.ui.Notifications = class Notifications { } get_dropdown_item_html(field) { - let doc_link = frappe.utils.get_form_link( - field.document_type, - field.document_name - ); + let doc_link = this.get_item_link(field); let read_class = field.read ? '' : 'unread'; let mark_read_action = field.read ? '': 'data-action="mark_as_read"'; let message = field.subject; @@ -336,6 +333,17 @@ frappe.ui.Notifications = class Notifications { return item_html; } + get_item_link(notification_doc) { + const link_doctype = + notification_doc.type == 'Alert' ? 'Notification Log': notification_doc.document_type; + const link_docname = + notification_doc.type == 'Alert' ? notification_doc.name: notification_doc.document_name; + return frappe.utils.get_form_link( + link_doctype, + link_docname + ); + } + render_dropdown_headers() { this.categories = [ {