diff --git a/cypress/integration/control_link.js b/cypress/integration/control_link.js
index 8f9257e9c4..e691150925 100644
--- a/cypress/integration/control_link.js
+++ b/cypress/integration/control_link.js
@@ -65,16 +65,49 @@ context('Control Link', () => {
cy.intercept('POST', '/api/method/frappe.desk.search.search_link').as('search_link');
cy.get('@todos').then(todos => {
- cy.get('.frappe-control[data-fieldname=link] input').as('input');
- cy.get('@input').focus();
+ cy.get('.frappe-control[data-fieldname=link] input').focus().as('input');
cy.wait('@search_link');
- cy.get('@input').type(todos[0]).blur();
+ cy.get('@input').type(todos[0]);
+ cy.wait('@search_link');
+ cy.get('.frappe-control[data-fieldname=link] ul').should('be.visible');
+ cy.get('.frappe-control[data-fieldname=link] input').type('{enter}', { delay: 100 });
cy.wait('@validate_link');
cy.get('@input').focus();
cy.get('.frappe-control[data-fieldname=link] .link-btn')
.should('be.visible')
.click();
- cy.location('pathname').should('eq', `/app/todo/${todos[0]}`);
+ cy.location('hash').should('eq', `#Form/ToDo/${todos[0]}`);
+ });
+ });
+
+ it('show title field in link', () => {
+ get_dialog_with_link().as('dialog');
+
+ cy.server();
+ cy.insert_doc("Property Setter", {
+ property: "show_title_field_in_link",
+ doc_type: "ToDo",
+ value: 1,
+ doctype_or_field: "DocType"
+ }, true);
+ cy.route('POST', '/api/method/frappe.desk.search.search_link').as('search_link');
+
+ cy.get('.frappe-control[data-fieldname=link] input').focus().as('input');
+ cy.wait('@search_link');
+ cy.get('@input').type('todo for link');
+ cy.wait('@search_link');
+ cy.get('.frappe-control[data-fieldname=link] ul').should('be.visible');
+ cy.get('.frappe-control[data-fieldname=link] input').type('{enter}', { delay: 100 });
+ cy.get('.frappe-control[data-fieldname=link] input').blur();
+ cy.get('@dialog').then(dialog => {
+ cy.get('@todos').then(todos => {
+ let field = dialog.get_field('link');
+ let value = field.get_value();
+ let label = field.get_label_value();
+
+ expect(value).to.eq(todos[0]);
+ expect(label).to.eq('this is a test todo for link');
+ });
});
});
});
diff --git a/frappe/boot.py b/frappe/boot.py
index 0589e32ac8..8f02b67934 100644
--- a/frappe/boot.py
+++ b/frappe/boot.py
@@ -87,6 +87,7 @@ def get_bootinfo():
bootinfo.additional_filters_config = get_additional_filters_from_hooks()
bootinfo.desk_settings = get_desk_settings()
bootinfo.app_logo_url = get_app_logo()
+ bootinfo.doctypes_with_show_link_field_title = doctypes_with_show_link_field_title()
return bootinfo
@@ -324,3 +325,9 @@ def get_desk_settings():
def get_notification_settings():
return frappe.get_cached_doc('Notification Settings', frappe.session.user)
+
+def doctypes_with_show_link_field_title():
+ dts = frappe.get_all("DocType", {"show_title_field_in_link": 1})
+ custom_dts = frappe.get_all("Property Setter", {"field_name": "show_title_field_in_link", "value": 1})
+
+ return [d.name for d in dts + custom_dts if d]
\ No newline at end of file
diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json
index 6a427f71e1..3024fb32a2 100644
--- a/frappe/core/doctype/doctype/doctype.json
+++ b/frappe/core/doctype/doctype/doctype.json
@@ -45,6 +45,7 @@
"allow_auto_repeat",
"view_settings",
"title_field",
+ "show_title_field_in_link",
"search_fields",
"default_print_format",
"sort_field",
@@ -554,6 +555,12 @@
"fieldname": "website_search_field",
"fieldtype": "Data",
"label": "Website Search Field"
+ },
+ {
+ "default": "0",
+ "fieldname": "show_title_field_in_link",
+ "fieldtype": "Check",
+ "label": "Show Title in Link and Table MultiSelect Field"
}
],
"icon": "fa fa-bolt",
@@ -635,7 +642,7 @@
"link_fieldname": "reference_doctype"
}
],
- "modified": "2021-06-17 23:31:44.974199",
+ "modified": "2021-08-03 13:41:50.319555",
"modified_by": "Administrator",
"module": "Core",
"name": "DocType",
diff --git a/frappe/custom/doctype/customize_form/customize_form.json b/frappe/custom/doctype/customize_form/customize_form.json
index c2940a92e3..cdcac1582a 100644
--- a/frappe/custom/doctype/customize_form/customize_form.json
+++ b/frappe/custom/doctype/customize_form/customize_form.json
@@ -27,6 +27,7 @@
"autoname",
"view_settings_section",
"title_field",
+ "show_title_field_in_link",
"image_field",
"default_print_format",
"column_break_29",
@@ -280,6 +281,12 @@
"fieldname": "autoname",
"fieldtype": "Data",
"label": "Auto Name"
+ },
+ {
+ "default": "0",
+ "fieldname": "show_title_field_in_link",
+ "fieldtype": "Check",
+ "label": "Show Title in Link and Table MultiSelect Field"
}
],
"hide_toolbar": 1,
@@ -288,7 +295,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2021-06-21 19:01:06.920663",
+ "modified": "2021-08-03 13:43:27.938781",
"modified_by": "Administrator",
"module": "Custom",
"name": "Customize Form",
diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py
index 8de194fb00..1866f4d368 100644
--- a/frappe/custom/doctype/customize_form/customize_form.py
+++ b/frappe/custom/doctype/customize_form/customize_form.py
@@ -495,7 +495,8 @@ doctype_properties = {
'email_append_to': 'Check',
'subject_field': 'Data',
'sender_field': 'Data',
- 'autoname': 'Data'
+ 'autoname': 'Data',
+ 'show_title_field_in_link': 'Check'
}
docfield_properties = {
diff --git a/frappe/database/mariadb/framework_mariadb.sql b/frappe/database/mariadb/framework_mariadb.sql
index f8841e9417..a74ece8478 100644
--- a/frappe/database/mariadb/framework_mariadb.sql
+++ b/frappe/database/mariadb/framework_mariadb.sql
@@ -224,6 +224,7 @@ CREATE TABLE `tabDocType` (
`email_append_to` int(1) NOT NULL DEFAULT 0,
`subject_field` varchar(255) DEFAULT NULL,
`sender_field` varchar(255) DEFAULT NULL,
+ `show_title_field_in_link` int(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`name`),
KEY `parent` (`parent`)
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
diff --git a/frappe/database/postgres/framework_postgres.sql b/frappe/database/postgres/framework_postgres.sql
index a4e94aa326..f65a832bc6 100644
--- a/frappe/database/postgres/framework_postgres.sql
+++ b/frappe/database/postgres/framework_postgres.sql
@@ -229,6 +229,7 @@ CREATE TABLE "tabDocType" (
"email_append_to" smallint NOT NULL DEFAULT 0,
"subject_field" varchar(255) DEFAULT NULL,
"sender_field" varchar(255) DEFAULT NULL,
+ "show_title_field_in_link" smallint NOT NULL DEFAULT 0,
PRIMARY KEY ("name")
) ;
diff --git a/frappe/desk/form/load.py b/frappe/desk/form/load.py
index a62bfd01d0..994a50f938 100644
--- a/frappe/desk/form/load.py
+++ b/frappe/desk/form/load.py
@@ -48,7 +48,7 @@ def getdoc(doctype, name, user=None):
raise
doc.add_seen()
-
+ set_link_titles(doc)
frappe.response.docs.append(doc)
@frappe.whitelist(allow_guest=True)
@@ -310,3 +310,55 @@ def get_additional_timeline_content(doctype, docname):
contents.extend(frappe.get_attr(method)(doctype, docname) or [])
return contents
+
+def set_link_titles(doc):
+ meta = frappe.get_meta(doc.doctype)
+ link_titles = {}
+ link_titles.update(get_title_values_for_link_and_dynamic_link_fields(meta, doc))
+ link_titles.update(get_title_values_for_table_and_multiselect_fields(meta, doc))
+
+ send_link_titles(link_titles)
+
+def get_title_values_for_link_and_dynamic_link_fields(meta, doc, link_fields=None):
+ link_titles = {}
+
+ if not link_fields:
+ link_fields = meta.get_link_fields() + meta.get_dynamic_link_fields()
+
+ for field in link_fields:
+ if not doc.get(field.fieldname):
+ continue
+
+ doctype = field.options if field.fieldtype == "Link" else doc.get(field.options)
+
+ meta = frappe.get_meta(doctype)
+ if not meta or not (meta.title_field and meta.show_title_field_in_link):
+ continue
+
+ link_title = frappe.get_cached_value(doctype, doc.get(field.fieldname), meta.title_field)
+ link_titles.update({doctype + "::" + doc.get(field.fieldname): link_title})
+
+ return link_titles
+
+def get_title_values_for_table_and_multiselect_fields(meta, doc, table_fields=None):
+ link_titles = {}
+
+ if not table_fields:
+ table_fields = meta.get_table_fields()
+
+ for field in table_fields:
+ if not doc.get(field.fieldname):
+ continue
+
+ _meta = frappe.get_meta(field.options)
+ for value in doc.get(field.fieldname):
+ link_titles.update(get_title_values_for_link_and_dynamic_link_fields(_meta, value))
+
+ return link_titles
+
+def send_link_titles(link_titles):
+ """Append link titles dict in `frappe.local.response`."""
+ if "_link_titles" not in frappe.local.response:
+ frappe.local.response["_link_titles"] = {}
+
+ frappe.local.response["_link_titles"].update(link_titles)
diff --git a/frappe/desk/search.py b/frappe/desk/search.py
index f9b65fc98e..a64d6efec4 100644
--- a/frappe/desk/search.py
+++ b/frappe/desk/search.py
@@ -49,8 +49,11 @@ def sanitize_searchfield(searchfield):
# this is called by the Link Field
@frappe.whitelist()
def search_link(doctype, txt, query=None, filters=None, page_length=20, searchfield=None, reference_doctype=None, ignore_user_permissions=False):
- search_widget(doctype, txt.strip(), query, searchfield=searchfield, page_length=page_length, filters=filters, reference_doctype=reference_doctype, ignore_user_permissions=ignore_user_permissions)
- frappe.response['results'] = build_for_autosuggest(frappe.response["values"])
+ search_widget(doctype, txt.strip(), query, searchfield=searchfield, page_length=page_length, filters=filters,
+ reference_doctype=reference_doctype, ignore_user_permissions=ignore_user_permissions)
+
+ frappe.response["results"] = build_for_autosuggest(frappe.response["values"], doctype=doctype,
+ is_query=True if query else False)
del frappe.response["values"]
# this is called by the search box
@@ -138,6 +141,11 @@ def search_widget(doctype, txt, query=None, searchfield=None, start=0,
fields = list(set(fields + json.loads(filter_fields)))
formatted_fields = ['`tab%s`.`%s`' % (meta.name, f.strip()) for f in fields]
+ title_field_query = get_title_field_query(meta)
+
+ # Insert title field query after name
+ formatted_fields.insert(1, title_field_query)
+
# find relevance as location of search term from the beginning of string `name`. used for sorting results.
formatted_fields.append("""locate({_txt}, `tab{doctype}`.`name`) as `_relevance`""".format(
_txt=frappe.db.escape((txt or "").replace("%", "").replace("@", "")), doctype=doctype))
@@ -205,10 +213,32 @@ def get_std_fields_list(meta, key):
return sflist
-def build_for_autosuggest(res):
+def get_title_field_query(meta):
+ title_field = meta.title_field if meta.title_field else None
+ show_title_field_in_link = meta.show_title_field_in_link if meta.show_title_field_in_link else None
+ field = "NULL as `label`"
+
+ if title_field and show_title_field_in_link:
+ field = "`tab{0}`.{1} as `label`".format(meta.name, title_field)
+
+ return field
+
+def build_for_autosuggest(res, doctype, is_query):
results = []
for r in res:
- out = {"value": r[0], "description": ", ".join(unique(cstr(d) for d in r if d)[1:])}
+ r = list(r)
+ if is_query or doctype in (frappe.get_hooks().standard_queries or {}):
+ out = {
+ "value": r[0],
+ "description": ", ".join(unique(cstr(d) for d in r[1:] if d))
+ }
+ else:
+ out = {
+ "value": r[0],
+ "label": r[1],
+ "description": ", ".join(unique(cstr(d) for d in r[2:] if d))
+ }
+
results.append(out)
return results
@@ -271,3 +301,12 @@ def get_user_groups():
return frappe.get_all('User Group', fields=['name as id', 'name as value'], update={
'is_group': True
})
+
+@frappe.whitelist()
+def get_link_title(doctype, docname):
+ meta = frappe.get_meta(doctype)
+
+ if meta.title_field and meta.show_title_field_in_link:
+ return frappe.get_cached_value(doctype, docname, meta.title_field)
+
+ return docname
\ No newline at end of file
diff --git a/frappe/public/js/controls.bundle.js b/frappe/public/js/controls.bundle.js
index 30b5d43905..b08cbb336d 100644
--- a/frappe/public/js/controls.bundle.js
+++ b/frappe/public/js/controls.bundle.js
@@ -16,3 +16,4 @@ import "air-datepicker/dist/js/i18n/datepicker.sk.js";
import "air-datepicker/dist/js/i18n/datepicker.zh.js";
import "./frappe/ui/capture.js";
import "./frappe/form/controls/control.js";
+import "./frappe/link_title.js";
diff --git a/frappe/public/js/frappe/form/controls/link.js b/frappe/public/js/frappe/form/controls/link.js
index 83f3f8dd70..82481d5303 100644
--- a/frappe/public/js/frappe/form/controls/link.js
+++ b/frappe/public/js/frappe/form/controls/link.js
@@ -29,12 +29,14 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
setTimeout(function() {
if(me.$input.val() && me.get_options()) {
let doctype = me.get_options();
- let name = me.$input.val();
+ let name = me.get_input_value();
me.$link.toggle(true);
me.$link_open.attr('href', frappe.utils.get_form_link(doctype, name));
}
if(!me.$input.val()) {
+ me.reset_value();
+ me.reset_fetch_values(me.df, me.docname);
me.$input.val("").trigger("input");
}
}, 500);
@@ -69,6 +71,76 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
this.$input_area.find(".link-btn").remove();
}
}
+ set_formatted_input(value) {
+ super.set_formatted_input();
+ if (!value) return;
+ let doctype = this.get_options();
+ this.set_data_value(frappe.get_link_title(doctype, value) || value, value);
+ }
+ set_data_value(link_display, value) {
+ if (!this.$input) {
+ return;
+ }
+
+ this.$input.val(__(link_display));
+ this.data_value = value;
+ }
+ parse_validate_and_set_in_model(value, label, e) {
+ if (this.parse) value = this.parse(value, label);
+ if (label) {
+ this.label = label;
+ frappe.add_link_title(this.doctype, value, label);
+ }
+
+ return this.validate_and_set_in_model(value, e);
+ }
+ validate_and_set_in_model(value, e) {
+ var me = this;
+ if (this.inside_change_event) {
+ return Promise.resolve();
+ }
+ this.inside_change_event = true;
+ var set = function(value) {
+ me.inside_change_event = false;
+ return frappe.run_serially([
+ () => me.set_model_value(value),
+ () => {
+ me.set_mandatory && me.set_mandatory(value);
+
+ if (me.df.change || me.df.onchange) {
+ // onchange event specified in df
+ frappe.set_link_title(me);
+ return (me.df.change || me.df.onchange).apply(me, [e]);
+ }
+ }
+ ]);
+ };
+
+ value = this.validate(value);
+ if (value && value.then) {
+ // got a promise
+ return value.then((value) => set(value));
+ } else {
+ // all clear
+ return set(value);
+ }
+ }
+ get_input_value() {
+ return (this.$input && this.data_value && this.$input.val()) ? this.data_value : "";
+ }
+ get_label_value() {
+ return this.$input ? this.$input.val() : "";
+ }
+ set_input_label(label) {
+ this.$input && this.$input.val(__(label));
+ }
+ reset_value() {
+ if (!this.$input) {
+ return;
+ }
+ this.$input.val("");
+ this.data_value = null;
+ }
open_advanced_search() {
var doctype = this.get_options();
if(!doctype) return;
@@ -98,7 +170,7 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
}
// partially entered name field
- frappe.route_options.name_field = this.get_value();
+ frappe.route_options.name_field = this.get_label_value();
// reference to calling link
frappe._from_link = this;
@@ -120,6 +192,11 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
maxItems: 99,
autoFirst: true,
list: [],
+ replace: function (suggestion) {
+ // Override Awesomeplete replace function as it is used to set the input value
+ // https://github.com/LeaVerou/awesomplete/issues/17104#issuecomment-359185403
+ this.input.value = suggestion.label || suggestion.value;
+ },
data: function (item) {
return {
label: item.label || item.value,
@@ -236,9 +313,11 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
me.selected = false;
return;
}
- var value = me.get_input_value();
- if(value!==me.last_value) {
- me.parse_validate_and_set_in_model(value);
+ let value = me.get_input_value();
+ let label = me.get_label_value();
+
+ if (value !== me.last_value || me.label !== label) {
+ me.parse_validate_and_set_in_model(value, label);
}
});
@@ -258,14 +337,15 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
// prevent selection on tab
var TABKEY = 9;
- if(e.keyCode === TABKEY) {
+ if (e.keyCode === TABKEY) {
e.preventDefault();
me.awesomplete.close();
return false;
}
- if(item.action) {
+ if (item.action) {
item.value = "";
+ item.label = "";
item.action.apply(me);
}
@@ -277,13 +357,14 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
frappe.boot.user.last_selected_values[me.df.options] = item.value;
}
- me.parse_validate_and_set_in_model(item.value);
+ me.parse_validate_and_set_in_model(item.value, item.label);
});
this.$input.on("awesomplete-selectcomplete", function(e) {
- var o = e.originalEvent;
- if(o.text.value.indexOf("__link_option") !== -1) {
- me.$input.val("");
+ let o = e.originalEvent;
+ if (o.text.value.indexOf("__link_option") !== -1) {
+ me.reset_value();
+ me.reset_fetch_values(me.df, me.docname);
}
});
}
@@ -452,10 +533,13 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
this.docname, value);
}
validate_link_and_fetch(df, doctype, docname, value) {
- if(value) {
+ let me = this;
+
+ if (value) {
return new Promise((resolve) => {
- var fetch = '';
- if(this.frm && this.frm.fetch_dict[df.fieldname]) {
+ let fetch = '';
+
+ if (this.frm && this.frm.fetch_dict[df.fieldname]) {
fetch = this.frm.fetch_dict[df.fieldname].columns.join(', ');
}
// if default and no fetch, no need to validate
@@ -465,10 +549,14 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
this.fetch_and_validate_link(resolve, df, doctype, docname, value, fetch);
});
+ } else {
+ me.reset_value();
+ me.reset_fetch_values(df, docname);
}
}
-
fetch_and_validate_link(resolve, df, doctype, docname, value, fetch) {
+ let me = this;
+
frappe.call({
method: 'frappe.desk.form.utils.validate_link',
type: "GET",
@@ -485,18 +573,27 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
}
resolve(r.valid_value);
} else {
+ me.reset_value();
+ me.reset_fetch_values(df, docname);
resolve("");
}
}
});
}
-
set_fetch_values(df, docname, fetch_values) {
- var fl = this.frm.fetch_dict[df.fieldname].fields;
- for(var i=0; i < fl.length; i++) {
+ let fl = this.frm.fetch_dict[df.fieldname].fields;
+
+ for (var i=0; i < fl.length; i++) {
frappe.model.set_value(df.parent, docname, fl[i], fetch_values[i], df.fieldtype);
}
}
+ reset_fetch_values(df, docname) {
+ let fields = this.frm && this.frm.fetch_dict && this.frm.fetch_dict[df.fieldname] ? this.frm.fetch_dict[df.fieldname].fields : [];
+
+ fields.forEach(field => {
+ frappe.model.set_value(df.parent, docname, field, null, df.fieldtype);
+ });
+ }
};
if (Awesomplete) {
diff --git a/frappe/public/js/frappe/form/controls/table_multiselect.js b/frappe/public/js/frappe/form/controls/table_multiselect.js
index 15dfd9649e..f74e0837ff 100644
--- a/frappe/public/js/frappe/form/controls/table_multiselect.js
+++ b/frappe/public/js/frappe/form/controls/table_multiselect.js
@@ -62,6 +62,7 @@ frappe.ui.form.ControlTableMultiSelect = class ControlTableMultiSelect extends f
[link_field.fieldname]: value
});
}
+ frappe.add_link_title(link_field.options, value, label);
}
this._rows_list = this.rows.map(row => row[link_field.fieldname]);
return this.rows;
@@ -126,10 +127,12 @@ frappe.ui.form.ControlTableMultiSelect = class ControlTableMultiSelect extends f
this.$input_area.prepend(html);
}
get_pill_html(value) {
- const encoded_value = encodeURIComponent(value);
+ const link_field = this.get_link_field();
+ const encoded_value = encodeURIComponent(value.name);
+ const pill_name = frappe.get_link_title(link_field.options, value[link_field.fieldname]) || value.name;
return `
`;
diff --git a/frappe/public/js/frappe/form/formatters.js b/frappe/public/js/frappe/form/formatters.js
index b9a838688d..c4f042533d 100644
--- a/frappe/public/js/frappe/form/formatters.js
+++ b/frappe/public/js/frappe/form/formatters.js
@@ -106,12 +106,14 @@ frappe.form.formatters = {
Link: function(value, docfield, options, doc) {
var doctype = docfield._options || docfield.options;
var original_value = value;
+ let link_title = frappe.get_link_title(doctype, value);
+
if(value && value.match && value.match(/^['"].*['"]$/)) {
value.replace(/^.(.*).$/, "$1");
}
if(options && (options.for_print || options.only_value)) {
- return value;
+ return link_title || value;
}
if(frappe.form.link_formatters[doctype]) {
@@ -135,13 +137,14 @@ frappe.form.formatters = {
return `
- ${__(options && options.label || value)}`;
+ data-name="${original_value}"
+ data-value="${original_value}">
+ ${__(options && options.label || link_title || value)}`;
} else {
- return value;
+ return link_title || value;
}
} else {
- return value;
+ return link_title || value;
}
},
Date: function(value) {
diff --git a/frappe/public/js/frappe/form/save.js b/frappe/public/js/frappe/form/save.js
index 65d84e2202..612e449a7e 100644
--- a/frappe/public/js/frappe/form/save.js
+++ b/frappe/public/js/frappe/form/save.js
@@ -249,31 +249,40 @@ frappe.ui.form.update_calling_link = (newdoc) => {
};
if (is_valid_doctype()) {
- // set value
- if (doc && doc.parentfield) {
- //update values for child table
- $.each(frappe._from_link.frm.fields_dict[doc.parentfield].grid.grid_rows, function (index, field) {
- if (field.doc && field.doc.name === frappe._from_link.docname) {
- frappe._from_link.set_value(newdoc.name);
- }
- });
- } else {
- frappe._from_link.set_value(newdoc.name);
- }
-
- // refresh field
- frappe._from_link.refresh();
-
- // if from form, switch
- if (frappe._from_link.frm) {
- frappe.set_route("Form",
- frappe._from_link.frm.doctype, frappe._from_link.frm.docname)
- .then(() => {
- frappe.utils.scroll_to(frappe._from_link_scrollY);
+ frappe.model.with_doctype(newdoc.doctype, () => {
+ let meta = frappe.get_meta(newdoc.doctype);
+ // set value
+ if (doc && doc.parentfield) {
+ //update values for child table
+ $.each(frappe._from_link.frm.fields_dict[doc.parentfield].grid.grid_rows, function (index, field) {
+ if (field.doc && field.doc.name === frappe._from_link.docname) {
+ if (meta.title_field && meta.show_title_field_in_link) {
+ frappe.add_link_title(newdoc.doctype, newdoc.name, newdoc[meta.title_field]);
+ }
+ frappe._from_link.set_value(newdoc.name);
+ }
});
- }
+ } else {
+ if (meta.title_field && meta.show_title_field_in_link) {
+ frappe.add_link_title(newdoc.doctype, newdoc.name, newdoc[meta.title_field]);
+ }
+ frappe._from_link.set_value(newdoc.name);
+ }
- frappe._from_link = null;
+ // refresh field
+ frappe._from_link.refresh();
+
+ // if from form, switch
+ if (frappe._from_link.frm) {
+ frappe.set_route("Form",
+ frappe._from_link.frm.doctype, frappe._from_link.frm.docname)
+ .then(() => {
+ frappe.utils.scroll_to(frappe._from_link_scrollY);
+ });
+ }
+
+ frappe._from_link = null;
+ });
}
}
diff --git a/frappe/public/js/frappe/link_title.js b/frappe/public/js/frappe/link_title.js
new file mode 100644
index 0000000000..82bc6c149c
--- /dev/null
+++ b/frappe/public/js/frappe/link_title.js
@@ -0,0 +1,34 @@
+// for link titles
+frappe._link_titles = {};
+
+frappe.get_link_title = function(doctype, name) {
+ if (!doctype || !name) {
+ return;
+ }
+
+ return frappe._link_titles[doctype + "::" + name];
+};
+
+frappe.add_link_title = function (doctype, name, value) {
+ if (!doctype || !name) {
+ return;
+ }
+
+ frappe._link_titles[doctype + "::" + name] = value;
+};
+
+frappe.set_link_title = function(f) {
+ let doctype = f.get_options();
+ let docname = f.get_input_value();
+
+ if ((!in_list(frappe.boot.doctypes_with_show_link_field_title, doctype)) || (!doctype || !docname) ||
+ (frappe.get_link_title(doctype, docname))) {
+ return;
+ }
+
+ frappe.xcall("frappe.desk.search.get_link_title", {"doctype": doctype, "docname": docname}).then((r) => {
+ if (r && docname !== r) {
+ f.set_input_label(r);
+ }
+ });
+};
\ No newline at end of file
diff --git a/frappe/public/js/frappe/request.js b/frappe/public/js/frappe/request.js
index 12fa9c8e21..9f09b288b6 100644
--- a/frappe/public/js/frappe/request.js
+++ b/frappe/public/js/frappe/request.js
@@ -241,6 +241,11 @@ frappe.request.call = function(opts) {
$.extend(frappe._messages, data.__messages);
}
+ // sync link titles
+ if (data._link_titles) {
+ $.extend(frappe._link_titles, data._link_titles);
+ }
+
// callbacks
var status_code_handler = statusCode[xhr.statusCode().status];
if (status_code_handler) {
diff --git a/frappe/public/js/frappe/ui/filters/filter.js b/frappe/public/js/frappe/ui/filters/filter.js
index b2b2b623e2..d8bc760668 100644
--- a/frappe/public/js/frappe/ui/filters/filter.js
+++ b/frappe/public/js/frappe/ui/filters/filter.js
@@ -313,6 +313,10 @@ frappe.ui.Filter = class {
return this.utils.get_selected_value(this.field, this.get_condition());
}
+ get_selected_label() {
+ return this.utils.get_selected_label(this.field);
+ }
+
get_condition() {
return this.filter_edit_area.find('.condition').val();
}
@@ -360,7 +364,7 @@ frappe.ui.Filter = class {
get_filter_button_text() {
let value = this.utils.get_formatted_value(
this.field,
- this.get_selected_value()
+ this.get_selected_label() || this.get_selected_value()
);
return `${__(this.field.df.label)} ${__(this.get_condition())} ${__(
value
@@ -448,6 +452,12 @@ frappe.ui.filter_utils = {
return val;
},
+ get_selected_label(field) {
+ if (in_list(["Link", "Dynamic Link"], field.df.fieldtype)) {
+ return field.get_label_value();
+ }
+ },
+
get_default_condition(df) {
if (df.fieldtype == 'Data') {
return 'like';
diff --git a/frappe/www/printview.py b/frappe/www/printview.py
index cdf47790eb..268a01eafb 100644
--- a/frappe/www/printview.py
+++ b/frappe/www/printview.py
@@ -170,6 +170,38 @@ def get_rendered_template(doc, name=None, print_format=None, meta=None,
return html
+def set_link_titles(doc):
+ # Replaces name with title of link field doctype
+
+ meta = frappe.get_meta(doc.doctype)
+ set_title_values_for_link_and_dynamic_link_fields(meta, doc)
+ set_title_values_for_table_and_multiselect_fields(meta, doc)
+
+def set_title_values_for_link_and_dynamic_link_fields(meta, doc):
+ for field in meta.get_link_fields() + meta.get_dynamic_link_fields():
+ if not doc.get(field.fieldname):
+ continue
+
+ # If link field, then get doctype from options
+ # If dynamic link field, then get doctype from dependent field
+ doctype = field.options if field.fieldtype == "Link" else doc.get(field.options)
+
+ meta = frappe.get_meta(doctype)
+ if not meta or not (meta.title_field and meta.show_title_field_in_link):
+ continue
+
+ link_title = frappe.get_cached_value(doctype, doc.get(field.fieldname), meta.title_field)
+ setattr(doc, field.fieldname, link_title)
+
+def set_title_values_for_table_and_multiselect_fields(meta, doc):
+ for field in meta.get_table_fields():
+ if not doc.get(field.fieldname):
+ continue
+
+ _meta = frappe.get_meta(field.options)
+ for value in doc.get(field.fieldname):
+ set_title_values_for_link_and_dynamic_link_fields(_meta, value)
+
def convert_markdown(doc, meta):
'''Convert text field values to markdown if necessary'''
for field in meta.fields:
@@ -191,6 +223,7 @@ def get_html_and_style(doc, name=None, print_format=None, meta=None,
doc = frappe.get_doc(json.loads(doc))
print_format = get_print_format_doc(print_format, meta=meta or frappe.get_meta(doc.doctype))
+ set_link_titles(doc)
try:
html = get_rendered_template(doc, name=name, print_format=print_format, meta=meta,