diff --git a/frappe/desk/form/linked_with.py b/frappe/desk/form/linked_with.py index b90a4d8aa6..52d70c905c 100644 --- a/frappe/desk/form/linked_with.py +++ b/frappe/desk/form/linked_with.py @@ -9,24 +9,36 @@ import frappe.desk.form.meta import frappe.desk.form.load @frappe.whitelist() -def get_linked_docs(doctype, name, linkinfo=None): +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) + results = frappe.cache().get_value(key, user=True) + if results: return results meta = frappe.desk.form.meta.get_meta(doctype) results = {} - if isinstance(linkinfo, basestring): - # additional fields are added in linkinfo - linkinfo = json.loads(linkinfo) - 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) @@ -61,11 +73,9 @@ def get_linked_docs(doctype, name, linkinfo=None): else: filters = [[dt, link.get("fieldname"), '=', name]] - # dynamic link if link.get("doctype_fieldname"): filters.append([dt, link.get("doctype_fieldname"), "=", doctype]) - ret = frappe.get_list(doctype=dt, fields=fields, filters=filters) except frappe.PermissionError: 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 eacd8f529a..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; } 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 344b79df59..f127659aee 100644 --- a/frappe/public/js/frappe/form/linked_with.js +++ b/frappe/public/js/frappe/form/linked_with.js @@ -110,13 +110,13 @@ frappe.ui.form.LinkedWith = Class.extend({ get_linked_docs: function() { var me = this; - return frappe.call({ method:"frappe.desk.form.linked_with.get_linked_docs", args: { doctype: me.frm.doctype, name: me.frm.docname, - linkinfo: me.frm.__linked_doctypes + linkinfo: me.frm.__linked_doctypes, + for_doctype: me.for_doctype }, callback: function(r) { var parent = me.dialog.fields_dict.list.$wrapper.empty(); 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 ed204957d7..9de99911f6 100644 --- a/frappe/public/js/frappe/ui/page.js +++ b/frappe/public/js/frappe/ui/page.js @@ -90,7 +90,6 @@ frappe.ui.Page = Class.extend({ this.page_form = $('
').prependTo(this.main); this.inner_toolbar = $('
').prependTo(this.main); this.icon_group = this.page_actions.find(".page-icon-group"); - }, set_indicator: function(label, color) { @@ -409,5 +408,15 @@ frappe.ui.Page = Class.extend({ this.views[name].toggle(true); this.wrapper.trigger('view-change'); + }, +}); + +frappe.ui.scroll = function(element, animate, additional_offset) { + var header_offset = $(".navbar").height() + $(".page-head").height(); + var top = $(element).offset().top - header_offset - cint(additional_offset); + if (animate) { + $("html, body").animate({ scrollTop: top }); + } 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;