You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

430 line
13 KiB

  1. # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
  2. # MIT License. See license.txt
  3. """
  4. Get metadata (main doctype with fields and permissions with all table doctypes)
  5. - if exists in cache, get it from cache
  6. - add custom fields
  7. - override properties from PropertySetter
  8. - sort based on prev_field
  9. - optionally, post process (add js, css, select fields), or without
  10. """
  11. from __future__ import unicode_literals
  12. # imports
  13. import webnotes
  14. import webnotes.model
  15. import webnotes.model.doc
  16. import webnotes.model.doclist
  17. from webnotes.utils import cint, get_base_path
  18. doctype_cache = webnotes.local('doctype_doctype_cache')
  19. docfield_types = webnotes.local('doctype_docfield_types')
  20. # doctype_cache = {}
  21. # docfield_types = None
  22. def get(doctype, processed=False, cached=True):
  23. """return doclist"""
  24. if cached:
  25. doclist = from_cache(doctype, processed)
  26. if doclist:
  27. if processed:
  28. update_language(doclist)
  29. return DocTypeDocList(doclist)
  30. load_docfield_types()
  31. # main doctype doclist
  32. doclist = get_doctype_doclist(doctype)
  33. # add doctypes of table fields
  34. table_types = [d.options for d in doclist \
  35. if d.doctype=='DocField' and d.fieldtype=='Table']
  36. for table_doctype in table_types:
  37. doclist += get_doctype_doclist(table_doctype)
  38. if processed:
  39. add_code(doctype, doclist)
  40. expand_selects(doclist)
  41. add_print_formats(doclist)
  42. add_search_fields(doclist)
  43. add_workflows(doclist)
  44. add_linked_with(doclist)
  45. to_cache(doctype, processed, doclist)
  46. if processed:
  47. update_language(doclist)
  48. return DocTypeDocList(doclist)
  49. def load_docfield_types():
  50. webnotes.local.doctype_docfield_types = dict(webnotes.conn.sql("""select fieldname, fieldtype from tabDocField
  51. where parent='DocField'"""))
  52. def add_workflows(doclist):
  53. from webnotes.model.workflow import get_workflow_name
  54. doctype = doclist[0].name
  55. # get active workflow
  56. workflow_name = get_workflow_name(doctype)
  57. if workflow_name and webnotes.conn.exists("Workflow", workflow_name):
  58. doclist += webnotes.get_doclist("Workflow", workflow_name)
  59. # add workflow states (for icons and style)
  60. for state in map(lambda d: d.state, doclist.get({"doctype":"Workflow Document State"})):
  61. doclist += webnotes.get_doclist("Workflow State", state)
  62. def get_doctype_doclist(doctype):
  63. """get doclist of single doctype"""
  64. doclist = webnotes.get_doclist('DocType', doctype)
  65. add_custom_fields(doctype, doclist)
  66. apply_property_setters(doctype, doclist)
  67. sort_fields(doclist)
  68. return doclist
  69. def sort_fields(doclist):
  70. """sort on basis of previous_field"""
  71. from webnotes.model.doclist import DocList
  72. newlist = DocList([])
  73. pending = filter(lambda d: d.doctype=='DocField', doclist)
  74. maxloops = 20
  75. while (pending and maxloops>0):
  76. maxloops -= 1
  77. for d in pending[:]:
  78. if d.previous_field:
  79. # field already added
  80. for n in newlist:
  81. if n.fieldname==d.previous_field:
  82. newlist.insert(newlist.index(n)+1, d)
  83. pending.remove(d)
  84. break
  85. else:
  86. newlist.append(d)
  87. pending.remove(d)
  88. # recurring at end
  89. if pending:
  90. newlist += pending
  91. # renum
  92. idx = 1
  93. for d in newlist:
  94. d.idx = idx
  95. idx += 1
  96. doclist.get({"doctype":["!=", "DocField"]}).extend(newlist)
  97. def apply_property_setters(doctype, doclist):
  98. for ps in webnotes.conn.sql("""select * from `tabProperty Setter` where
  99. doc_type=%s""", doctype, as_dict=1):
  100. if ps['doctype_or_field']=='DocType':
  101. if ps.get('property_type', None) in ('Int', 'Check'):
  102. ps['value'] = cint(ps['value'])
  103. doclist[0].fields[ps['property']] = ps['value']
  104. else:
  105. docfield = filter(lambda d: d.doctype=="DocField" and d.fieldname==ps['field_name'],
  106. doclist)
  107. if not docfield: continue
  108. if docfield_types.get(ps['property'], None) in ('Int', 'Check'):
  109. ps['value'] = cint(ps['value'])
  110. docfield[0].fields[ps['property']] = ps['value']
  111. def add_custom_fields(doctype, doclist):
  112. try:
  113. res = webnotes.conn.sql("""SELECT * FROM `tabCustom Field`
  114. WHERE dt = %s AND docstatus < 2""", doctype, as_dict=1)
  115. except Exception, e:
  116. if e.args[0]==1146:
  117. return doclist
  118. else:
  119. raise e
  120. for r in res:
  121. custom_field = webnotes.model.doc.Document(fielddata=r)
  122. # convert to DocField
  123. custom_field.fields.update({
  124. 'doctype': 'DocField',
  125. 'parent': doctype,
  126. 'parentfield': 'fields',
  127. 'parenttype': 'DocType',
  128. '__custom_field': 1
  129. })
  130. doclist.append(custom_field)
  131. return doclist
  132. def add_linked_with(doclist):
  133. """add list of doctypes this doctype is 'linked' with"""
  134. doctype = doclist[0].name
  135. links = webnotes.conn.sql("""select parent, fieldname from tabDocField
  136. where (fieldtype="Link" and options=%s)
  137. or (fieldtype="Select" and options=%s)""", (doctype, "link:"+ doctype))
  138. links += webnotes.conn.sql("""select dt as parent, fieldname from `tabCustom Field`
  139. where (fieldtype="Link" and options=%s)
  140. or (fieldtype="Select" and options=%s)""", (doctype, "link:"+ doctype))
  141. links = dict(links)
  142. if not links:
  143. return {}
  144. ret = {}
  145. for dt in links:
  146. ret[dt] = { "fieldname": links[dt] }
  147. for grand_parent, options in webnotes.conn.sql("""select parent, options from tabDocField
  148. where fieldtype="Table"
  149. and options in (select name from tabDocType
  150. where istable=1 and name in (%s))""" % ", ".join(["%s"] * len(links)) ,tuple(links)):
  151. ret[grand_parent] = {"child_doctype": options, "fieldname": links[options] }
  152. if options in ret:
  153. del ret[options]
  154. doclist[0].fields["__linked_with"] = ret
  155. def from_cache(doctype, processed):
  156. """ load doclist from cache.
  157. sets flag __from_cache in first doc of doclist if loaded from cache"""
  158. # from memory
  159. if doctype_cache and not processed and doctype in doctype_cache:
  160. return doctype_cache[doctype]
  161. doclist = webnotes.cache().get_value(cache_name(doctype, processed))
  162. if doclist:
  163. from webnotes.model.doclist import DocList
  164. doclist = DocList([webnotes.model.doc.Document(fielddata=d)
  165. for d in doclist])
  166. doclist[0].fields["__from_cache"] = 1
  167. return doclist
  168. def to_cache(doctype, processed, doclist):
  169. if not doctype_cache:
  170. webnotes.local.doctype_doctype_cache = {}
  171. webnotes.cache().set_value(cache_name(doctype, processed),
  172. [d.fields for d in doclist])
  173. if not processed:
  174. doctype_cache[doctype] = doclist
  175. def cache_name(doctype, processed):
  176. """returns cache key"""
  177. suffix = ""
  178. if processed:
  179. suffix = ":Raw"
  180. return "doctype:" + doctype + suffix
  181. def clear_cache(doctype=None):
  182. def clear_single(dt):
  183. webnotes.cache().delete_value(cache_name(dt, False))
  184. webnotes.cache().delete_value(cache_name(dt, True))
  185. webnotes.cache().delete_value("_server_script:" + dt)
  186. if doctype_cache and doctype in doctype_cache:
  187. del doctype_cache[dt]
  188. if doctype:
  189. clear_single(doctype)
  190. # clear all parent doctypes
  191. for dt in webnotes.conn.sql("""select parent from tabDocField
  192. where fieldtype="Table" and options=%s""", doctype):
  193. clear_single(dt[0])
  194. else:
  195. # clear all
  196. for dt in webnotes.conn.sql("""select name from tabDocType"""):
  197. clear_single(dt[0])
  198. def add_code(doctype, doclist):
  199. import os
  200. from webnotes.modules import scrub, get_module_path
  201. doc = doclist[0]
  202. path = os.path.join(get_module_path(doc.module), 'doctype', scrub(doc.name))
  203. def _add_code(fname, fieldname):
  204. fpath = os.path.join(path, fname)
  205. if os.path.exists(fpath):
  206. with open(fpath, 'r') as f:
  207. doc.fields[fieldname] = f.read()
  208. _add_code(scrub(doc.name) + '.js', '__js')
  209. _add_code(scrub(doc.name) + '.css', '__css')
  210. _add_code('%s_list.js' % scrub(doc.name), '__list_js')
  211. _add_code('%s_calendar.js' % scrub(doc.name), '__calendar_js')
  212. _add_code('%s_map.js' % scrub(doc.name), '__map_js')
  213. add_embedded_js(doc)
  214. def add_embedded_js(doc):
  215. """embed all require files"""
  216. import re, os
  217. from webnotes import conf
  218. # custom script
  219. custom = webnotes.conn.get_value("Custom Script", {"dt": doc.name,
  220. "script_type": "Client"}, "script") or ""
  221. doc.fields['__js'] = ((doc.fields.get('__js') or '') + '\n' + custom).encode("utf-8")
  222. def _sub(match):
  223. require_path = re.search('["\'][^"\']*["\']', match.group(0)).group(0)[1:-1]
  224. fpath = os.path.join(get_base_path(), require_path)
  225. if os.path.exists(fpath):
  226. with open(fpath, 'r') as f:
  227. return '\n' + unicode(f.read(), "utf-8") + '\n'
  228. else:
  229. return 'wn.require("%s")' % require_path
  230. if doc.fields.get('__js'):
  231. doc.fields['__js'] = re.sub('(wn.require\([^\)]*.)', _sub, doc.fields['__js'])
  232. def expand_selects(doclist):
  233. for d in filter(lambda d: d.fieldtype=='Select' \
  234. and (d.options or '').startswith('link:'), doclist):
  235. doctype = d.options.split("\n")[0][5:]
  236. d.link_doctype = doctype
  237. d.options = '\n'.join([''] + [o.name for o in webnotes.conn.sql("""select
  238. name from `tab%s` where docstatus<2 order by name asc""" % doctype, as_dict=1)])
  239. def add_print_formats(doclist):
  240. print_formats = webnotes.conn.sql("""select * FROM `tabPrint Format`
  241. WHERE doc_type=%s AND docstatus<2""", doclist[0].name, as_dict=1)
  242. for pf in print_formats:
  243. doclist.append(webnotes.model.doc.Document('Print Format', fielddata=pf))
  244. def get_property(dt, prop, fieldname=None):
  245. """get a doctype property"""
  246. doctypelist = get(dt)
  247. if fieldname:
  248. field = doctypelist.get_field(fieldname)
  249. return field and field.fields.get(prop) or None
  250. else:
  251. return doctypelist[0].fields.get(prop)
  252. def get_link_fields(doctype):
  253. """get docfields of links and selects with "link:" """
  254. doctypelist = get(doctype)
  255. return doctypelist.get({"fieldtype":"Link"}).extend(doctypelist.get({"fieldtype":"Select",
  256. "options": "^link:"}))
  257. def add_validators(doctype, doclist):
  258. for validator in webnotes.conn.sql("""select name from `tabDocType Validator` where
  259. for_doctype=%s""", doctype, as_dict=1):
  260. doclist.extend(webnotes.get_doclist('DocType Validator', validator.name))
  261. def add_search_fields(doclist):
  262. """add search fields found in the doctypes indicated by link fields' options"""
  263. for lf in doclist.get({"fieldtype": "Link", "options":["!=", "[Select]"]}):
  264. if lf.options:
  265. search_fields = get(lf.options)[0].search_fields
  266. if search_fields:
  267. lf.search_fields = map(lambda sf: sf.strip(), search_fields.split(","))
  268. def update_language(doclist):
  269. """update language"""
  270. if webnotes.lang != 'en':
  271. from webnotes.modules import get_doc_path
  272. if not hasattr(webnotes.local, 'translations'):
  273. webnotes.local.translations = {}
  274. translations = webnotes.local.translations
  275. # load languages for each doctype
  276. from webnotes.translate import get_lang_data
  277. _messages = {}
  278. for d in doclist:
  279. if d.doctype=='DocType':
  280. _messages.update(get_lang_data(get_doc_path(d.module, d.doctype, d.name),
  281. webnotes.lang, 'doc'))
  282. _messages.update(get_lang_data(get_doc_path(d.module, d.doctype, d.name),
  283. webnotes.lang, 'js'))
  284. doc = doclist[0]
  285. # attach translations to client
  286. doc.fields["__messages"] = _messages
  287. if not webnotes.lang in translations:
  288. translations[webnotes.lang] = webnotes._dict({})
  289. translations[webnotes.lang].update(_messages)
  290. class DocTypeDocList(webnotes.model.doclist.DocList):
  291. def get_field(self, fieldname, parent=None, parentfield=None):
  292. filters = {"doctype":"DocField"}
  293. if isinstance(fieldname, dict):
  294. filters.update(fieldname)
  295. else:
  296. filters["fieldname"] = fieldname
  297. # if parentfield, get the name of the parent table
  298. if parentfield:
  299. parent = self.get_options(parentfield)
  300. if parent:
  301. filters["parent"] = parent
  302. else:
  303. filters["parent"] = self[0].name
  304. fields = self.get(filters)
  305. if fields:
  306. return fields[0]
  307. def get_fieldnames(self, filters=None):
  308. if not filters: filters = {}
  309. filters.update({"doctype": "DocField", "parent": self[0].name})
  310. return map(lambda df: df.fieldname, self.get(filters))
  311. def get_options(self, fieldname, parent=None, parentfield=None):
  312. return self.get_field(fieldname, parent, parentfield).options
  313. def get_label(self, fieldname, parent=None, parentfield=None):
  314. return self.get_field(fieldname, parent, parentfield).label
  315. def get_table_fields(self):
  316. return self.get({"doctype": "DocField", "fieldtype": "Table"})
  317. def get_parent_doclist(self):
  318. return webnotes.doclist([self[0]] + self.get({"parent": self[0].name}))
  319. def rename_field(doctype, old_fieldname, new_fieldname, lookup_field=None):
  320. """this function assumes that sync is NOT performed"""
  321. import webnotes.model
  322. doctype_list = get(doctype)
  323. old_field = doctype_list.get_field(lookup_field or old_fieldname)
  324. if not old_field:
  325. print "rename_field: " + (lookup_field or old_fieldname) + " not found."
  326. if old_field.fieldtype == "Table":
  327. # change parentfield of table mentioned in options
  328. webnotes.conn.sql("""update `tab%s` set parentfield=%s
  329. where parentfield=%s""" % (old_field.options.split("\n")[0], "%s", "%s"),
  330. (new_fieldname, old_fieldname))
  331. elif old_field.fieldtype not in webnotes.model.no_value_fields:
  332. # copy
  333. if doctype_list[0].issingle:
  334. webnotes.conn.sql("""update `tabSingles` set field=%s
  335. where doctype=%s and field=%s""",
  336. (new_fieldname, doctype, old_fieldname))
  337. else:
  338. webnotes.conn.sql("""update `tab%s` set `%s`=`%s`""" % \
  339. (doctype, new_fieldname, old_fieldname))