您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

194 行
5.6 KiB

  1. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. from __future__ import unicode_literals
  4. import frappe, json
  5. from frappe.model.meta import is_single
  6. from frappe.modules import load_doctype_module
  7. import frappe.desk.form.meta
  8. import frappe.desk.form.load
  9. @frappe.whitelist()
  10. def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None):
  11. key = "linked_with:{doctype}:{name}".format(doctype=doctype, name=name)
  12. if isinstance(linkinfo, basestring):
  13. # additional fields are added in linkinfo
  14. linkinfo = json.loads(linkinfo)
  15. if for_doctype:
  16. key = "{key}:{for_doctype}".format(key=key, for_doctype=for_doctype)
  17. results = frappe.cache().get_value(key, user=True)
  18. if results:
  19. return results
  20. meta = frappe.desk.form.meta.get_meta(doctype)
  21. results = {}
  22. if not linkinfo:
  23. return results
  24. if for_doctype:
  25. if for_doctype in linkinfo:
  26. # only get linked with for this particular doctype
  27. linkinfo = { for_doctype: linkinfo.get(for_doctype) }
  28. else:
  29. return results
  30. me = frappe.db.get_value(doctype, name, ["parenttype", "parent"], as_dict=True)
  31. for dt, link in linkinfo.items():
  32. link["doctype"] = dt
  33. link_meta_bundle = frappe.desk.form.load.get_meta_bundle(dt)
  34. linkmeta = link_meta_bundle[0]
  35. if not linkmeta.get("issingle"):
  36. fields = [d.fieldname for d in linkmeta.get("fields", {"in_list_view":1,
  37. "fieldtype": ["not in", ["Image", "HTML", "Button", "Table"]]})] \
  38. + ["name", "modified", "docstatus"]
  39. if link.get("add_fields"):
  40. fields += link["add_fields"]
  41. fields = ["`tab{dt}`.`{fn}`".format(dt=dt, fn=sf.strip()) for sf in fields if sf
  42. and "`tab" not in sf]
  43. try:
  44. if link.get("get_parent"):
  45. if me and me.parent and me.parenttype == dt:
  46. ret = frappe.get_list(doctype=dt, fields=fields,
  47. filters=[[dt, "name", '=', me.parent]])
  48. else:
  49. ret = None
  50. elif link.get("child_doctype"):
  51. filters = [[link.get('child_doctype'), link.get("fieldname"), '=', name]]
  52. # dynamic link
  53. if link.get("doctype_fieldname"):
  54. filters.append([link.get('child_doctype'), link.get("doctype_fieldname"), "=", doctype])
  55. ret = frappe.get_list(doctype=dt, fields=fields, filters=filters)
  56. else:
  57. filters = [[dt, link.get("fieldname"), '=', name]]
  58. # dynamic link
  59. if link.get("doctype_fieldname"):
  60. filters.append([dt, link.get("doctype_fieldname"), "=", doctype])
  61. ret = frappe.get_list(doctype=dt, fields=fields, filters=filters)
  62. except frappe.PermissionError:
  63. if frappe.local.message_log:
  64. frappe.local.message_log.pop()
  65. continue
  66. if ret:
  67. results[dt] = ret
  68. frappe.cache().set_value(key, results, user=True)
  69. return results
  70. @frappe.whitelist()
  71. def get_linked_doctypes(doctype):
  72. """add list of doctypes this doctype is 'linked' with.
  73. Example, for Customer:
  74. {"Address": {"fieldname": "customer"}..}
  75. """
  76. return frappe.cache().hget("linked_doctypes", doctype, lambda: _get_linked_doctypes(doctype))
  77. def _get_linked_doctypes(doctype):
  78. ret = {}
  79. # find fields where this doctype is linked
  80. ret.update(get_linked_fields(doctype))
  81. ret.update(get_dynamic_linked_fields(doctype))
  82. # find links of parents
  83. links = frappe.db.sql("""select dt from `tabCustom Field`
  84. where (fieldtype="Table" and options=%s)""", (doctype))
  85. links += frappe.db.sql("""select parent from tabDocField
  86. where (fieldtype="Table" and options=%s)""", (doctype))
  87. for dt, in links:
  88. if not dt in ret:
  89. ret[dt] = {"get_parent": True}
  90. for dt in ret.keys():
  91. try:
  92. doctype_module = load_doctype_module(dt)
  93. except ImportError:
  94. # in case of Custom DocType
  95. continue
  96. if getattr(doctype_module, "exclude_from_linked_with", False):
  97. del ret[dt]
  98. return ret
  99. def get_linked_fields(doctype):
  100. links = frappe.db.sql("""select parent, fieldname from tabDocField
  101. where (fieldtype="Link" and options=%s)
  102. or (fieldtype="Select" and options=%s)""", (doctype, "link:"+ doctype))
  103. links += frappe.db.sql("""select dt as parent, fieldname from `tabCustom Field`
  104. where (fieldtype="Link" and options=%s)
  105. or (fieldtype="Select" and options=%s)""", (doctype, "link:"+ doctype))
  106. links = dict(links)
  107. ret = {}
  108. if links:
  109. for dt in links:
  110. ret[dt] = { "fieldname": links[dt] }
  111. # find out if linked in a child table
  112. for parent, options in frappe.db.sql("""select parent, options from tabDocField
  113. where fieldtype="Table"
  114. and options in (select name from tabDocType
  115. where istable=1 and name in (%s))""" % ", ".join(["%s"] * len(links)) ,tuple(links)):
  116. ret[parent] = {"child_doctype": options, "fieldname": links[options] }
  117. if options in ret:
  118. del ret[options]
  119. return ret
  120. def get_dynamic_linked_fields(doctype):
  121. ret = {}
  122. links = frappe.db.sql("""select parent as doctype, fieldname, options as doctype_fieldname
  123. from `tabDocField` where fieldtype='Dynamic Link'""", as_dict=True)
  124. links += frappe.db.sql("""select dt as doctype, fieldname, options as doctype_fieldname
  125. from `tabCustom Field` where fieldtype='Dynamic Link'""", as_dict=True)
  126. for df in links:
  127. if is_single(df.doctype):
  128. continue
  129. # optimized to get both link exists and parenttype
  130. possible_link = frappe.db.sql("""select distinct `{doctype_fieldname}`, parenttype
  131. from `tab{doctype}` where `{doctype_fieldname}`=%s""".format(**df), doctype, as_dict=True)
  132. if possible_link:
  133. for d in possible_link:
  134. # is child
  135. if d.parenttype:
  136. ret[d.parenttype] = {
  137. "child_doctype": df.doctype,
  138. "fieldname": df.fieldname,
  139. "doctype_fieldname": df.doctype_fieldname
  140. }
  141. else:
  142. ret[df.doctype] = {
  143. "fieldname": df.fieldname,
  144. "doctype_fieldname": df.doctype_fieldname
  145. }
  146. return ret