204 lines
No EOL
5.9 KiB
Python
204 lines
No EOL
5.9 KiB
Python
# 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:
|
|
links = frappe.get_doc(doctype, name).get_link_filters(for_doctype)
|
|
|
|
if links:
|
|
linkinfo = links
|
|
|
|
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("filters"):
|
|
ret = frappe.get_list(doctype=dt, fields=fields, filters=link.get("filters"))
|
|
|
|
elif 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:
|
|
if link.get("fieldname"):
|
|
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)
|
|
|
|
else:
|
|
ret = None
|
|
|
|
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 |