[New Feature Proposal] Enable search user tags in link field and global search

  • Currently there is very handy user tag feature, which on one hand enable the user to tag every doc, and on the other hand to allow user to quickly filter out the tagged docs either by selecting the user tag directly from the left panel or choosing user tag as the filtering field via standard filter.

  • It is a kind of powerful universal classification mechanism for all doctypes, so far so good. but if it is desired to further utilize the user tag, it is still missing 2 important features: search user tag for linked field and global search, here it is one typical example, user tagged some regularly used item, but normally it is desirable to to auto filter the item by typing part of the tag text when select item for relevant transactions such as purchase order and sales order, also searching the same items via global search by typing into the awesome bar is highly needed, especially searching the same tag which assigned to different transactions will be very useful, just like google the internal ERP!

  • Anyway, after almost half day hard work, I got the following technical solution, which turned out to be technically feasible, my question at the moment is that is there enough community member interested in this new feature proposal? anyone from technical side or the core team have comments concern the below solution?

  • If it is decided to proceed as a new pull request, anyone from foundation or core team can help to handle the pull request? at the moment I am a little worried whether I can easily fulfill all the quality standards of the pull request, after failed some previous PR due to different reasons.

  • Enable search user tag for link field, the following changes to be made

file: frappe/desk/search.py
method:def search_widget
context:

				for f in search_fields:
					fmeta = meta.get_field(f.strip())
					if f == "name" or (fmeta and fmeta.fieldtype in ["Data", "Text", "Small Text", "Long Text",
						"Link", "Select", "Read Only", "Text Editor"]):
							or_filters.append([doctype, f.strip(), "like", "%{0}%".format(txt)])
action:insert after:
				if frappe.db.has_column(doctype, "_user_tags"):
					or_filters.append([doctype, "_user_tags", "like", "%{0}%".format(txt)])

file: erpnext/controllers/queries
method:def customer_query
context:searchfields = " or ā€œ.join([field + " like %(txt)sā€ for field in searchfields])
action:insert before

if frappe.db.has_column(doctype, "_user_tags"):
		searchfields.append("_user_tags")

file: erpnext/controllers/queries
method:def item_query
action: replace

def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
	
	conditions = []
	
	searchfields = ["item_group", "item_name","barcode","description"] 		
	if frappe.db.has_column(doctype, "_user_tags"):
		searchfields.append("_user_tags")
	searchfields = " or ".join([field + " like %(txt)s" for field in searchfields])

	return frappe.db.sql("""select tabItem.name, tabItem.item_group, tabItem.image,
		if(length(tabItem.item_name) > 40,
			concat(substr(tabItem.item_name, 1, 40), "..."), item_name) as item_name,
		if(length(tabItem.description) > 40, \
			concat(substr(tabItem.description, 1, 40), "..."), description) as decription
		from tabItem
		where tabItem.docstatus < 2
			and tabItem.has_variants=0
			and tabItem.disabled=0
			and (tabItem.end_of_life > %(today)s or ifnull(tabItem.end_of_life, '0000-00-00')='0000-00-00')
			and (tabItem.`{key}` LIKE %(txt)s
			     or {scond} )
			{fcond} {mcond}
		order by
			if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
			if(locate(%(_txt)s, item_name), locate(%(_txt)s, item_name), 99999),
			idx desc,
			name, item_name
		limit %(start)s, %(page_len)s """.format(
			key=searchfield,
			scond=searchfields,
			fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
			mcond=get_match_cond(doctype).replace('%', '%%')),
			{
				"today": nowdate(),
				"txt": "%%%s%%" % txt,
				"_txt": txt.replace("%", ""),
				"start": start,
				"page_len": page_len
				 
			}, as_dict=as_dict)

for other customized queries which need search via user tag, do the similiar change like above for customer and item

  • Enable search user tag in global search

file: frappe/des/tags
method: class DocTags, def update(self, dn, tl)
context:

try:
			frappe.db.sql("update `tab%s` set _user_tags=%s where name=%s" % \
				(self.dt,'%s','%s'), (tags , dn))
action: insert after
			from frappe.utils.global_search import update_global_search
			doc= frappe.get_doc(self.dt, dn)
			update_global_search(doc)

file:frappe/utils/global_search.py
method: def update_global_search
context:
for field in doc.meta.get_global_search_fields():
		if doc.get(field.fieldname) and field.fieldtype != "Table":
			content.append(get_formatted_value(doc.get(field.fieldname), field))
action: insert after
	tag =(frappe.db.get_value(doc.doctype, doc.name, '_user_tags', ignore=1) or '').strip()
	if tag:
		content.append("User Tags : " + tag)

@szufisher yes, tags should be part of global search!

Please send the PR (its very hard to do code-reviews looking at your snippets!)