Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 
 

223 linhas
7.1 KiB

  1. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. # metadata
  4. from __future__ import unicode_literals
  5. import frappe, os
  6. from frappe.model.meta import Meta
  7. from frappe.modules import scrub, get_module_path, load_doctype_module
  8. from frappe.model.workflow import get_workflow_name
  9. from frappe.utils import get_html_format
  10. from frappe.translate import make_dict_from_messages, extract_messages_from_code
  11. from frappe.utils.jinja import render_include
  12. from frappe.build import html_to_js_template
  13. ######
  14. def get_meta(doctype, cached=True):
  15. if cached and not frappe.conf.developer_mode:
  16. meta = frappe.cache().hget("form_meta", doctype, lambda: FormMeta(doctype))
  17. else:
  18. meta = FormMeta(doctype)
  19. if frappe.local.lang != 'en':
  20. meta.set_translations(frappe.local.lang)
  21. return meta
  22. class FormMeta(Meta):
  23. def __init__(self, doctype):
  24. super(FormMeta, self).__init__(doctype)
  25. self.load_assets()
  26. def load_assets(self):
  27. self.add_search_fields()
  28. self.add_linked_document_type()
  29. if not self.istable:
  30. self.add_linked_with()
  31. self.add_code()
  32. self.load_print_formats()
  33. self.load_workflows()
  34. self.load_templates()
  35. def as_dict(self, no_nulls=False):
  36. d = super(FormMeta, self).as_dict(no_nulls=no_nulls)
  37. for k in ("__js", "__css", "__list_js", "__calendar_js", "__map_js",
  38. "__linked_with", "__messages", "__print_formats", "__workflow_docs",
  39. "__form_grid_templates", "__listview_template"):
  40. d[k] = self.get(k)
  41. for i, df in enumerate(d.get("fields")):
  42. for k in ("search_fields", "is_custom_field", "linked_document_type"):
  43. df[k] = self.get("fields")[i].get(k)
  44. return d
  45. def add_code(self):
  46. path = os.path.join(get_module_path(self.module), 'doctype', scrub(self.name))
  47. def _get_path(fname):
  48. return os.path.join(path, scrub(fname))
  49. self._add_code(_get_path(self.name + '.js'), '__js')
  50. self._add_code(_get_path(self.name + '.css'), "__css")
  51. self._add_code(_get_path(self.name + '_list.js'), '__list_js')
  52. self._add_code(_get_path(self.name + '_calendar.js'), '__calendar_js')
  53. listview_template = _get_path(self.name + '_list.html')
  54. if os.path.exists(listview_template):
  55. self.set("__listview_template", get_html_format(listview_template))
  56. self.add_code_via_hook("doctype_js", "__js")
  57. self.add_code_via_hook("doctype_list_js", "__list_js")
  58. self.add_custom_script()
  59. self.add_html_templates(path)
  60. def _add_code(self, path, fieldname):
  61. js = frappe.read_file(path)
  62. if js:
  63. self.set(fieldname, (self.get(fieldname) or "") + "\n\n" + render_include(js))
  64. def add_html_templates(self, path):
  65. if self.custom:
  66. return
  67. js = ""
  68. for fname in os.listdir(path):
  69. if fname.endswith(".html"):
  70. with open(os.path.join(path, fname), 'r') as f:
  71. template = unicode(f.read(), "utf-8")
  72. js += html_to_js_template(fname, template)
  73. self.set("__js", (self.get("__js") or "") + js)
  74. def add_code_via_hook(self, hook, fieldname):
  75. for app_name in frappe.get_installed_apps():
  76. code_hook = frappe.get_hooks(hook, default={}, app_name=app_name)
  77. if not code_hook:
  78. continue
  79. files = code_hook.get(self.name, [])
  80. if not isinstance(files, list):
  81. files = [files]
  82. for file in files:
  83. path = frappe.get_app_path(app_name, *file.strip("/").split("/"))
  84. self._add_code(path, fieldname)
  85. def add_custom_script(self):
  86. """embed all require files"""
  87. # custom script
  88. custom = frappe.db.get_value("Custom Script", {"dt": self.name,
  89. "script_type": "Client"}, "script") or ""
  90. self.set("__js", (self.get('__js') or '') + "\n\n" + custom)
  91. def add_search_fields(self):
  92. """add search fields found in the doctypes indicated by link fields' options"""
  93. for df in self.get("fields", {"fieldtype": "Link", "options":["!=", "[Select]"]}):
  94. if df.options:
  95. search_fields = frappe.get_meta(df.options).search_fields
  96. if search_fields:
  97. df.search_fields = map(lambda sf: sf.strip(), search_fields.split(","))
  98. def add_linked_document_type(self):
  99. for df in self.get("fields", {"fieldtype": "Link"}):
  100. if df.options:
  101. try:
  102. df.linked_document_type = frappe.get_meta(df.options).document_type
  103. except frappe.DoesNotExistError:
  104. # edge case where options="[Select]"
  105. pass
  106. def add_linked_with(self):
  107. """add list of doctypes this doctype is 'linked' with.
  108. Example, for Customer:
  109. {"Address": {"fieldname": "customer"}..}
  110. """
  111. # find fields where this doctype is linked
  112. links = frappe.db.sql("""select parent, fieldname from tabDocField
  113. where (fieldtype="Link" and options=%s)
  114. or (fieldtype="Select" and options=%s)""", (self.name, "link:"+ self.name))
  115. links += frappe.db.sql("""select dt as parent, fieldname from `tabCustom Field`
  116. where (fieldtype="Link" and options=%s)
  117. or (fieldtype="Select" and options=%s)""", (self.name, "link:"+ self.name))
  118. links = dict(links)
  119. ret = {}
  120. for dt in links:
  121. ret[dt] = { "fieldname": links[dt] }
  122. if links:
  123. # find out if linked in a child table
  124. for parent, options in frappe.db.sql("""select parent, options from tabDocField
  125. where fieldtype="Table"
  126. and options in (select name from tabDocType
  127. where istable=1 and name in (%s))""" % ", ".join(["%s"] * len(links)) ,tuple(links)):
  128. ret[parent] = {"child_doctype": options, "fieldname": links[options] }
  129. if options in ret:
  130. del ret[options]
  131. # find links of parents
  132. links = frappe.db.sql("""select dt from `tabCustom Field`
  133. where (fieldtype="Table" and options=%s)""", (self.name))
  134. links += frappe.db.sql("""select parent from tabDocField
  135. where (fieldtype="Table" and options=%s)""", (self.name))
  136. for dt, in links:
  137. if not dt in ret:
  138. ret[dt] = {"get_parent": True}
  139. self.set("__linked_with", ret, as_value=True)
  140. def load_print_formats(self):
  141. print_formats = frappe.db.sql("""select * FROM `tabPrint Format`
  142. WHERE doc_type=%s AND docstatus<2 and ifnull(disabled, 0)=0""", (self.name,), as_dict=1,
  143. update={"doctype":"Print Format"})
  144. self.set("__print_formats", print_formats, as_value=True)
  145. def load_workflows(self):
  146. # get active workflow
  147. workflow_name = get_workflow_name(self.name)
  148. workflow_docs = []
  149. if workflow_name and frappe.db.exists("Workflow", workflow_name):
  150. workflow = frappe.get_doc("Workflow", workflow_name)
  151. workflow_docs.append(workflow)
  152. for d in workflow.get("states"):
  153. workflow_docs.append(frappe.get_doc("Workflow State", d.state))
  154. self.set("__workflow_docs", workflow_docs, as_value=True)
  155. def load_templates(self):
  156. if not self.custom:
  157. module = load_doctype_module(self.name)
  158. app = module.__name__.split(".")[0]
  159. templates = {}
  160. if hasattr(module, "form_grid_templates"):
  161. for key, path in module.form_grid_templates.iteritems():
  162. templates[key] = get_html_format(frappe.get_app_path(app, path))
  163. self.set("__form_grid_templates", templates)
  164. def set_translations(self, lang):
  165. self.set("__messages", frappe.get_lang_dict("doctype", self.name))
  166. # set translations for grid templates
  167. if self.get("__form_grid_templates"):
  168. for content in self.get("__form_grid_templates").values():
  169. messages = extract_messages_from_code(content)
  170. messages = make_dict_from_messages(messages)
  171. self.get("__messages").update(messages, as_value=True)