@@ -11,20 +11,16 @@ import frappe.desk.form.load | |||
@frappe.whitelist() | |||
def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None): | |||
key = "linked_with:{doctype}:{name}".format(doctype=doctype, name=name) | |||
if isinstance(linkinfo, basestring): | |||
# additional fields are added in linkinfo | |||
linkinfo = json.loads(linkinfo) | |||
if for_doctype: | |||
key = "{key}:{for_doctype}".format(key=key, for_doctype=for_doctype) | |||
if linkinfo.get(for_doctype): | |||
linkinfo = {for_doctype: linkinfo.get(for_doctype)} | |||
else: | |||
return {} | |||
results = frappe.cache().get_value(key, user=True) | |||
if results: | |||
return results | |||
@@ -34,8 +30,15 @@ def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None): | |||
if not linkinfo: | |||
return results | |||
if for_doctype: | |||
if for_doctype in linkinfo: | |||
# only get linked with for this particular doctype | |||
linkinfo = { for_doctype: linkinfo.get(for_doctype) } | |||
else: | |||
return results | |||
me = frappe.db.get_value(doctype, name, ["parenttype", "parent"], as_dict=True) | |||
for dt, link in linkinfo.items(): | |||
link["doctype"] = dt | |||
link_meta_bundle = frappe.desk.form.load.get_meta_bundle(dt) | |||
@@ -44,13 +47,13 @@ def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None): | |||
fields = [d.fieldname for d in linkmeta.get("fields", {"in_list_view":1, | |||
"fieldtype": ["not in", ["Image", "HTML", "Button", "Table"]]})] \ | |||
+ ["name", "modified", "docstatus"] | |||
if link.get("add_fields"): | |||
fields += link["add_fields"] | |||
fields = ["`tab{dt}`.`{fn}`".format(dt=dt, fn=sf.strip()) for sf in fields if sf | |||
and "`tab" not in sf] | |||
try: | |||
if link.get("get_parent"): | |||
if me and me.parent and me.parenttype == dt: | |||
@@ -693,8 +693,9 @@ class Document(BaseDocument): | |||
# clear linked doctypes list | |||
cache.hdel("linked_doctypes", doctype) | |||
# delete linked with cache for all users | |||
# for all users, delete linked with cache and per doctype linked with cache | |||
cache.delete_value("user:*:linked_with:{doctype}:{name}".format(doctype=doctype, name=name)) | |||
cache.delete_value("user:*:linked_with:{doctype}:{name}:*".format(doctype=doctype, name=name)) | |||
_clear_cache(self) | |||
for d in self.get_all_children(): | |||
@@ -161,12 +161,14 @@ | |||
"public/js/frappe/form/templates/set_sharing.html", | |||
"public/js/frappe/form/templates/form_sidebar.html", | |||
"public/js/frappe/form/templates/form_dashboard.html", | |||
"public/js/frappe/form/templates/form_document_flow.html", | |||
"public/js/frappe/form/templates/form_links.html", | |||
"public/js/frappe/views/formview.js", | |||
"public/js/legacy/form.js", | |||
"public/js/legacy/clientscriptAPI.js", | |||
"public/js/frappe/form/toolbar.js", | |||
"public/js/frappe/form/dashboard.js", | |||
"public/js/frappe/form/document_flow.js", | |||
"public/js/frappe/form/save.js", | |||
"public/js/frappe/form/script_manager.js", | |||
"public/js/frappe/form/grid.js", | |||
@@ -30,6 +30,42 @@ | |||
.form-message { | |||
padding: 15px; | |||
} | |||
.document-flow-wrapper { | |||
padding: 40px 15px 30px; | |||
font-size: 12px; | |||
border-bottom: 1px solid #EBEFF2; | |||
} | |||
.document-flow-wrapper .document-flow { | |||
display: inline-block; | |||
position: relative; | |||
left: 50%; | |||
transform: translateX(-50%); | |||
} | |||
.document-flow-wrapper .document-flow .document-flow-link-wrapper:not(:last-child) { | |||
border-top: 1px solid #b8c2cc; | |||
padding-right: 60px; | |||
display: inline-block; | |||
margin-right: -4px; | |||
} | |||
.document-flow-wrapper .document-flow .document-flow-link { | |||
margin-top: -10px; | |||
display: inline-block; | |||
} | |||
.document-flow-wrapper .document-flow .document-flow-link:not(.disabled):hover .document-flow-link-label, | |||
.document-flow-wrapper .document-flow .document-flow-link:not(.disabled):focus .document-flow-link-label, | |||
.document-flow-wrapper .document-flow .document-flow-link:not(.disabled):active .document-flow-link-label { | |||
text-decoration: underline; | |||
} | |||
.document-flow-wrapper .document-flow .document-flow-link-label { | |||
display: inline-block; | |||
margin-left: -50%; | |||
margin-top: 5px; | |||
} | |||
@media (max-width: 767px) { | |||
.document-flow-wrapper { | |||
display: none; | |||
} | |||
} | |||
.form-dashboard { | |||
border-bottom: 1px solid #EBEFF2; | |||
} | |||
@@ -477,19 +513,3 @@ select.form-control { | |||
box-shadow: none; | |||
} | |||
} | |||
.module-flow { | |||
float:left; | |||
width: 100%; | |||
padding-top:15px; | |||
padding-bottom:15px; | |||
padding-left:10px; | |||
margin-bottom:20px; | |||
background-color: #fafbfc; | |||
border-bottom: 1px solid #EBEFF2; | |||
} | |||
.module-flow a { | |||
margin-left: 5px; | |||
margin-right: 5px; | |||
font-size:12px; | |||
} |
@@ -48,6 +48,10 @@ | |||
.indicator-right.darkgrey::after { | |||
background: #b8c2cc; | |||
} | |||
.indicator.black::before, | |||
.indicator-right.black::after { | |||
background: #36414C; | |||
} | |||
.indicator.yellow::before, | |||
.indicator-right.yellow::after { | |||
background: #FEEF72; | |||
@@ -0,0 +1,52 @@ | |||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors | |||
// MIT License. See license.txt | |||
frappe.ui.form.DocumentFlow = Class.extend({ | |||
init: function(opts) { | |||
$.extend(this, opts); | |||
this.wrapper = $('<div class="document-flow-wrapper hidden"></div>').prependTo(this.frm.layout.wrapper); | |||
}, | |||
refresh: function() { | |||
this.reset(); | |||
this.render(); | |||
}, | |||
reset: function() { | |||
this.wrapper.empty().addClass('hidden'); | |||
this.linked_with = {}; | |||
}, | |||
render: function() { | |||
var me = this; | |||
var module = frappe.get_meta(this.frm.doctype).module | |||
var doctypes = frappe.document_flow[module][this.frm.doctype]; | |||
if (!doctypes) { | |||
return; | |||
} | |||
$(frappe.render_template('form_document_flow', { | |||
frm: this.frm, | |||
doctypes: doctypes, | |||
})).appendTo(this.wrapper.removeClass('hidden')); | |||
this.wrapper.on('click', '.document-flow-link', function() { | |||
var doctype = $(this).attr("data-doctype"); | |||
if (me.frm.doctype != doctype) { | |||
me.get_linked_docs(doctype); | |||
return false; | |||
} | |||
}); | |||
}, | |||
get_linked_docs: function(for_doctype) { | |||
if(!this.linked_with[for_doctype]) { | |||
this.linked_with[for_doctype] = new frappe.ui.form.LinkedWith({ | |||
frm: this.frm, | |||
for_doctype: for_doctype | |||
}); | |||
} | |||
this.linked_with[for_doctype].show(); | |||
} | |||
}); |
@@ -116,11 +116,11 @@ frappe.ui.form.LinkedWith = Class.extend({ | |||
doctype: me.frm.doctype, | |||
name: me.frm.docname, | |||
linkinfo: me.frm.__linked_doctypes, | |||
for_doctype: me.link_for | |||
for_doctype: me.for_doctype | |||
}, | |||
callback: function(r) { | |||
var parent = me.dialog.fields_dict.list.$wrapper.empty(); | |||
if(keys(r.message || {}).length) { | |||
$.each(keys(r.message).sort(), function(i, doctype) { | |||
var listview = frappe.views.get_listview(doctype, me); | |||
@@ -0,0 +1,12 @@ | |||
<div class="document-flow"> | |||
{% for dt in doctypes %} | |||
<span class="document-flow-link-wrapper"> | |||
<a data-doctype="{{ dt }}" | |||
class="document-flow-link {% if (dt===frm.doctype) { %} strong disabled {% } %} " | |||
style="color: inherit;"> | |||
<span class="indicator {% if (dt===frm.doctype) { %} blue {% } else { %} darkgrey {% } %}"></span><br> | |||
<span class="document-flow-link-label">{{ __(dt) }}</span> | |||
</a> | |||
</span> | |||
{% endfor %} | |||
</div> |
@@ -89,7 +89,6 @@ frappe.ui.Page = Class.extend({ | |||
this.page_form = $('<div class="page-form row hide"></div>').prependTo(this.main); | |||
this.inner_toolbar = $('<div class="form-inner-toolbar hide"></div>').prependTo(this.main); | |||
this.module_flow = $('<div class="module-flow-section"></div>').appendTo(this.body); | |||
this.icon_group = this.page_actions.find(".page-icon-group"); | |||
}, | |||
@@ -410,48 +409,6 @@ frappe.ui.Page = Class.extend({ | |||
this.wrapper.trigger('view-change'); | |||
}, | |||
add_document_flow: function(frm){ | |||
var me = this; | |||
$('.module-flow-section').empty(); | |||
if (frm.doctype) { | |||
var module = frappe.get_meta(frm.doctype).module | |||
var module_flow = frappe.module_flow[module][frm.doctype]; | |||
if (module_flow){ | |||
if (!$('.doc-flow').length) { | |||
$('<div class="module-flow"></div>').prependTo(this.module_flow.removeClass("hide")); | |||
} | |||
$.each(module_flow, function(i, doc) { | |||
var doctype = doc; | |||
if (doc == frm.doctype) { | |||
doctype = "<b>"+__(doc)+"</b>" | |||
} | |||
$('<a data-name="'+__(doc)+'">'+__(doctype)+' </a>').on("click", function(){ | |||
me.get_linked_docs(frm, $(this).attr("data-name")) | |||
}).appendTo('.module-flow') | |||
if((module_flow.length-1) != i){ | |||
$('<span> > </span>').appendTo('.module-flow') | |||
} | |||
}) | |||
} | |||
} | |||
}, | |||
get_linked_docs: function(frm, link_for) { | |||
this.linked_with = null; | |||
if(!this.linked_with) { | |||
this.linked_with = new frappe.ui.form.LinkedWith({ | |||
frm: frm, | |||
link_for: link_for | |||
}); | |||
} | |||
this.linked_with.show(); | |||
} | |||
}); | |||
frappe.ui.scroll = function(element, animate, additional_offset) { | |||
@@ -462,4 +419,4 @@ frappe.ui.scroll = function(element, animate, additional_offset) { | |||
} else { | |||
$(window).scrollTop(top); | |||
} | |||
} | |||
} |
@@ -253,6 +253,10 @@ _f.Frm.prototype.setup_std_layout = function() { | |||
this.fields_dict = this.layout.fields_dict; | |||
this.fields = this.layout.fields_list; | |||
this.document_flow = new frappe.ui.form.DocumentFlow({ | |||
frm: this | |||
}); | |||
this.dashboard = new frappe.ui.form.Dashboard({ | |||
frm: this, | |||
}); | |||
@@ -341,6 +345,7 @@ _f.Frm.prototype.refresh_header = function(is_a_different_doc) { | |||
this.toolbar.refresh(); | |||
} | |||
this.document_flow.refresh(); | |||
this.dashboard.reset(); | |||
this.clear_custom_buttons(); | |||
@@ -40,6 +40,51 @@ | |||
padding: 15px; | |||
} | |||
.document-flow-wrapper { | |||
padding: 40px 15px 30px; | |||
font-size: @text-medium; | |||
border-bottom: 1px solid @light-border-color; | |||
.document-flow { | |||
display: inline-block; | |||
position: relative; | |||
left: 50%; | |||
transform: translateX(-50%); | |||
.document-flow-link-wrapper:not(:last-child) { | |||
border-top: 1px solid @indicator-darkgrey; | |||
padding-right: 60px; | |||
display: inline-block; | |||
margin-right: -4px; | |||
} | |||
.document-flow-link { | |||
margin-top: -10px; | |||
display: inline-block; | |||
} | |||
.document-flow-link:not(.disabled):hover, | |||
.document-flow-link:not(.disabled):focus, | |||
.document-flow-link:not(.disabled):active { | |||
.document-flow-link-label { | |||
text-decoration: underline; | |||
} | |||
} | |||
.document-flow-link-label { | |||
display: inline-block; | |||
margin-left: -50%; | |||
margin-top: 5px; | |||
} | |||
} | |||
} | |||
@media(max-width: @screen-xs) { | |||
.document-flow-wrapper { | |||
display: none; | |||
} | |||
} | |||
.form-dashboard { | |||
border-bottom: 1px solid @light-border-color; | |||
} | |||
@@ -55,6 +55,11 @@ | |||
background: @indicator-darkgrey; | |||
} | |||
.indicator.black::before, | |||
.indicator-right.black::after { | |||
background: @text-color; | |||
} | |||
.indicator.yellow::before, | |||
.indicator-right.yellow::after { | |||
background: @indicator-yellow; | |||