@@ -195,5 +195,4 @@ def get_badge_info(doctypes, filters): | |||||
def run_onload(doc): | def run_onload(doc): | ||||
doc.set("__onload", frappe._dict()) | doc.set("__onload", frappe._dict()) | ||||
doc.set_onload('links', doc.meta.get_links_setup()) | |||||
doc.run_method("onload") | doc.run_method("onload") |
@@ -40,12 +40,13 @@ class FormMeta(Meta): | |||||
self.load_print_formats() | self.load_print_formats() | ||||
self.load_workflows() | self.load_workflows() | ||||
self.load_templates() | self.load_templates() | ||||
self.load_dashboard() | |||||
def as_dict(self, no_nulls=False): | def as_dict(self, no_nulls=False): | ||||
d = super(FormMeta, self).as_dict(no_nulls=no_nulls) | d = super(FormMeta, self).as_dict(no_nulls=no_nulls) | ||||
for k in ("__js", "__css", "__list_js", "__calendar_js", "__map_js", | for k in ("__js", "__css", "__list_js", "__calendar_js", "__map_js", | ||||
"__linked_with", "__messages", "__print_formats", "__workflow_docs", | "__linked_with", "__messages", "__print_formats", "__workflow_docs", | ||||
"__form_grid_templates", "__listview_template", "__tree_js"): | |||||
"__form_grid_templates", "__listview_template", "__tree_js", '__dashboard'): | |||||
d[k] = self.get(k) | d[k] = self.get(k) | ||||
for i, df in enumerate(d.get("fields")): | for i, df in enumerate(d.get("fields")): | ||||
@@ -164,6 +165,9 @@ class FormMeta(Meta): | |||||
messages = make_dict_from_messages(messages) | messages = make_dict_from_messages(messages) | ||||
self.get("__messages").update(messages, as_value=True) | self.get("__messages").update(messages, as_value=True) | ||||
def load_dashboard(self): | |||||
self.set('__dashboard', self.get_dashboard_data()) | |||||
def get_code_files_via_hooks(hook, name): | def get_code_files_via_hooks(hook, name): | ||||
code_files = [] | code_files = [] | ||||
for app_name in frappe.get_installed_apps(): | for app_name in frappe.get_installed_apps(): | ||||
@@ -180,7 +180,7 @@ def get_open_count(doctype, name): | |||||
frappe.has_permission(doc=frappe.get_doc(doctype, name), throw=True) | frappe.has_permission(doc=frappe.get_doc(doctype, name), throw=True) | ||||
meta = frappe.get_meta(doctype) | meta = frappe.get_meta(doctype) | ||||
links = meta.get_links_setup() | |||||
links = meta.get_dashboard_data() | |||||
# compile all items in a list | # compile all items in a list | ||||
items = [] | items = [] | ||||
@@ -96,7 +96,6 @@ frappe.ui.form.on("Email Account", { | |||||
frm.events.show_gmail_message_for_less_secure_apps(frm); | frm.events.show_gmail_message_for_less_secure_apps(frm); | ||||
}, | }, | ||||
show_gmail_message_for_less_secure_apps: function(frm) { | show_gmail_message_for_less_secure_apps: function(frm) { | ||||
frm.dashboard.reset(); | |||||
if(frm.doc.service==="Gmail") { | if(frm.doc.service==="Gmail") { | ||||
frm.dashboard.set_headline_alert('Gmail will only work if you allow access for less secure \ | frm.dashboard.set_headline_alert('Gmail will only work if you allow access for less secure \ | ||||
apps in Gmail settings. <a target="_blank" \ | apps in Gmail settings. <a target="_blank" \ | ||||
@@ -32,7 +32,6 @@ cur_frm.cscript.refresh = function(doc) { | |||||
} | } | ||||
cur_frm.cscript.setup_dashboard = function() { | cur_frm.cscript.setup_dashboard = function() { | ||||
cur_frm.dashboard.reset(); | |||||
if(!cur_frm.doc.__islocal && cint(cur_frm.doc.email_sent) && cur_frm.doc.__onload && cur_frm.doc.__onload.status_count) { | if(!cur_frm.doc.__islocal && cint(cur_frm.doc.email_sent) && cur_frm.doc.__onload && cur_frm.doc.__onload.status_count) { | ||||
var stat = cur_frm.doc.__onload.status_count; | var stat = cur_frm.doc.__onload.status_count; | ||||
var total = frappe.utils.sum($.map(stat, function(v) { return v; })); | var total = frappe.utils.sum($.map(stat, function(v) { return v; })); | ||||
@@ -263,17 +263,18 @@ class Meta(Document): | |||||
return self.high_permlevel_fields | return self.high_permlevel_fields | ||||
def get_links_setup(self): | |||||
'''Returns setup for documents related to this doctype. | |||||
def get_dashboard_data(self): | |||||
'''Returns dashboard setup related to this doctype. | |||||
This method will return the `links_setup` property in the | |||||
`[doctype]_links.py` file in the doctype folder''' | |||||
This method will return the `data` property in the | |||||
`[doctype]_dashboard.py` file in the doctype folder''' | |||||
try: | try: | ||||
module = load_doctype_module(self.name, suffix='_links') | |||||
return frappe._dict(module.links) | |||||
module = load_doctype_module(self.name, suffix='_dashboard') | |||||
data = frappe._dict(module.data) | |||||
except ImportError: | except ImportError: | ||||
return frappe._dict() | |||||
data = frappe._dict() | |||||
return data | |||||
doctype_table_fields = [ | doctype_table_fields = [ | ||||
frappe._dict({"fieldname": "fields", "options": "DocField"}), | frappe._dict({"fieldname": "fields", "options": "DocField"}), | ||||
@@ -25,7 +25,6 @@ frappe.ui.form.Dashboard = Class.extend({ | |||||
// clear links | // clear links | ||||
this.links_area.addClass('hidden'); | this.links_area.addClass('hidden'); | ||||
this.transactions_area.empty(); | |||||
// clear stats | // clear stats | ||||
this.stats_area.empty().addClass('hidden'); | this.stats_area.empty().addClass('hidden'); | ||||
@@ -112,25 +111,44 @@ frappe.ui.form.Dashboard = Class.extend({ | |||||
return progress_chart; | return progress_chart; | ||||
}, | }, | ||||
// | |||||
show_dashboard: function() { | |||||
refresh: function() { | |||||
this.reset(); | this.reset(); | ||||
if(this.frm.doc.__islocal) | |||||
if(this.frm.doc.__islocal) { | |||||
return; | return; | ||||
} | |||||
if(!this.links) { | |||||
this.links = this.frm.doc.__onload.links; | |||||
if(!this.data) { | |||||
this.data = this.frm.meta.__dashboard; | |||||
this.filter_permissions(); | this.filter_permissions(); | ||||
} | } | ||||
this.render_links(); | |||||
this.set_open_count(); | |||||
this.render_heatmap(); | |||||
var show = false; | |||||
if(this.data && (this.data.transactions || []).length) { | |||||
if(this.data.docstatus && this.frm.doc.docstatus !== this.data.docstatus) { | |||||
// limited docstatus | |||||
return; | |||||
} | |||||
this.render_links(); | |||||
this.set_open_count(); | |||||
show = true; | |||||
} | |||||
if(this.data.heatmap) { | |||||
this.render_heatmap(); | |||||
show = true; | |||||
} | |||||
if(show) { | |||||
this.show(); | |||||
} | |||||
}, | }, | ||||
filter_permissions: function() { | filter_permissions: function() { | ||||
// filter out transactions for which the user | // filter out transactions for which the user | ||||
// does not have permission | // does not have permission | ||||
var transactions = []; | var transactions = []; | ||||
(this.links.transactions || []).forEach(function(group) { | |||||
(this.data.transactions || []).forEach(function(group) { | |||||
var items = []; | var items = []; | ||||
group.items.forEach(function(doctype) { | group.items.forEach(function(doctype) { | ||||
if(frappe.model.can_read(doctype)) { | if(frappe.model.can_read(doctype)) { | ||||
@@ -145,12 +163,17 @@ frappe.ui.form.Dashboard = Class.extend({ | |||||
transactions.push(group); | transactions.push(group); | ||||
} | } | ||||
}); | }); | ||||
this.links.transactions = transactions; | |||||
this.data.transactions = transactions; | |||||
}, | }, | ||||
render_links: function() { | render_links: function() { | ||||
var me = this; | var me = this; | ||||
this.links_area.removeClass('hidden'); | |||||
if(this.data_rendered) { | |||||
return; | |||||
} | |||||
$(frappe.render_template('form_links', | $(frappe.render_template('form_links', | ||||
{transactions: this.links.transactions})) | |||||
{transactions: this.data.transactions})) | |||||
.appendTo(this.transactions_area) | .appendTo(this.transactions_area) | ||||
// bind links | // bind links | ||||
@@ -163,8 +186,7 @@ frappe.ui.form.Dashboard = Class.extend({ | |||||
me.open_document_list($(this).parent().attr('data-doctype'), true); | me.open_document_list($(this).parent().attr('data-doctype'), true); | ||||
}); | }); | ||||
this.show(); | |||||
this.links_area.removeClass('hidden'); | |||||
this.data_rendered = true; | |||||
}, | }, | ||||
open_document_list: function(doctype, show_open) { | open_document_list: function(doctype, show_open) { | ||||
// show document list with filters | // show document list with filters | ||||
@@ -179,22 +201,26 @@ frappe.ui.form.Dashboard = Class.extend({ | |||||
// return the default filter for the given document | // return the default filter for the given document | ||||
// like {"customer": frm.doc.name} | // like {"customer": frm.doc.name} | ||||
var filter = {}; | var filter = {}; | ||||
var fieldname = this.links.non_standard_fieldnames | |||||
? (this.links.non_standard_fieldnames[doctype] || this.links.fieldname) | |||||
: this.links.fieldname; | |||||
var fieldname = this.data.non_standard_fieldnames | |||||
? (this.data.non_standard_fieldnames[doctype] || this.data.fieldname) | |||||
: this.data.fieldname; | |||||
filter[fieldname] = this.frm.doc.name; | filter[fieldname] = this.frm.doc.name; | ||||
return filter; | return filter; | ||||
}, | }, | ||||
set_open_count: function() { | set_open_count: function() { | ||||
if(!this.data.transactions) { | |||||
return; | |||||
} | |||||
// list all items from the transaction list | // list all items from the transaction list | ||||
var items = [], | var items = [], | ||||
me = this; | me = this; | ||||
this.links.transactions.forEach(function(group) { | |||||
this.data.transactions.forEach(function(group) { | |||||
group.items.forEach(function(item) { items.push(item); }); | group.items.forEach(function(item) { items.push(item); }); | ||||
}); | }); | ||||
method = this.links.method || 'frappe.desk.notifications.get_open_count'; | |||||
method = this.data.method || 'frappe.desk.notifications.get_open_count'; | |||||
frappe.call({ | frappe.call({ | ||||
type: "GET", | type: "GET", | ||||
@@ -204,7 +230,9 @@ frappe.ui.form.Dashboard = Class.extend({ | |||||
name: this.frm.doc.name, | name: this.frm.doc.name, | ||||
}, | }, | ||||
callback: function(r) { | callback: function(r) { | ||||
me.heatmap && me.heatmap.update(r.message.timeline_data); | |||||
if(r.message.timeline_data) { | |||||
me.update_heatmap(r.message.timeline_data); | |||||
} | |||||
$.each(r.message.count, function(i, d) { | $.each(r.message.count, function(i, d) { | ||||
me.frm.dashboard.set_badge_count(d.name, cint(d.open_count), cint(d.count)); | me.frm.dashboard.set_badge_count(d.name, cint(d.open_count), cint(d.count)); | ||||
}); | }); | ||||
@@ -231,9 +259,15 @@ frappe.ui.form.Dashboard = Class.extend({ | |||||
}, | }, | ||||
update_heatmap: function(data) { | |||||
if(this.heatmap) { | |||||
this.heatmap.update(data); | |||||
} | |||||
}, | |||||
// heatmap | // heatmap | ||||
render_heatmap: function() { | render_heatmap: function() { | ||||
if(this.show_heatmap && !this.heatmap) { | |||||
if(!this.heatmap) { | |||||
this.heatmap = new CalHeatMap(); | this.heatmap = new CalHeatMap(); | ||||
this.heatmap.init({ | this.heatmap.init({ | ||||
itemSelector: "#heatmap-" + this.frm.doctype, | itemSelector: "#heatmap-" + this.frm.doctype, | ||||
@@ -257,8 +291,8 @@ frappe.ui.form.Dashboard = Class.extend({ | |||||
// message | // message | ||||
var heatmap_message = this.heatmap_area.find('.heatmap-message'); | var heatmap_message = this.heatmap_area.find('.heatmap-message'); | ||||
if(this.heatmap_message) { | |||||
heatmap_message.removeClass('hidden').html(this.heatmap_message); | |||||
if(this.data.heatmap_message) { | |||||
heatmap_message.removeClass('hidden').html(this.data.heatmap_message); | |||||
} else { | } else { | ||||
heatmap_message.addClass('hidden'); | heatmap_message.addClass('hidden'); | ||||
} | } | ||||
@@ -346,7 +346,14 @@ _f.Frm.prototype.refresh_header = function(is_a_different_doc) { | |||||
} | } | ||||
this.document_flow.refresh(); | this.document_flow.refresh(); | ||||
this.dashboard.reset(); | |||||
this.dashboard.refresh(); | |||||
if(this.meta.is_submittable && | |||||
! this.is_dirty() && | |||||
! this.is_new() && | |||||
this.doc.docstatus===0) { | |||||
this.dashboard.add_comment(__('Submit this document to confirm'), true); | |||||
} | |||||
this.clear_custom_buttons(); | this.clear_custom_buttons(); | ||||
@@ -827,11 +834,6 @@ _f.Frm.prototype.save_or_update = function() { | |||||
} | } | ||||
} | } | ||||
_f.get_value = function(dt, dn, fn) { | |||||
if(locals[dt] && locals[dt][dn]) | |||||
return locals[dt][dn][fn]; | |||||
} | |||||
_f.Frm.prototype.dirty = function() { | _f.Frm.prototype.dirty = function() { | ||||
this.doc.__unsaved = 1; | this.doc.__unsaved = 1; | ||||
$(this.wrapper).trigger('dirty'); | $(this.wrapper).trigger('dirty'); | ||||
@@ -841,6 +843,15 @@ _f.Frm.prototype.get_docinfo = function() { | |||||
return frappe.model.docinfo[this.doctype][this.docname]; | return frappe.model.docinfo[this.doctype][this.docname]; | ||||
} | } | ||||
_f.Frm.prototype.is_dirty = function() { | |||||
return this.doc.__unsaved; | |||||
} | |||||
_f.Frm.prototype.is_new = function() { | |||||
return this.doc.__islocal; | |||||
} | |||||
_f.Frm.prototype.reload_docinfo = function(callback) { | _f.Frm.prototype.reload_docinfo = function(callback) { | ||||
var me = this; | var me = this; | ||||
frappe.call({ | frappe.call({ | ||||
@@ -42,6 +42,10 @@ _p.preview = function(html) { | |||||
return w | return w | ||||
} | } | ||||
_f.get_value = function(dt, dn, fn) { | |||||
if(locals[dt] && locals[dt][dn]) | |||||
return locals[dt][dn][fn]; | |||||
} | |||||
// _p can be referenced as this inside $.extend | // _p can be referenced as this inside $.extend | ||||
$.extend(_p, { | $.extend(_p, { | ||||