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 @@ +