25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

309 lines
8.5 KiB

  1. # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. # metadata
  4. from __future__ import unicode_literals
  5. import frappe, json
  6. from frappe.utils import cstr, cint
  7. from frappe.model import integer_docfield_properties, default_fields
  8. from frappe.model.document import Document
  9. from frappe.model.base_document import BaseDocument
  10. from frappe.model.db_schema import type_map
  11. ######
  12. def get_meta(doctype, cached=True):
  13. if cached:
  14. return frappe.cache().get_value("meta:" + doctype, lambda: Meta(doctype))
  15. else:
  16. return Meta(doctype)
  17. def get_table_columns(doctype):
  18. return frappe.cache().get_value("table_columns:" + doctype, lambda: frappe.db.get_table_columns(doctype))
  19. def load_doctype_from_file(doctype):
  20. fname = frappe.scrub(doctype)
  21. with open(frappe.get_app_path("frappe", "core", "doctype", fname, fname + ".json"), "r") as f:
  22. txt = json.loads(f.read())
  23. for d in txt.get("fields", []):
  24. d["doctype"] = "DocField"
  25. for d in txt.get("permissions", []):
  26. d["doctype"] = "DocPerm"
  27. txt["fields"] = [BaseDocument(d) for d in txt["fields"]]
  28. if "permissions" in txt:
  29. txt["permissions"] = [BaseDocument(d) for d in txt["permissions"]]
  30. return txt
  31. class Meta(Document):
  32. _metaclass = True
  33. default_fields = default_fields[1:]
  34. special_doctypes = ("DocField", "DocPerm", "Role", "DocType", "Module Def")
  35. def __init__(self, doctype):
  36. self._fields = {}
  37. super(Meta, self).__init__("DocType", doctype)
  38. self.process()
  39. def load_from_db(self):
  40. try:
  41. super(Meta, self).load_from_db()
  42. except frappe.DoesNotExistError:
  43. if self.doctype=="DocType" and self.name in self.special_doctypes:
  44. self.__dict__.update(load_doctype_from_file(self.name))
  45. else:
  46. raise
  47. def get_link_fields(self):
  48. return self.get("fields", {"fieldtype": "Link", "options":["!=", "[Select]"]})
  49. def get_select_fields(self):
  50. return self.get("fields", {"fieldtype": "Select", "options":["not in",
  51. ["[Select]", "Loading...", "attach_files:"]]})
  52. def get_table_fields(self):
  53. if not hasattr(self, "_table_fields"):
  54. if self.name!="DocType":
  55. self._table_fields = self.get('fields', {"fieldtype":"Table"})
  56. else:
  57. self._table_fields = doctype_table_fields
  58. return self._table_fields
  59. def get_valid_columns(self):
  60. if not hasattr(self, "_valid_columns"):
  61. if self.name in ("DocType", "DocField", "DocPerm", "Property Setter"):
  62. self._valid_columns = get_table_columns(self.name)
  63. else:
  64. self._valid_columns = self.default_fields + \
  65. [df.fieldname for df in self.get("fields") if df.fieldtype in type_map]
  66. return self._valid_columns
  67. def get_table_field_doctype(self, fieldname):
  68. return { "fields": "DocField", "permissions": "DocPerm"}.get(fieldname)
  69. def get_field(self, fieldname):
  70. if not fieldname in self._fields:
  71. fields = self.get("fields", {"fieldname":fieldname})
  72. self._fields[fieldname] = fields[0] if fields else frappe._dict()
  73. return self._fields[fieldname]
  74. def get_label(self, fieldname):
  75. return self.get_field(fieldname).label
  76. def get_options(self, fieldname):
  77. return self.get_field(fieldname).options
  78. def get_search_fields(self):
  79. search_fields = self.search_fields or "name"
  80. search_fields = [d.strip() for d in search_fields.split(",")]
  81. if "name" not in search_fields:
  82. search_fields.append("name")
  83. return search_fields
  84. def get_list_fields(self):
  85. list_fields = ["name"] + [d.fieldname \
  86. for d in self.fields if (d.in_list_view and d.fieldtype in type_map)]
  87. if self.title_field and self.title_field not in list_fields:
  88. list_fields.append(self.title_field)
  89. return list_fields
  90. def process(self):
  91. # don't process for special doctypes
  92. # prevent's circular dependency
  93. if self.name in self.special_doctypes:
  94. return
  95. self.add_custom_fields()
  96. self.apply_property_setters()
  97. self.sort_fields()
  98. def add_custom_fields(self):
  99. try:
  100. self.extend("fields", frappe.db.sql("""SELECT * FROM `tabCustom Field`
  101. WHERE dt = %s AND docstatus < 2""", (self.name,), as_dict=1,
  102. update={"is_custom_field": True}))
  103. except Exception, e:
  104. if e.args[0]==1146:
  105. return
  106. else:
  107. raise
  108. def apply_property_setters(self):
  109. for ps in frappe.db.sql("""select * from `tabProperty Setter` where
  110. doc_type=%s""", (self.name,), as_dict=1):
  111. if ps.doctype_or_field=='DocType':
  112. if ps.property_type in ('Int', 'Check'):
  113. ps.value = cint(ps.value)
  114. self.set(ps.property, ps.value)
  115. else:
  116. docfield = self.get("fields", {"fieldname":ps.field_name}, limit=1)
  117. if docfield:
  118. docfield = docfield[0]
  119. else:
  120. continue
  121. if ps.property in integer_docfield_properties:
  122. ps.value = cint(ps.value)
  123. docfield.set(ps.property, ps.value)
  124. def sort_fields(self):
  125. """sort on basis of previous_field"""
  126. newlist = []
  127. pending = self.get("fields")
  128. if self.get("_idx"):
  129. for fieldname in json.loads(self.get("_idx")):
  130. d = self.get("fields", {"fieldname": fieldname}, limit=1)
  131. if d:
  132. newlist.append(d[0])
  133. pending.remove(d[0])
  134. else:
  135. maxloops = 20
  136. while (pending and maxloops>0):
  137. maxloops -= 1
  138. for d in pending[:]:
  139. if d.get("previous_field"):
  140. # field already added
  141. for n in newlist:
  142. if n.fieldname==d.previous_field:
  143. newlist.insert(newlist.index(n)+1, d)
  144. pending.remove(d)
  145. break
  146. else:
  147. newlist.append(d)
  148. pending.remove(d)
  149. # recurring at end
  150. if pending:
  151. newlist += pending
  152. # renum
  153. idx = 1
  154. for d in newlist:
  155. d.idx = idx
  156. idx += 1
  157. self.set("fields", newlist)
  158. def get_fields_to_check_permissions(self, user_permission_doctypes):
  159. fields = self.get("fields", {
  160. "fieldtype":"Link",
  161. "parent": self.name,
  162. "ignore_user_permissions":("!=", 1),
  163. "options":("in", user_permission_doctypes)
  164. })
  165. if self.name in user_permission_doctypes:
  166. fields.append(frappe._dict({
  167. "label":"Name",
  168. "fieldname":"name",
  169. "options": self.name
  170. }))
  171. return fields
  172. def is_print_hide(self, fieldname):
  173. df = self.get_field(fieldname)
  174. return df.get("__print_hide") or df.print_hide
  175. doctype_table_fields = [
  176. frappe._dict({"fieldname": "fields", "options": "DocField"}),
  177. frappe._dict({"fieldname": "permissions", "options": "DocPerm"})
  178. ]
  179. #######
  180. def is_single(doctype):
  181. try:
  182. return frappe.db.get_value("DocType", doctype, "issingle")
  183. except IndexError:
  184. raise Exception, 'Cannot determine whether %s is single' % doctype
  185. def get_parent_dt(dt):
  186. parent_dt = frappe.db.sql("""select parent from tabDocField
  187. where fieldtype="Table" and options=%s and (parent not like "old_parent:%%")
  188. limit 1""", dt)
  189. return parent_dt and parent_dt[0][0] or ''
  190. def set_fieldname(field_id, fieldname):
  191. frappe.db.set_value('DocField', field_id, 'fieldname', fieldname)
  192. def get_field_currency(df, doc):
  193. """get currency based on DocField options and fieldvalue in doc"""
  194. currency = None
  195. if not df.get("options"):
  196. return None
  197. if ":" in cstr(df.get("options")):
  198. split_opts = df.get("options").split(":")
  199. if len(split_opts)==3:
  200. currency = frappe.db.get_value(split_opts[0], doc.get(split_opts[1]),
  201. split_opts[2])
  202. else:
  203. currency = doc.get(df.get("options"))
  204. return currency
  205. def get_field_precision(df, doc):
  206. """get precision based on DocField options and fieldvalue in doc"""
  207. from frappe.utils import get_number_format_info
  208. if cint(df.precision):
  209. precision = cint(df.precision)
  210. elif df.fieldtype == "Currency":
  211. number_format = None
  212. currency = get_field_currency(df, doc)
  213. if not currency:
  214. # use default currency
  215. currency = frappe.db.get_default("currency")
  216. if currency:
  217. number_format = frappe.db.get_value("Currency", currency, "number_format")
  218. if not number_format:
  219. number_format = frappe.db.get_default("number_format") or "#,###.##"
  220. decimal_str, comma_str, precision = get_number_format_info(number_format)
  221. else:
  222. precision = cint(frappe.db.get_default("float_precision")) or 3
  223. return precision
  224. def clear_cache(doctype=None):
  225. def clear_single(dt):
  226. frappe.cache().delete_value("meta:" + dt)
  227. frappe.cache().delete_value("form_meta:" + dt)
  228. frappe.cache().delete_value("table_columns:" + dt)
  229. if doctype:
  230. clear_single(doctype)
  231. # clear all parent doctypes
  232. for dt in frappe.db.sql("""select parent from tabDocField
  233. where fieldtype="Table" and options=%s""", (doctype,)):
  234. clear_single(dt[0])
  235. # clear all notifications
  236. from frappe.core.doctype.notification_count.notification_count import delete_notification_count_for
  237. delete_notification_count_for(doctype)
  238. else:
  239. # clear all
  240. for dt in frappe.db.sql("""select name from tabDocType"""):
  241. clear_single(dt[0])
  242. frappe.cache().delete_value("is_table")