Show Assigned To in list view
Paired between Anand and Rushabh
This commit is contained in:
parent
41aada3b5a
commit
023b5cbcdf
18 changed files with 196 additions and 45 deletions
|
|
@ -6,6 +6,7 @@
|
|||
import frappe
|
||||
import frappe.defaults
|
||||
import unittest
|
||||
import json
|
||||
|
||||
test_records = frappe.get_test_records('Event')
|
||||
|
||||
|
|
@ -53,3 +54,43 @@ class TestEvent(unittest.TestCase):
|
|||
# the name should be same!
|
||||
self.assertEquals(ev.name, name)
|
||||
|
||||
def test_assign(self):
|
||||
from frappe.widgets.form.assign_to import add
|
||||
|
||||
ev = frappe.get_doc(test_records[0]).insert()
|
||||
|
||||
add({
|
||||
"assign_to": "test@example.com",
|
||||
"doctype": "Event",
|
||||
"name": ev.name,
|
||||
"description": "Test Assignment"
|
||||
})
|
||||
|
||||
ev = frappe.get_doc("Event", ev.name)
|
||||
|
||||
self.assertEquals(ev._assign, json.dumps(["test@example.com"]))
|
||||
|
||||
# add another one
|
||||
add({
|
||||
"assign_to": "test1@example.com",
|
||||
"doctype": "Event",
|
||||
"name": ev.name,
|
||||
"description": "Test Assignment"
|
||||
})
|
||||
|
||||
ev = frappe.get_doc("Event", ev.name)
|
||||
|
||||
self.assertEquals(ev._assign, json.dumps(["test@example.com", "test1@example.com"]))
|
||||
|
||||
# close an assignment
|
||||
todo = frappe.get_doc("ToDo", {"reference_type": ev.doctype, "reference_name": ev.name,
|
||||
"owner": "test1@example.com"})
|
||||
todo.status = "Closed"
|
||||
todo.save()
|
||||
|
||||
ev = frappe.get_doc("Event", ev.name)
|
||||
self.assertEquals(ev._assign, json.dumps(["test@example.com"]))
|
||||
|
||||
# cleanup
|
||||
ev.delete()
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import json
|
||||
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
|
@ -15,11 +16,17 @@ class ToDo(Document):
|
|||
if cur_status != self.status:
|
||||
self.add_comment(frappe._("Assignment Status Changed"), "Assignment Completed")
|
||||
|
||||
def on_update(self):
|
||||
self.update_in_reference()
|
||||
|
||||
def on_trash(self):
|
||||
self.update_in_reference()
|
||||
|
||||
def add_comment(self, text, comment_type):
|
||||
if not self.reference_type and self.reference_name:
|
||||
return
|
||||
|
||||
comment = frappe.get_doc({
|
||||
frappe.get_doc({
|
||||
"doctype":"Comment",
|
||||
"comment_by": frappe.session.user,
|
||||
"comment_type": comment_type,
|
||||
|
|
@ -32,6 +39,36 @@ class ToDo(Document):
|
|||
description = self.description)
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
def update_in_reference(self):
|
||||
if not (self.reference_type and self.reference_name):
|
||||
return
|
||||
|
||||
try:
|
||||
assignments = [d[0] for d in frappe.get_list("ToDo",
|
||||
filters={
|
||||
"reference_type": self.reference_type,
|
||||
"reference_name": self.reference_name,
|
||||
"status": "Open"
|
||||
},
|
||||
fields=["owner"], ignore_permissions=True, as_list=True)]
|
||||
|
||||
assignments.reverse()
|
||||
frappe.db.set_value(self.reference_type, self.reference_name,
|
||||
"_assign", json.dumps(assignments))
|
||||
|
||||
except Exception, e:
|
||||
if e.args[0] == 1146 and frappe.flags.in_install:
|
||||
# no table
|
||||
return
|
||||
|
||||
elif e.args[0]==1054:
|
||||
from frappe.model.db_schema import add_column
|
||||
add_column(self.reference_type, "_assign", "Text")
|
||||
self.update_in_reference()
|
||||
|
||||
else:
|
||||
raise
|
||||
|
||||
# NOTE: todo is viewable if either owner or assigned_to or System Manager in roles
|
||||
|
||||
def get_permission_query_conditions(user):
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ class DatabaseQuery(object):
|
|||
columns = frappe.db.get_table_columns(self.doctype)
|
||||
to_remove = []
|
||||
for fld in self.fields:
|
||||
for f in ("_user_tags", "_comments"):
|
||||
for f in ("_user_tags", "_comments", "_assign"):
|
||||
if f in fld and not f in columns:
|
||||
to_remove.append(fld)
|
||||
|
||||
|
|
|
|||
|
|
@ -413,6 +413,9 @@ class Document(BaseDocument):
|
|||
self.docstatus = 2
|
||||
self.save()
|
||||
|
||||
def delete(self):
|
||||
frappe.delete_doc(self.doctype, self.name)
|
||||
|
||||
def run_before_save_methods(self):
|
||||
if getattr(self, "ignore_validate", False):
|
||||
return
|
||||
|
|
|
|||
|
|
@ -51,3 +51,4 @@ frappe.patches.v4_1.file_manager_fix
|
|||
frappe.patches.v4_2.print_with_letterhead
|
||||
execute:frappe.delete_doc("DocType", "Control Panel", force=1)
|
||||
frappe.patches.v4_2.refactor_website_routing
|
||||
frappe.patches.v4_2.set_assign_in_doc
|
||||
|
|
|
|||
6
frappe/patches/v4_2/set_assign_in_doc.py
Normal file
6
frappe/patches/v4_2/set_assign_in_doc.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import frappe
|
||||
|
||||
def execute():
|
||||
for name in frappe.db.sql_list("""select name from `tabToDo`
|
||||
where ifnull(reference_type, '')!='' and ifnull(reference_name, '')!=''"""):
|
||||
frappe.get_doc("ToDo", name).on_update()
|
||||
|
|
@ -71,6 +71,7 @@
|
|||
"public/js/lib/microtemplate.js",
|
||||
|
||||
"public/html/print_template.html",
|
||||
"public/html/list_info_template.html",
|
||||
|
||||
"public/js/legacy/globals.js",
|
||||
"public/js/legacy/datatype.js",
|
||||
|
|
|
|||
|
|
@ -25,3 +25,12 @@
|
|||
width: 72px;
|
||||
height: 72px;
|
||||
}
|
||||
|
||||
.avatar-xs {
|
||||
margin-right: 3px;
|
||||
margin-top: -2px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ div#freeze {
|
|||
}
|
||||
|
||||
.list-row {
|
||||
padding: 5px 15px;
|
||||
padding: 5px 15px 10px;
|
||||
margin: 0px -15px;
|
||||
border-bottom: 1px solid #c7c7c7;
|
||||
}
|
||||
|
|
@ -177,7 +177,7 @@ div#freeze {
|
|||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.doclist-row .filterable {
|
||||
.filterable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
|
@ -188,7 +188,7 @@ div#freeze {
|
|||
.list-timestamp {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
bottom: 2px;
|
||||
bottom: 5px;
|
||||
font-size: 70%;
|
||||
color: #888;
|
||||
}
|
||||
|
|
|
|||
17
frappe/public/html/list_info_template.html
Normal file
17
frappe/public/html/list_info_template.html
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{% if (tags.length) { %}
|
||||
<span style="margin-right: 10px;" class="list-tag-preview">
|
||||
{%= tags.join(", ") %}</span>
|
||||
{% } %}
|
||||
{% if (comments.length) { %}
|
||||
<a style="margin-right: 10px;" href="#Form/{%= doctype %}/{%= encodeURIComponent(data.name) %}"
|
||||
title="{%= comments[comments.length-1].comment %}">
|
||||
<i class="icon-comments"></i> {%= comments.length %}
|
||||
</a>
|
||||
{% } %}
|
||||
{% if (assign.length) { %}
|
||||
{% for (var i=0, l=assign.length; i<l; i++) { %}
|
||||
<span class="filterable" data-filter="_assign,like,%{%= assign[i] %}%">
|
||||
{%= frappe.avatar(assign[i], "avatar-xs") %}</span>
|
||||
{% }%}
|
||||
{% } %}
|
||||
{%= comment_when(data.modified) %}
|
||||
|
|
@ -101,6 +101,15 @@ frappe.form.formatters = {
|
|||
});
|
||||
return html;
|
||||
},
|
||||
Assign: function(value) {
|
||||
var html = "";
|
||||
$.each(JSON.parse(value || "[]"), function(i, v) {
|
||||
if(v) html+= '<span class="label label-warning" \
|
||||
style="margin-right: 7px;"\
|
||||
data-field="_assign">'+v+'</span>';
|
||||
});
|
||||
return html;
|
||||
},
|
||||
SmallText: function(value) {
|
||||
return frappe.form.formatters.Text(value);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -12,16 +12,15 @@ frappe.user_info = function(uid) {
|
|||
return frappe.boot.user_info[uid];
|
||||
}
|
||||
|
||||
frappe.avatar = function(user, large, title) {
|
||||
frappe.avatar = function(user, css_class, title) {
|
||||
var image = frappe.utils.get_file_link(frappe.user_info(user).image);
|
||||
var to_size = large ? 72 : 30;
|
||||
if(!title) title = frappe.user_info(user).fullname;
|
||||
|
||||
return repl('<span class="avatar %(small_or_large)s" title="%(title)s">\
|
||||
return repl('<span class="avatar %(css_class)s" title="%(title)s">\
|
||||
<img src="%(image)s"></span>', {
|
||||
image: image,
|
||||
title: title,
|
||||
small_or_large: large ? "avatar-large" : "avatar-small"
|
||||
css_class: css_class || "avatar-small"
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -61,9 +60,6 @@ $.extend(frappe.user, {
|
|||
image: function(uid) {
|
||||
return frappe.user_info(uid).image;
|
||||
},
|
||||
avatar: function(uid, large) {
|
||||
return frappe.avatar(uid, large);
|
||||
},
|
||||
has_role: function(rl) {
|
||||
if(typeof rl=='string')
|
||||
rl = [rl];
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ $.extend(frappe.model, {
|
|||
layout_fields: ['Section Break', 'Column Break', 'Fold'],
|
||||
|
||||
std_fields_list: ['name', 'owner', 'creation', 'modified', 'modified_by',
|
||||
'_user_tags', '_comments', 'docstatus', 'parent', 'parenttype', 'parentfield', 'idx'],
|
||||
'_user_tags', '_comments', '_assign', 'docstatus',
|
||||
'parent', 'parenttype', 'parentfield', 'idx'],
|
||||
|
||||
std_fields: [
|
||||
{fieldname:'name', fieldtype:'Link', label:__('ID')},
|
||||
{fieldname:'owner', fieldtype:'Data', label:__('Created By')},
|
||||
|
|
@ -20,6 +22,7 @@ $.extend(frappe.model, {
|
|||
{fieldname:'modified_by', fieldtype:'Data', label:__('Last Updated By')},
|
||||
{fieldname:'_user_tags', fieldtype:'Data', label:__('Tags')},
|
||||
{fieldname:'_comments', fieldtype:'Text', label:__('Comments')},
|
||||
{fieldname:'_assign', fieldtype:'Text', label:__('Assigned To')},
|
||||
{fieldname:'docstatus', fieldtype:'Int', label:__('Document Status')},
|
||||
],
|
||||
|
||||
|
|
|
|||
|
|
@ -361,7 +361,7 @@ frappe.ui.Listing = Class.extend({
|
|||
if(fieldname=='_user_tags') {
|
||||
// and for tags
|
||||
this.filter_list.add_filter(doctype, fieldname,
|
||||
'like', '%' + label);
|
||||
'like', '%' + label + '%');
|
||||
} else {
|
||||
// or for rest using "in"
|
||||
filter.set_values(doctype, fieldname, 'in', v + ', ' + label);
|
||||
|
|
@ -370,9 +370,9 @@ frappe.ui.Listing = Class.extend({
|
|||
} else {
|
||||
// no filter for this item,
|
||||
// setup one
|
||||
if(['_user_tags', '_comments'].indexOf(fieldname)!==-1) {
|
||||
if(['_user_tags', '_comments', '_assign'].indexOf(fieldname)!==-1) {
|
||||
this.filter_list.add_filter(doctype, fieldname,
|
||||
'like', '%' + label);
|
||||
'like', '%' + label + '%');
|
||||
} else {
|
||||
this.filter_list.add_filter(doctype, fieldname, '=', label);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ frappe.ui.TagEditor = Class.extend({
|
|||
init: function(opts) {
|
||||
/* docs:
|
||||
Arguments
|
||||
|
||||
|
||||
- parent
|
||||
- user_tags
|
||||
- doctype
|
||||
|
|
@ -20,21 +20,38 @@ frappe.ui.TagEditor = Class.extend({
|
|||
placeholderText: __('Add Tag'),
|
||||
onTagAdded: function(ev, tag) {
|
||||
if(me.initialized && !me.refreshing) {
|
||||
var tag = tag.find('.tagit-label').text();
|
||||
return frappe.call({
|
||||
method: 'frappe.widgets.tags.add_tag',
|
||||
args: me.get_args(tag.find('.tagit-label').text())
|
||||
});
|
||||
args: me.get_args(tag),
|
||||
callback: function(r) {
|
||||
var user_tags = me.user_tags.split(",");
|
||||
user_tags.push(tag)
|
||||
me.user_tags = user_tags.join(",");
|
||||
me.on_change && me.on_change(me.user_tags);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
onTagRemoved: function(ev, tag) {
|
||||
if(!me.refreshing) {
|
||||
var tag = tag.find('.tagit-label').text();
|
||||
return frappe.call({
|
||||
method: 'frappe.widgets.tags.remove_tag',
|
||||
args: me.get_args(tag.find('.tagit-label').text())
|
||||
args: me.get_args(tag),
|
||||
callback: function(r) {
|
||||
var user_tags = me.user_tags.split(",");
|
||||
user_tags.splice(user_tags.indexOf(tag), 1);
|
||||
me.user_tags = user_tags.join(",");
|
||||
me.on_change && me.on_change(me.user_tags);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
if (!this.user_tags) {
|
||||
this.user_tags = "";
|
||||
}
|
||||
this.refresh(this.user_tags);
|
||||
this.initialized = true;
|
||||
},
|
||||
|
|
@ -51,15 +68,15 @@ frappe.ui.TagEditor = Class.extend({
|
|||
me.refreshing = true;
|
||||
me.$tags.tagit("removeAll");
|
||||
|
||||
if(!user_tags && this.frm)
|
||||
if(!user_tags && this.frm)
|
||||
user_tags = frappe.model.get_value(this.frm.doctype, this.frm.docname, "_user_tags");
|
||||
|
||||
|
||||
if(user_tags) {
|
||||
$.each(user_tags.split(','), function(i, v) {
|
||||
me.$tags.tagit("createTag", v);
|
||||
});
|
||||
}
|
||||
me.refreshing = false;
|
||||
|
||||
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ frappe.views.ListView = Class.extend({
|
|||
}
|
||||
|
||||
$.each(['name', 'owner', 'docstatus', '_user_tags', '_comments', 'modified',
|
||||
'modified_by'], function(i, fieldname) { add_field(fieldname); })
|
||||
'modified_by', '_assign'], function(i, fieldname) { add_field(fieldname); })
|
||||
|
||||
// add title field
|
||||
if(this.meta.title_field) {
|
||||
|
|
@ -221,6 +221,7 @@ frappe.views.ListView = Class.extend({
|
|||
var comments = data._comments ? JSON.parse(data._comments) : [],
|
||||
tags = $.map((data._user_tags || "").split(","),
|
||||
function(v) { return v ? v : null; }),
|
||||
assign = data._assign ? JSON.parse(data._assign) : [],
|
||||
me = this;
|
||||
|
||||
if(me.title_field && data[me.title_field]!==data.name) {
|
||||
|
|
@ -230,24 +231,18 @@ frappe.views.ListView = Class.extend({
|
|||
.html('<a href="#Form/'+ data.doctype+'/'+data.name +'">#' + data.name + "</a>");
|
||||
}
|
||||
|
||||
$(row).find(".list-timestamp").remove();
|
||||
|
||||
var timestamp_and_comment =
|
||||
$('<div class="list-timestamp">')
|
||||
.appendTo(row)
|
||||
.html(""
|
||||
+ (tags.length ? (
|
||||
'<span style="margin-right: 10px;" class="list-tag-preview">' + tags.join(", ") + '</span>'
|
||||
): "")
|
||||
+ (comments.length ?
|
||||
('<a style="margin-right: 10px;" href="#Form/'+
|
||||
this.doctype + '/' + data.name
|
||||
+'" title="'+
|
||||
comments[comments.length-1].comment
|
||||
+'"><i class="icon-comments"></i> '
|
||||
+ comments.length + " " + (
|
||||
comments.length===1 ? __("comment") : __("comments")) + '</a>')
|
||||
: "")
|
||||
+ comment_when(data.modified));
|
||||
|
||||
.html(frappe.render(frappe.templates.list_info_template, {
|
||||
"tags": tags,
|
||||
"comments": comments,
|
||||
"assign": assign,
|
||||
"data": data,
|
||||
"doctype": this.doctype
|
||||
}));
|
||||
},
|
||||
|
||||
render_tags: function(row, data) {
|
||||
|
|
@ -272,7 +267,11 @@ frappe.views.ListView = Class.extend({
|
|||
doctype: this.doctype,
|
||||
docname: data.name
|
||||
},
|
||||
user_tags: data._user_tags
|
||||
user_tags: data._user_tags,
|
||||
on_change: function(user_tags) {
|
||||
data._user_tags = user_tags;
|
||||
me.render_timestamp_and_comments(row, data);
|
||||
}
|
||||
});
|
||||
tag_editor.$w.on("click", ".tagit-label", function() {
|
||||
me.doclistview.set_filter("_user_tags",
|
||||
|
|
|
|||
|
|
@ -220,8 +220,12 @@ frappe.views.ReportView = frappe.ui.Listing.extend({
|
|||
width: (docfield ? cint(docfield.width) : 120) || 120,
|
||||
formatter: function(row, cell, value, columnDef, dataContext) {
|
||||
var docfield = columnDef.docfield;
|
||||
if(docfield.fieldname==="_user_tags") docfield.fieldtype = "Tag";
|
||||
if(docfield.fieldname==="_comments") docfield.fieldtype = "Comment";
|
||||
docfield.fieldtype = {
|
||||
"_user_tags": "Tag",
|
||||
"_comments": "Comment",
|
||||
"_assign": "Assign"
|
||||
}[docfield.fieldname] || docfield.fieldtype;
|
||||
|
||||
if(docfield.fieldtype==="Link" && docfield.fieldname!=="name") {
|
||||
docfield.link_onclick =
|
||||
repl('frappe.container.page.reportview.set_filter("%(fieldname)s", "%(value)s").page.reportview.run()',
|
||||
|
|
|
|||
|
|
@ -22,7 +22,15 @@ def get(args=None):
|
|||
|
||||
@frappe.whitelist()
|
||||
def add(args=None):
|
||||
"""add in someone's to do list"""
|
||||
"""add in someone's to do list
|
||||
args = {
|
||||
"assign_to": ,
|
||||
"doctype": ,
|
||||
"name": ,
|
||||
"description":
|
||||
}
|
||||
|
||||
"""
|
||||
if not args:
|
||||
args = frappe.local.form_dict
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue