diff --git a/frappe/desk/form/linked_with.py b/frappe/desk/form/linked_with.py index 8ca042dc58..52d70c905c 100644 --- a/frappe/desk/form/linked_with.py +++ b/frappe/desk/form/linked_with.py @@ -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: diff --git a/frappe/model/document.py b/frappe/model/document.py index 83d7d8c8c1..b303e1e8b7 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -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(): diff --git a/frappe/public/build.json b/frappe/public/build.json index e0ae0ad621..e01f97b8d4 100644 --- a/frappe/public/build.json +++ b/frappe/public/build.json @@ -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", diff --git a/frappe/public/css/form.css b/frappe/public/css/form.css index d0752b1c16..460805984e 100644 --- a/frappe/public/css/form.css +++ b/frappe/public/css/form.css @@ -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; -} \ No newline at end of file diff --git a/frappe/public/css/indicator.css b/frappe/public/css/indicator.css index 711df4d87a..2b7f651164 100644 --- a/frappe/public/css/indicator.css +++ b/frappe/public/css/indicator.css @@ -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; diff --git a/frappe/public/js/frappe/form/document_flow.js b/frappe/public/js/frappe/form/document_flow.js new file mode 100644 index 0000000000..610adb863b --- /dev/null +++ b/frappe/public/js/frappe/form/document_flow.js @@ -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 = $('').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(); + } +}); diff --git a/frappe/public/js/frappe/form/linked_with.js b/frappe/public/js/frappe/form/linked_with.js index 849f3d45ab..f127659aee 100644 --- a/frappe/public/js/frappe/form/linked_with.js +++ b/frappe/public/js/frappe/form/linked_with.js @@ -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); diff --git a/frappe/public/js/frappe/form/templates/form_document_flow.html b/frappe/public/js/frappe/form/templates/form_document_flow.html new file mode 100644 index 0000000000..42e2b91efd --- /dev/null +++ b/frappe/public/js/frappe/form/templates/form_document_flow.html @@ -0,0 +1,12 @@ +
+ {% for dt in doctypes %} + + +
+ {{ __(dt) }} +
+
+ {% endfor %} +
diff --git a/frappe/public/js/frappe/ui/page.js b/frappe/public/js/frappe/ui/page.js index e39ceec431..9de99911f6 100644 --- a/frappe/public/js/frappe/ui/page.js +++ b/frappe/public/js/frappe/ui/page.js @@ -89,7 +89,6 @@ frappe.ui.Page = Class.extend({ this.page_form = $('
').prependTo(this.main); this.inner_toolbar = $('
').prependTo(this.main); - this.module_flow = $('
').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) { - $('
').prependTo(this.module_flow.removeClass("hide")); - } - - $.each(module_flow, function(i, doc) { - var doctype = doc; - - if (doc == frm.doctype) { - doctype = ""+__(doc)+"" - } - - $(''+__(doctype)+' ').on("click", function(){ - me.get_linked_docs(frm, $(this).attr("data-name")) - }).appendTo('.module-flow') - - if((module_flow.length-1) != i){ - $(' > ').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); } -} \ No newline at end of file +} diff --git a/frappe/public/js/legacy/form.js b/frappe/public/js/legacy/form.js index 153696b130..2ac064861a 100644 --- a/frappe/public/js/legacy/form.js +++ b/frappe/public/js/legacy/form.js @@ -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(); diff --git a/frappe/public/less/form.less b/frappe/public/less/form.less index 92cba57a47..00d59c396c 100644 --- a/frappe/public/less/form.less +++ b/frappe/public/less/form.less @@ -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; } diff --git a/frappe/public/less/indicator.less b/frappe/public/less/indicator.less index 900acf7408..f7126cec33 100644 --- a/frappe/public/less/indicator.less +++ b/frappe/public/less/indicator.less @@ -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;