diff --git a/frappe/automation/doctype/assignment_rule/assignment_rule.py b/frappe/automation/doctype/assignment_rule/assignment_rule.py
index 0a5d85636f..bf45347c4f 100644
--- a/frappe/automation/doctype/assignment_rule/assignment_rule.py
+++ b/frappe/automation/doctype/assignment_rule/assignment_rule.py
@@ -44,7 +44,7 @@ class AssignmentRule(Document):
user = self.get_user()
assign_to.add(dict(
- assign_to = user,
+ assign_to = [user],
doctype = doc.get('doctype'),
name = doc.get('name'),
description = frappe.render_template(self.description, doc),
diff --git a/frappe/desk/doctype/event/test_event.py b/frappe/desk/doctype/event/test_event.py
index 6d1e865a45..dcfb38bd08 100644
--- a/frappe/desk/doctype/event/test_event.py
+++ b/frappe/desk/doctype/event/test_event.py
@@ -71,7 +71,7 @@ class TestEvent(unittest.TestCase):
ev = frappe.get_doc(self.test_records[0]).insert()
add({
- "assign_to": "test@example.com",
+ "assign_to": ["test@example.com"],
"doctype": "Event",
"name": ev.name,
"description": "Test Assignment"
@@ -83,7 +83,7 @@ class TestEvent(unittest.TestCase):
# add another one
add({
- "assign_to": self.test_user,
+ "assign_to": [self.test_user],
"doctype": "Event",
"name": ev.name,
"description": "Test Assignment"
diff --git a/frappe/desk/doctype/notification_log/test_notification_log.py b/frappe/desk/doctype/notification_log/test_notification_log.py
index fe7d56c081..e59aee30c9 100644
--- a/frappe/desk/doctype/notification_log/test_notification_log.py
+++ b/frappe/desk/doctype/notification_log/test_notification_log.py
@@ -13,7 +13,7 @@ class TestNotificationLog(unittest.TestCase):
user = get_user()
assign_task({
- "assign_to": user,
+ "assign_to": [user],
"doctype": 'ToDo',
"name": todo.name,
"description": todo.description
diff --git a/frappe/desk/form/assign_to.py b/frappe/desk/form/assign_to.py
index 76c7caa63d..a916cbca82 100644
--- a/frappe/desk/form/assign_to.py
+++ b/frappe/desk/form/assign_to.py
@@ -11,6 +11,7 @@ from frappe.desk.doctype.notification_log.notification_log import enqueue_create
get_title, get_title_html
import frappe.utils
import frappe.share
+import json
class DuplicateToDoError(frappe.ValidationError): pass
@@ -19,17 +20,17 @@ def get(args=None):
if not args:
args = frappe.local.form_dict
- return frappe.get_all('ToDo', fields = ['owner', 'description'], filters = dict(
+ return frappe.get_all('ToDo', fields=['owner', 'name'], filters=dict(
reference_type = args.get('doctype'),
reference_name = args.get('name'),
status = ('!=', 'Cancelled')
- ), limit = 5)
+ ), limit=5)
@frappe.whitelist()
def add(args=None):
"""add in someone's to do list
args = {
- "assign_to": ,
+ "assign_to": [],
"doctype": ,
"name": ,
"description": ,
@@ -40,56 +41,68 @@ def add(args=None):
if not args:
args = frappe.local.form_dict
- if frappe.db.sql("""SELECT `owner`
- FROM `tabToDo`
- WHERE `reference_type`=%(doctype)s
- AND `reference_name`=%(name)s
- AND `status`='Open'
- AND `owner`=%(assign_to)s""", args):
- frappe.throw(_("Already in user's To Do list"), DuplicateToDoError)
- else:
- from frappe.utils import nowdate
+ users_with_duplicate_todo = []
+ shared_with_users = []
- if not args.get('description'):
- args['description'] = _('Assignment for {0} {1}').format(args['doctype'], args['name'])
-
- d = frappe.get_doc({
- "doctype":"ToDo",
- "owner": args['assign_to'],
+ for assign_to in frappe.parse_json(args.get("assign_to")):
+ filters = {
"reference_type": args['doctype'],
"reference_name": args['name'],
- "description": args.get('description'),
- "priority": args.get("priority", "Medium"),
"status": "Open",
- "date": args.get('date', nowdate()),
- "assigned_by": args.get('assigned_by', frappe.session.user),
- 'assignment_rule': args.get('assignment_rule')
- }).insert(ignore_permissions=True)
+ "owner": assign_to
+ }
- # set assigned_to if field exists
- if frappe.get_meta(args['doctype']).get_field("assigned_to"):
- frappe.db.set_value(args['doctype'], args['name'], "assigned_to", args['assign_to'])
+ if frappe.get_all("ToDo", filters=filters):
+ users_with_duplicate_todo.append(assign_to)
+ else:
+ from frappe.utils import nowdate
- doc = frappe.get_doc(args['doctype'], args['name'])
+ if not args.get('description'):
+ args['description'] = _('Assignment for {0} {1}').format(args['doctype'], args['name'])
- # if assignee does not have permissions, share
- if not frappe.has_permission(doc=doc, user=args['assign_to']):
- frappe.share.add(doc.doctype, doc.name, args['assign_to'])
- frappe.msgprint(_('Shared with user {0} with read access').format(args['assign_to']), alert=True)
+ d = frappe.get_doc({
+ "doctype": "ToDo",
+ "owner": assign_to,
+ "reference_type": args['doctype'],
+ "reference_name": args['name'],
+ "description": args.get('description'),
+ "priority": args.get("priority", "Medium"),
+ "status": "Open",
+ "date": args.get('date', nowdate()),
+ "assigned_by": args.get('assigned_by', frappe.session.user),
+ 'assignment_rule': args.get('assignment_rule')
+ }).insert(ignore_permissions=True)
- # make this document followed by assigned user
- follow_document(args['doctype'], args['name'], args['assign_to'])
+ # set assigned_to if field exists
+ if frappe.get_meta(args['doctype']).get_field("assigned_to"):
+ frappe.db.set_value(args['doctype'], args['name'], "assigned_to", assign_to)
- # notify
- notify_assignment(d.assigned_by, d.owner, d.reference_type, d.reference_name, action='ASSIGN',\
- description=args.get("description"))
+ doc = frappe.get_doc(args['doctype'], args['name'])
+
+ # if assignee does not have permissions, share
+ if not frappe.has_permission(doc=doc, user=assign_to):
+ frappe.share.add(doc.doctype, doc.name, assign_to)
+ shared_with_users.append(assign_to)
+
+ # make this document followed by assigned user
+ follow_document(args['doctype'], args['name'], assign_to)
+
+ # notify
+ notify_assignment(d.assigned_by, d.owner, d.reference_type, d.reference_name, action='ASSIGN',
+ description=args.get("description"))
+
+ if shared_with_users:
+ user_list = format_message_for_assign_to(shared_with_users)
+ frappe.msgprint(_("Shared with the following Users with Read access:{0}").format(user_list, alert=True))
+
+ if users_with_duplicate_todo:
+ user_list = format_message_for_assign_to(users_with_duplicate_todo)
+ frappe.msgprint(_("Already in the following Users ToDo list:{0}").format(user_list, alert=True))
return get(args)
@frappe.whitelist()
def add_multiple(args=None):
- import json
-
if not args:
args = frappe.local.form_dict
@@ -183,3 +196,5 @@ def notify_assignment(assigned_by, owner, doc_type, doc_name, action='CLOSE',
enqueue_create_notification(owner, notification_doc)
+def format_message_for_assign_to(users):
+ return "
" + "
".join(users)
\ No newline at end of file
diff --git a/frappe/public/js/frappe/form/sidebar/assign_to.js b/frappe/public/js/frappe/form/sidebar/assign_to.js
index 61d1789518..95ceb246e6 100644
--- a/frappe/public/js/frappe/form/sidebar/assign_to.js
+++ b/frappe/public/js/frappe/form/sidebar/assign_to.js
@@ -87,23 +87,17 @@ frappe.ui.form.AssignTo = Class.extend({
if(!me.assign_to) {
me.assign_to = new frappe.ui.form.AssignToDialog({
- obj: me,
- method: 'frappe.desk.form.assign_to.add',
+ method: "frappe.desk.form.assign_to.add",
doctype: me.frm.doctype,
docname: me.frm.docname,
- callback: function(r) {
+ frm: me.frm,
+ callback: function (r) {
me.render(r.message);
}
});
}
me.assign_to.dialog.clear();
-
- if(me.frm.meta.title_field) {
- me.assign_to.dialog.set_value("description", me.frm.doc[me.frm.meta.title_field])
- }
-
me.assign_to.dialog.show();
- me.assign_to = null;
},
remove: function(owner) {
var me = this;
@@ -130,81 +124,126 @@ frappe.ui.form.AssignTo = Class.extend({
frappe.ui.form.AssignToDialog = Class.extend({
init: function(opts){
- var me = this
- var dialog = new frappe.ui.Dialog({
- title: __('Add to To Do'),
- fields: [
- { fieldtype: 'Link', fieldname: 'assign_to', options: 'User', label: __("Assign To"), reqd: true, filters: { 'user_type': 'System User' }},
- { fieldtype: 'Check', fieldname: 'myself', label: __("Assign to me"), "default": 0 },
- { fieldtype: 'Small Text', fieldname: 'description', label: __("Comment") },
- { fieldtype: 'Section Break' },
- { fieldtype: 'Column Break' },
- { fieldtype: 'Date', fieldname: 'date', label: __("Complete By") },
- { fieldtype: 'Column Break' },
- { fieldtype: 'Select', fieldname: 'priority', label: __("Priority"),
- options: [
- { value: 'Low', label: __('Low') },
- { value: 'Medium', label: __('Medium') },
- { value: 'High', label: __('High') }
- ],
- // Pick up priority from the source document, if it exists and is available in ToDo
- 'default': ["Low", "Medium", "High"].includes(opts.obj.frm && opts.obj.frm.doc.priority
- ? opts.obj.frm.doc.priority : 'Medium')
- },
- ],
- primary_action: function() { frappe.ui.add_assignment(opts, this) },
- primary_action_label: __("Add")
- })
- $.extend(me, dialog);
+ $.extend(this, opts);
- me.dialog = dialog;
-
- me.dialog.fields_dict.assign_to.get_query = "frappe.core.doctype.user.user.user_query";
-
- var myself = me.dialog.get_input("myself").on("click", function() {
- me.toggle_myself(this);
- });
- me.toggle_myself(myself);
- },
- toggle_myself: function(myself) {
- var me = this;
- if($(myself).prop("checked")) {
- me.dialog.set_value("assign_to", frappe.session.user);
- me.dialog.get_field("notify").$wrapper.toggle(false);
- me.dialog.get_field("assign_to").$wrapper.toggle(false);
- } else {
- me.dialog.set_value("assign_to", "");
- me.dialog.get_field("assign_to").$wrapper.toggle(true);
- }
+ this.make();
+ this.set_description_from_doc();
},
+ make: function() {
+ let me = this;
-});
+ me.dialog = new frappe.ui.Dialog({
+ title: __('Add to ToDo'),
+ fields: me.get_fields(),
+ primary_action_label: __("Add"),
+ primary_action: function() {
+ let args = me.dialog.get_values();
-frappe.ui.add_assignment = function(opts, dialog) {
- var assign_to = dialog.fields_dict.assign_to.get_value();
- var args = dialog.get_values();
- if(args && assign_to) {
- dialog.set_message('Assigning...');
- return frappe.call({
- method: opts.method,
- args: $.extend(args, {
- doctype: opts.doctype,
- name: opts.docname,
- assign_to: assign_to,
- bulk_assign: opts.bulk_assign || false,
- re_assign: opts.re_assign || false
- }),
- btn: dialog.get_primary_btn(),
- callback: function(r) {
- if(!r.exc) {
- if(opts.callback){
- opts.callback(r);
- }
- dialog && dialog.hide();
- } else {
- dialog.clear_message();
+ if (args && args.assign_to) {
+ me.dialog.set_message("Assigning...");
+
+ frappe.call({
+ method: me.method,
+ args: $.extend(args, {
+ doctype: me.doctype,
+ name: me.docname,
+ assign_to: args.assign_to,
+ bulk_assign: me.bulk_assign || false,
+ re_assign: me.re_assign || false
+ }),
+ btn: me.dialog.get_primary_btn(),
+ callback: function(r) {
+ if (!r.exc) {
+ if (me.callback) {
+ me.callback(r);
+ }
+ me.dialog && me.dialog.hide();
+ } else {
+ me.dialog.clear_message();
+ }
+ },
+ });
}
},
});
+ },
+ assign_to_me: function() {
+ let me = this;
+ let assign_to = [];
+
+ if (me.dialog.get_value("assign_to_me")) {
+ assign_to.push(frappe.session.user);
+ }
+
+ me.dialog.set_value("assign_to", assign_to);
+ },
+ set_description_from_doc: function() {
+ let me = this;
+
+ if (me.frm && me.frm.meta.title_field) {
+ me.dialog.set_value("description", me.frm.doc[me.frm.meta.title_field]);
+ }
+ },
+ get_fields: function() {
+ let me = this;
+
+ return [
+ {
+ fieldtype: 'MultiSelectPills',
+ fieldname: 'assign_to',
+ label: __("Assign To"),
+ reqd: true,
+ get_data: function(txt) {
+ return frappe.db.get_link_options("User", txt, {user_type: "System User", enabled: 1});
+ }
+ },
+ {
+ label: __("Assign to me"),
+ fieldtype: 'Check',
+ fieldname: 'assign_to_me',
+ default: 0,
+ onchange: () => me.assign_to_me()
+ },
+ {
+ label: __("Comment"),
+ fieldtype: 'Small Text',
+ fieldname: 'description'
+ },
+ {
+ fieldtype: 'Section Break'
+ },
+ {
+ fieldtype: 'Column Break'
+ },
+ {
+ label: __("Complete By"),
+ fieldtype: 'Date',
+ fieldname: 'date'
+ },
+ {
+ fieldtype: 'Column Break'
+ },
+ {
+ label: __("Priority"),
+ fieldtype: 'Select',
+ fieldname: 'priority',
+ options: [
+ {
+ value: 'Low',
+ label: __('Low')
+ },
+ {
+ value: 'Medium',
+ label: __('Medium')
+ },
+ {
+ value: 'High',
+ label: __('High')
+ }
+ ],
+ // Pick up priority from the source document, if it exists and is available in ToDo
+ default: ["Low", "Medium", "High"].includes(me.frm && me.frm.doc.priority ? me.frm.doc.priority : 'Medium')
+ }
+ ];
}
-}
+});
diff --git a/frappe/social/doctype/energy_point_log/test_energy_point_log.py b/frappe/social/doctype/energy_point_log/test_energy_point_log.py
index 9e4d0e6425..1ecaba9cd5 100644
--- a/frappe/social/doctype/energy_point_log/test_energy_point_log.py
+++ b/frappe/social/doctype/energy_point_log/test_energy_point_log.py
@@ -306,7 +306,7 @@ def get_points(user, point_type='energy_points'):
def assign_users_to_todo(todo_name, users):
for user in users:
assign_to({
- 'assign_to': user,
+ 'assign_to': [user],
'doctype': 'ToDo',
'name': todo_name
})
\ No newline at end of file
diff --git a/frappe/tests/test_assign.py b/frappe/tests/test_assign.py
index f32e3c9272..439e1546c0 100644
--- a/frappe/tests/test_assign.py
+++ b/frappe/tests/test_assign.py
@@ -60,7 +60,7 @@ class TestAssign(unittest.TestCase):
def assign(doc, user):
return frappe.desk.form.assign_to.add({
- "assign_to": user,
+ "assign_to": [user],
"doctype": doc.doctype,
"name": doc.name,
"description": 'test',