|
- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
- # MIT License. See license.txt
- from __future__ import unicode_literals
-
- import frappe, json
- from frappe.model.meta import is_single
- from frappe.modules import load_doctype_module
- import frappe.desk.form.meta
- 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)
-
- results = frappe.cache().get_value(key, user=True)
-
- if results:
- return results
-
- meta = frappe.desk.form.meta.get_meta(doctype)
- results = {}
-
- 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)
- linkmeta = link_meta_bundle[0]
- if not linkmeta.get("issingle"):
- 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:
- ret = frappe.get_list(doctype=dt, fields=fields,
- filters=[[dt, "name", '=', me.parent]])
- else:
- ret = None
-
- elif link.get("child_doctype"):
- filters = [[link.get('child_doctype'), link.get("fieldname"), '=', name]]
-
- # dynamic link
- if link.get("doctype_fieldname"):
- filters.append([link.get('child_doctype'), link.get("doctype_fieldname"), "=", doctype])
-
- ret = frappe.get_list(doctype=dt, fields=fields, filters=filters)
-
- 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:
- if frappe.local.message_log:
- frappe.local.message_log.pop()
-
- continue
-
- if ret:
- results[dt] = ret
-
- frappe.cache().set_value(key, results, user=True)
-
- return results
-
- @frappe.whitelist()
- def get_linked_doctypes(doctype):
- """add list of doctypes this doctype is 'linked' with.
-
- Example, for Customer:
-
- {"Address": {"fieldname": "customer"}..}
- """
- return frappe.cache().hget("linked_doctypes", doctype, lambda: _get_linked_doctypes(doctype))
-
- def _get_linked_doctypes(doctype):
- ret = {}
-
- # find fields where this doctype is linked
- ret.update(get_linked_fields(doctype))
-
- ret.update(get_dynamic_linked_fields(doctype))
-
- # find links of parents
- links = frappe.db.sql("""select dt from `tabCustom Field`
- where (fieldtype="Table" and options=%s)""", (doctype))
- links += frappe.db.sql("""select parent from tabDocField
- where (fieldtype="Table" and options=%s)""", (doctype))
-
- for dt, in links:
- if not dt in ret:
- ret[dt] = {"get_parent": True}
-
- for dt in ret.keys():
- try:
- doctype_module = load_doctype_module(dt)
- except ImportError:
- # in case of Custom DocType
- continue
-
- if getattr(doctype_module, "exclude_from_linked_with", False):
- del ret[dt]
-
- return ret
-
- def get_linked_fields(doctype):
- links = frappe.db.sql("""select parent, fieldname from tabDocField
- where (fieldtype="Link" and options=%s)
- or (fieldtype="Select" and options=%s)""", (doctype, "link:"+ doctype))
- links += frappe.db.sql("""select dt as parent, fieldname from `tabCustom Field`
- where (fieldtype="Link" and options=%s)
- or (fieldtype="Select" and options=%s)""", (doctype, "link:"+ doctype))
-
- links = dict(links)
-
- ret = {}
-
- if links:
- for dt in links:
- ret[dt] = { "fieldname": links[dt] }
-
- # find out if linked in a child table
- for parent, options in frappe.db.sql("""select parent, options from tabDocField
- where fieldtype="Table"
- and options in (select name from tabDocType
- where istable=1 and name in (%s))""" % ", ".join(["%s"] * len(links)) ,tuple(links)):
-
- ret[parent] = {"child_doctype": options, "fieldname": links[options] }
- if options in ret:
- del ret[options]
-
- return ret
-
- def get_dynamic_linked_fields(doctype):
- ret = {}
-
- links = frappe.db.sql("""select parent as doctype, fieldname, options as doctype_fieldname
- from `tabDocField` where fieldtype='Dynamic Link'""", as_dict=True)
- links += frappe.db.sql("""select dt as doctype, fieldname, options as doctype_fieldname
- from `tabCustom Field` where fieldtype='Dynamic Link'""", as_dict=True)
-
- for df in links:
- if is_single(df.doctype):
- continue
-
- # optimized to get both link exists and parenttype
- possible_link = frappe.db.sql("""select distinct `{doctype_fieldname}`, parenttype
- from `tab{doctype}` where `{doctype_fieldname}`=%s""".format(**df), doctype, as_dict=True)
- if possible_link:
- for d in possible_link:
- # is child
- if d.parenttype:
- ret[d.parenttype] = {
- "child_doctype": df.doctype,
- "fieldname": df.fieldname,
- "doctype_fieldname": df.doctype_fieldname
- }
-
- else:
- ret[df.doctype] = {
- "fieldname": df.fieldname,
- "doctype_fieldname": df.doctype_fieldname
- }
-
- return ret
|