# 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): results = frappe.cache().get_value("linked_with:{doctype}:{name}".format(doctype=doctype, name=name)) 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 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("linked_with:{doctype}:{name}".format(doctype=doctype, name=name), 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