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.

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