Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 
 
 
 

451 rinda
13 KiB

  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. # TODO:
  23. # Patch: Remove DocFormat
  24. # imports
  25. from __future__ import unicode_literals
  26. import webnotes
  27. import webnotes.model
  28. import webnotes.model.doc
  29. from webnotes.utils.cache import CacheItem
  30. class _DocType:
  31. """
  32. The _DocType object is created internally using the module's `get` method.
  33. """
  34. def __init__(self, name):
  35. self.name = name
  36. def make_doclist(self, form=1, force=False):
  37. """
  38. """
  39. # do not load from cache if auto cache clear is enabled
  40. import conf
  41. if hasattr(conf, 'auto_cache_clear'):
  42. force = not conf.auto_cache_clear
  43. if form and not force:
  44. cached_doclist = self.load_from_cache()
  45. if cached_doclist: return cached_doclist
  46. # Get parent doc and its fields
  47. doclist = webnotes.model.doc.get('DocType', self.name, 1)
  48. doclist += self.get_custom_fields(self.name)
  49. if form:
  50. table_fields = [t[0] for t in self.get_table_fields(doclist)]
  51. # for each unique table
  52. for t in list(set(table_fields)):
  53. # Get child doc and its fields
  54. table_doclist = webnotes.model.doc.get('DocType', t, 1)
  55. table_doclist += self.get_custom_fields(t)
  56. doclist += table_doclist
  57. self.apply_property_setters(doclist)
  58. if form:
  59. self.load_select_options(doclist)
  60. self.add_code(doclist[0])
  61. self.load_print_formats(doclist)
  62. self.insert_into_cache(doclist)
  63. return doclist
  64. def get_custom_fields(self, doc_type):
  65. """
  66. Gets a list of custom field docs masked as type DocField
  67. """
  68. from webnotes.model.doclist import DocList
  69. custom_doclist = DocList()
  70. res = webnotes.conn.sql("""SELECT * FROM `tabCustom Field`
  71. WHERE dt = %s AND docstatus < 2""", doc_type, as_dict=1)
  72. for r in res:
  73. # Cheat! Mask Custom Field as DocField
  74. custom_field = webnotes.model.doc.Document(fielddata=r)
  75. self.mask_custom_field(custom_field, doc_type)
  76. custom_doclist.append(custom_field)
  77. return custom_doclist
  78. def mask_custom_field(self, custom_field, doc_type):
  79. """
  80. Masks doctype and parent related properties of Custom Field as that
  81. of DocField
  82. """
  83. custom_field.fields.update({
  84. 'doctype': 'DocField',
  85. 'parent': doc_type,
  86. 'parentfield': 'fields',
  87. 'parenttype': 'DocType',
  88. })
  89. def get_table_fields(self, doclist):
  90. """
  91. Returns [[options, fieldname]] of fields of type 'Table'
  92. """
  93. table_fields = []
  94. for d in doclist:
  95. if d.doctype=='DocField' and d.fieldtype == 'Table':
  96. table_fields.append([d.options, d.fieldname])
  97. return table_fields
  98. def apply_property_setters(self, doclist):
  99. """
  100. """
  101. property_dict, doc_type_list = self.get_property_setters(doclist)
  102. for d in doclist:
  103. self.update_field_properties(d, property_dict)
  104. self.apply_previous_field_properties(doclist, property_dict,
  105. doc_type_list)
  106. def get_property_setters(self, doclist):
  107. """
  108. Returns a dict of property setter lists and doc_type_list
  109. """
  110. from webnotes.utils import cstr
  111. property_dict = {}
  112. doc_type_list = list(set(
  113. d.doctype=='DocType' and d.name or d.parent
  114. for d in doclist))
  115. in_string = '", "'.join(doc_type_list)
  116. for ps in webnotes.conn.sql("""\
  117. SELECT doc_type, field_name, property, property_type, value
  118. FROM `tabProperty Setter`
  119. WHERE doc_type IN ("%s")""" % in_string, as_dict=1):
  120. property_dict.setdefault(ps.get('doc_type'),
  121. {}).setdefault(cstr(ps.get('field_name')), []).append(ps)
  122. return property_dict, doc_type_list
  123. def update_field_properties(self, d, property_dict):
  124. """
  125. apply properties except previous_field ones
  126. """
  127. from webnotes.utils import cstr
  128. # get property setters for a given doctype's fields
  129. doctype_property_dict = (d.doctype=='DocField' and property_dict.get(d.parent) or
  130. property_dict.get(d.name))
  131. if not (doctype_property_dict and doctype_property_dict.get(cstr(d.fieldname))): return
  132. from webnotes.utils import cint
  133. prop_updates = []
  134. for prop in doctype_property_dict.get(cstr(d.fieldname)):
  135. if prop.get('property')=='previous_field': continue
  136. if prop.get('property_type') == 'Check' or \
  137. prop.get('value') in ['0', '1']:
  138. prop_updates.append([prop.get('property'), cint(prop.get('value'))])
  139. else:
  140. prop_updates.append([prop.get('property'), prop.get('value')])
  141. prop_updates and d.fields.update(dict(prop_updates))
  142. def apply_previous_field_properties(self, doclist, property_dict,
  143. doc_type_list):
  144. """
  145. """
  146. prev_field_dict = self.get_previous_field_properties(property_dict)
  147. if not prev_field_dict: return
  148. for doc_type in doc_type_list:
  149. docfields = self.get_sorted_docfields(doclist, doc_type)
  150. docfields = self.sort_docfields(doc_type, docfields, prev_field_dict)
  151. if docfields: self.change_idx(doclist, docfields, doc_type)
  152. def get_previous_field_properties(self, property_dict):
  153. """
  154. setup prev_field_dict
  155. """
  156. from webnotes.utils import cstr
  157. doctype_prev_field_list = []
  158. for doc_type in property_dict:
  159. prev_field_list = []
  160. for prop_list in property_dict.get(doc_type).values():
  161. for prop in prop_list:
  162. if prop.get('property') == 'previous_field':
  163. prev_field_list.append([prop.get('value'),
  164. prop.get('field_name')])
  165. break
  166. if not prev_field_list: continue
  167. doctype_prev_field_list.append([doc_type, dict(prev_field_list)])
  168. if not doctype_prev_field_list: return
  169. return dict(doctype_prev_field_list)
  170. def get_sorted_docfields(self, doclist, doc_type):
  171. """
  172. get a sorted list of docfield names
  173. """
  174. sorted_list = sorted([
  175. d for d in doclist
  176. if d.doctype == 'DocField'
  177. and d.parent == doc_type
  178. ], key=lambda df: df.idx)
  179. return [d.fieldname for d in sorted_list]
  180. def sort_docfields(self, doc_type, docfields, prev_field_dict):
  181. """
  182. """
  183. temp_dict = prev_field_dict.get(doc_type)
  184. if not temp_dict: return
  185. prev_field = 'None' in temp_dict and 'None' or docfields[0]
  186. i = 0
  187. while temp_dict:
  188. get_next_docfield = True
  189. cur_field = temp_dict.get(prev_field)
  190. if cur_field and cur_field in docfields:
  191. try:
  192. del temp_dict[prev_field]
  193. if prev_field in docfields:
  194. docfields.remove(cur_field)
  195. docfields.insert(docfields.index(prev_field) + 1,
  196. cur_field)
  197. elif prev_field == 'None':
  198. docfields.remove(cur_field)
  199. docfields.insert(0, cur_field)
  200. except ValueError:
  201. pass
  202. if cur_field in temp_dict:
  203. prev_field = cur_field
  204. get_next_docfield = False
  205. if get_next_docfield:
  206. i += 1
  207. if i>=len(docfields): break
  208. prev_field = docfields[i]
  209. keys, vals = temp_dict.keys(), temp_dict.values()
  210. if prev_field in vals:
  211. i -= 1
  212. prev_field = keys[vals.index(prev_field)]
  213. return docfields
  214. def change_idx(self, doclist, docfields, doc_type):
  215. for d in doclist:
  216. if d.fieldname and d.fieldname in docfields and d.parent == doc_type:
  217. d.idx = docfields.index(d.fieldname) + 1
  218. def add_code(self, doc):
  219. """add js, css code"""
  220. import os
  221. from webnotes.modules import scrub, get_module_path
  222. import conf
  223. module_path = get_module_path(doc.module)
  224. path = os.path.join(module_path, 'doctype', scrub(doc.name))
  225. def _add_code(fname, fieldname):
  226. fpath = os.path.join(path, fname)
  227. if os.path.exists(fpath):
  228. with open(fpath, 'r') as f:
  229. doc.fields[fieldname] = f.read()
  230. _add_code(scrub(doc.name) + '.js', '__js')
  231. _add_code(scrub(doc.name) + '.css', '__css')
  232. _add_code('%s_list.js' % scrub(doc.name), '__listjs')
  233. _add_code('help.md', 'description')
  234. # custom script
  235. from webnotes.model.code import get_custom_script
  236. custom = get_custom_script(doc.name, 'Client') or ''
  237. doc.fields['__js'] = doc.fields.setdefault('__js', '') + '\n' + custom
  238. # embed all require files
  239. import re
  240. def _sub(match):
  241. fpath = os.path.join(os.path.dirname(conf.__file__),
  242. re.search('["\'][^"\']*["\']', match.group(0)).group(0)[1:-1])
  243. if os.path.exists(fpath):
  244. with open(fpath, 'r') as f:
  245. return '\n' + f.read() + '\n'
  246. else:
  247. return '\n// no file "%s" found \n' % fpath
  248. if doc.fields.get('__js'):
  249. doc.fields['__js'] = re.sub('(wn.require\([^\)]*.)', _sub, doc.fields['__js'])
  250. def load_select_options(self, doclist):
  251. """
  252. Loads Select options for 'Select' fields
  253. with link: as start of options
  254. """
  255. for d in doclist:
  256. if (d.doctype == 'DocField' and d.fieldtype == 'Select' and
  257. d.options and d.options[:5].lower() == 'link:'):
  258. # Get various options
  259. opt_list = self._get_select_options(d)
  260. opt_list = [''] + [o[0] or '' for o in opt_list]
  261. d.options = "\n".join(opt_list)
  262. def _get_select_options(self, d):
  263. """
  264. Queries and returns select options
  265. (called by load_select_options)
  266. """
  267. op = d.options.split('\n')
  268. if len(op) > 1 and op[1][:4].lower() == 'sql:':
  269. # Execute the sql query
  270. query = op[1][4:].replace('__user',
  271. webnotes.session.get('user'))
  272. else:
  273. # Extract DocType and Conditions
  274. # and execute the resulting query
  275. dt = op[0][5:].strip()
  276. cond_list = [cond.replace('__user',
  277. webnotes.session.get('user')) for cond in op[1:]]
  278. query = """\
  279. SELECT name FROM `tab%s`
  280. WHERE %s docstatus!=2
  281. ORDER BY name ASC""" % (dt,
  282. cond_list and (" AND ".join(cond_list) + " AND ") or "")
  283. try:
  284. opt_list = webnotes.conn.sql(query)
  285. except:
  286. # WARNING: Exception suppressed
  287. opt_list = []
  288. return opt_list
  289. def load_print_formats(self, doclist):
  290. """
  291. Load Print Formats in doclist
  292. """
  293. # TODO: Process Print Formats for $import
  294. # to deprecate code in print_format.py
  295. # if this is implemented, clear CacheItem on saving print format
  296. print_formats = webnotes.conn.sql("""\
  297. SELECT * FROM `tabPrint Format`
  298. WHERE doc_type=%s AND docstatus<2""", doclist[0].fields.get('name'),
  299. as_dict=1)
  300. for pf in print_formats:
  301. if not pf: continue
  302. print_format_doc = webnotes.model.doc.Document('Print Format', fielddata=pf)
  303. doclist.append(print_format_doc)
  304. def load_from_cache(self):
  305. import json
  306. json_doclist = CacheItem(self.name).get()
  307. if json_doclist:
  308. return [webnotes.model.doc.Document(fielddata=d)
  309. for d in json.loads(json_doclist)]
  310. def insert_into_cache(self, doclist):
  311. import json
  312. json_doclist = json.dumps([d.fields for d in doclist])
  313. CacheItem(self.name).set(json_doclist)
  314. def get(dt, form=1, force=False):
  315. """
  316. Load "DocType" - called by form builder, report buider and from code.py (when there is no cache)
  317. """
  318. if not dt: return []
  319. doclist = _DocType(dt).make_doclist(form, force)
  320. return doclist
  321. # Deprecate after import_docs rewrite
  322. def get_field_property(dt, fieldname, property):
  323. """
  324. get a field property, override it from property setter if specified
  325. """
  326. field = webnotes.conn.sql("""
  327. select name, `%s`
  328. from tabDocField
  329. where parent=%s and fieldname=%s""" % (property, '%s', '%s'), (dt, fieldname))
  330. prop = webnotes.conn.sql("""
  331. select value
  332. from `tabProperty Setter`
  333. where doc_type=%s and field_name=%s and property=%s""", (dt, fieldname, property))
  334. if prop:
  335. return prop[0][0]
  336. else:
  337. return field[0][1]
  338. def get_property(dt, property, fieldname=None):
  339. """
  340. get a doctype property, override it from property setter if specified
  341. """
  342. if fieldname:
  343. prop = webnotes.conn.sql("""
  344. select value
  345. from `tabProperty Setter`
  346. where doc_type=%s and field_name=%s
  347. and property=%s""", (dt, fieldname, property))
  348. if prop:
  349. return prop[0][0]
  350. else:
  351. val = webnotes.conn.sql("""\
  352. SELECT %s FROM `tabDocField`
  353. WHERE parent = %s AND fieldname = %s""" % \
  354. (property, '%s', '%s'), (dt, fieldname))
  355. if val and val[0][0]: return val[0][0] or ''
  356. else:
  357. prop = webnotes.conn.sql("""
  358. select value
  359. from `tabProperty Setter`
  360. where doc_type=%s and doctype_or_field='DocType'
  361. and property=%s""", (dt, property))
  362. if prop:
  363. return prop[0][0]
  364. else:
  365. return webnotes.conn.get_value('DocType', dt, property)
  366. # Test Cases
  367. import unittest
  368. class DocTypeTest(unittest.TestCase):
  369. def setUp(self):
  370. self.name = 'Sales Order'
  371. self.dt = _DocType(self.name)
  372. def tearDown(self):
  373. webnotes.conn.rollback()
  374. def test_make_doclist(self):
  375. doclist = self.dt.make_doclist()
  376. for d in doclist:
  377. print d.idx, d.doctype, d.name, d.parent
  378. if not d.doctype: print d.fields
  379. #print "--", d.name, "--"
  380. #print d.doctype
  381. self.assertTrue(doclist)
  382. def test_get_custom_fields(self):
  383. return
  384. doclist = self.dt.get_custom_fields(self.name)
  385. for d in doclist:
  386. print "--", d.name, "--"
  387. print d.fields
  388. self.assertTrue(doclist)