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.
 
 
 
 
 
 

391 rivejä
10 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. from __future__ import unicode_literals
  23. """build query for doclistview and return results"""
  24. import webnotes, json
  25. import webnotes.defaults
  26. tables = None
  27. doctypes = {}
  28. roles = []
  29. @webnotes.whitelist()
  30. def get(arg=None):
  31. query = build_query(arg)
  32. return compress(webnotes.conn.sql(query, as_dict=1))
  33. def build_query(arg=None):
  34. """
  35. build query
  36. gets doctype, subject, filters
  37. limit_start, limit_page_length
  38. """
  39. data = prepare_data(arg)
  40. if 'query' in data:
  41. return run_custom_query(data)
  42. query = """select %(fields)s from %(tables)s where %(conditions)s
  43. %(group_by)s order by %(order_by)s %(limit)s""" % data
  44. return query
  45. def prepare_data(arg=None):
  46. global tables
  47. if arg:
  48. data = webnotes._dict(arg)
  49. else:
  50. data = webnotes._dict(webnotes.form_dict)
  51. if 'query' in data:
  52. return data
  53. if isinstance(data.get("filters"), basestring):
  54. data["filters"] = json.loads(data["filters"])
  55. if isinstance(data.get("fields"), basestring):
  56. data["fields"] = json.loads(data["fields"])
  57. tables = get_tables(data)
  58. load_doctypes()
  59. remove_user_tags(data)
  60. conditions = build_conditions(data)
  61. # query dict
  62. data['tables'] = ', '.join(tables)
  63. data['conditions'] = ' and '.join(conditions)
  64. data['fields'] = ', '.join(data.fields)
  65. if not data.get('order_by'):
  66. data['order_by'] = tables[0] + '.modified desc'
  67. if data.get('group_by'):
  68. data['group_by'] = "group by " + data.get('group_by')
  69. else:
  70. data['group_by'] = ''
  71. check_sort_by_table(data.get('order_by'), tables)
  72. add_limit(data)
  73. return data
  74. def compress(data):
  75. """separate keys and values"""
  76. if not data: return data
  77. values = []
  78. keys = data[0].keys()
  79. for row in data:
  80. new_row = []
  81. for key in keys:
  82. new_row.append(row[key])
  83. values.append(new_row)
  84. return {
  85. "keys": keys,
  86. "values": values
  87. }
  88. def check_sort_by_table(sort_by, tables):
  89. """check atleast 1 column selected from the sort by table """
  90. tbl = sort_by.split('.')[0]
  91. if tbl not in tables:
  92. if tbl.startswith('`'):
  93. tbl = tbl[4:-1]
  94. webnotes.msgprint("Please select atleast 1 column from '%s' to sort"\
  95. % tbl, raise_exception=1)
  96. def run_custom_query(data):
  97. """run custom query"""
  98. query = data['query']
  99. if '%(key)s' in query:
  100. query = query.replace('%(key)s', 'name')
  101. return webnotes.conn.sql(query, as_dict=1)
  102. def load_doctypes():
  103. """load all doctypes and roles"""
  104. global doctypes, roles
  105. import webnotes.model.doctype
  106. roles = webnotes.get_roles()
  107. for t in tables:
  108. if t.startswith('`'):
  109. doctype = t[4:-1]
  110. if not webnotes.has_permission(doctype):
  111. webnotes.response['403'] = 1
  112. raise webnotes.PermissionError, doctype
  113. doctypes[doctype] = webnotes.model.doctype.get(doctype)
  114. def remove_user_tags(data):
  115. """remove column _user_tags if not in table"""
  116. for fld in data.fields:
  117. if '_user_tags' in fld:
  118. if not '_user_tags' in get_table_columns(data.doctype):
  119. del data.fields[data.fields.index(fld)]
  120. break
  121. def add_limit(data):
  122. if 'limit_page_length' in data:
  123. data['limit'] = 'limit %(limit_start)s, %(limit_page_length)s' % data
  124. else:
  125. data['limit'] = ''
  126. def build_conditions(data):
  127. """build conditions"""
  128. # docstatus condition
  129. if isinstance(data.docstatus, basestring):
  130. data.docstatus = json.loads(data.docstatus)
  131. if data.docstatus:
  132. conditions = [tables[0] + '.docstatus in (' + ','.join(data.docstatus) + ')']
  133. else:
  134. # default condition
  135. conditions = [tables[0] + '.docstatus < 2']
  136. # make conditions from filters
  137. build_filter_conditions(data, conditions)
  138. # join parent, child tables
  139. for tname in tables[1:]:
  140. conditions.append(tname + '.parent = ' + tables[0] + '.name')
  141. # match conditions
  142. match_conditions = build_match_conditions(data)
  143. if match_conditions:
  144. conditions.append(match_conditions)
  145. return conditions
  146. def build_filter_conditions(data, conditions):
  147. """build conditions from user filters"""
  148. from webnotes.utils import cstr
  149. for f in data.filters:
  150. tname = ('`tab' + f[0] + '`')
  151. if not tname in tables:
  152. tables.append(tname)
  153. # prepare in condition
  154. if f[2]=='in':
  155. opts = ["'" + t.strip().replace("'", "\'") + "'" for t in f[3].split(',')]
  156. f[3] = "(" + ', '.join(opts) + ")"
  157. conditions.append(tname + '.' + f[1] + " " + f[2] + " " + f[3])
  158. else:
  159. if isinstance(f[3], basestring):
  160. f[3] = "'" + f[3].replace("'", "\'") + "'"
  161. conditions.append(tname + '.' + f[1] + " " + f[2] + " " + f[3])
  162. else:
  163. conditions.append('ifnull(' + tname + '.' + f[1] + ",0) " + f[2] \
  164. + " " + cstr(f[3]))
  165. def build_match_conditions(data):
  166. """add match conditions if applicable"""
  167. match_conditions = []
  168. match = True
  169. if not tables or not doctypes:
  170. global tables
  171. tables = get_tables(data)
  172. load_doctypes()
  173. for d in doctypes[data['doctype']]:
  174. if d.doctype == 'DocPerm' and d.parent == data['doctype']:
  175. if d.role in roles:
  176. if d.match: # role applicable
  177. if ':' in d.match:
  178. document_key, default_key = d.match.split(":")
  179. else:
  180. default_key = document_key = d.match
  181. for v in webnotes.defaults.get_user_default_as_list(default_key) or ["** No Match **"]:
  182. match_conditions.append('`tab%s`.%s="%s"' % (data['doctype'],
  183. document_key, v))
  184. elif d.read == 1 and d.permlevel == 0:
  185. # don't restrict if another read permission at level 0
  186. # exists without a match restriction
  187. match = False
  188. if match_conditions and match:
  189. return '('+ ' or '.join(match_conditions) +')'
  190. def get_tables(data):
  191. """extract tables from fields"""
  192. tables = ['`tab' + data['doctype'] + '`']
  193. # add tables from fields
  194. for f in data.get('fields') or []:
  195. if "." not in f: continue
  196. table_name = f.split('.')[0]
  197. if table_name.lower().startswith('group_concat('):
  198. table_name = table_name[13:]
  199. # check if ifnull function is used
  200. if table_name.lower().startswith('ifnull('):
  201. table_name = table_name[7:]
  202. if not table_name[0]=='`':
  203. table_name = '`' + table_name + '`'
  204. if not table_name in tables:
  205. tables.append(table_name)
  206. return tables
  207. @webnotes.whitelist()
  208. def save_report():
  209. """save report"""
  210. from webnotes.model.doc import Document
  211. data = webnotes.form_dict
  212. if webnotes.conn.exists('Report', data['name'].title()):
  213. d = Document('Report', data['name'].title())
  214. else:
  215. d = Document('Report')
  216. d.name = data['name']
  217. d.ref_doctype = data['doctype']
  218. d.json = data['json']
  219. webnotes.bean([d]).save()
  220. webnotes.msgprint("%s saved." % d.name)
  221. return d.name
  222. @webnotes.whitelist()
  223. def export_query():
  224. """export from report builder"""
  225. # TODO: validate use is allowed to export
  226. verify_export_allowed()
  227. ret = webnotes.conn.sql(build_query(), as_dict=1)
  228. columns = [x[0] for x in webnotes.conn.get_description()]
  229. data = [['Sr'] + get_labels(columns),]
  230. # flatten dict
  231. cnt = 1
  232. for row in ret:
  233. flat = [cnt,]
  234. for c in columns:
  235. flat.append(row.get(c))
  236. data.append(flat)
  237. cnt += 1
  238. # convert to csv
  239. from cStringIO import StringIO
  240. import csv
  241. f = StringIO()
  242. writer = csv.writer(f)
  243. from webnotes.utils import cstr
  244. for r in data:
  245. # encode only unicode type strings and not int, floats etc.
  246. writer.writerow(map(lambda v: isinstance(v, unicode) and v.encode('utf-8') or v, r))
  247. f.seek(0)
  248. webnotes.response['result'] = unicode(f.read(), 'utf-8')
  249. webnotes.response['type'] = 'csv'
  250. webnotes.response['doctype'] = [t[4:-1] for t in tables][0]
  251. def verify_export_allowed():
  252. """throw exception if user is not allowed to export"""
  253. global roles
  254. roles = webnotes.get_roles()
  255. if not ('Administrator' in roles or 'System Manager' in roles or 'Report Manager' in roles):
  256. raise webnotes.PermissionError
  257. def get_labels(columns):
  258. """get column labels based on column names"""
  259. label_dict = {}
  260. for doctype in doctypes:
  261. for d in doctypes[doctype]:
  262. if d.doctype=='DocField' and d.fieldname:
  263. label_dict[d.fieldname] = d.label
  264. return map(lambda x: label_dict.get(x, x.title()), columns)
  265. @webnotes.whitelist()
  266. def delete_items():
  267. """delete selected items"""
  268. import json
  269. from webnotes.model import delete_doc
  270. from webnotes.model.code import get_obj
  271. il = json.loads(webnotes.form_dict.get('items'))
  272. doctype = webnotes.form_dict.get('doctype')
  273. for d in il:
  274. try:
  275. dt_obj = get_obj(doctype, d)
  276. if hasattr(dt_obj, 'on_trash'):
  277. dt_obj.on_trash()
  278. delete_doc(doctype, d)
  279. except Exception, e:
  280. webnotes.errprint(webnotes.getTraceback())
  281. pass
  282. @webnotes.whitelist()
  283. def get_stats():
  284. """get tag info"""
  285. import json
  286. tags = json.loads(webnotes.form_dict.get('stats'))
  287. doctype = webnotes.form_dict['doctype']
  288. stats = {}
  289. columns = get_table_columns(doctype)
  290. for tag in tags:
  291. if not tag in columns: continue
  292. tagcount = webnotes.conn.sql("""select %(tag)s, count(*)
  293. from `tab%(doctype)s`
  294. where ifnull(%(tag)s, '')!=''
  295. group by %(tag)s;""" % locals(), as_list=1)
  296. if tag=='_user_tags':
  297. stats[tag] = scrub_user_tags(tagcount)
  298. else:
  299. stats[tag] = tagcount
  300. return stats
  301. def scrub_user_tags(tagcount):
  302. """rebuild tag list for tags"""
  303. rdict = {}
  304. tagdict = dict(tagcount)
  305. for t in tagdict:
  306. alltags = t.split(',')
  307. for tag in alltags:
  308. if tag:
  309. if not tag in rdict:
  310. rdict[tag] = 0
  311. rdict[tag] += tagdict[t]
  312. rlist = []
  313. for tag in rdict:
  314. rlist.append([tag, rdict[tag]])
  315. return rlist
  316. def get_table_columns(table):
  317. res = webnotes.conn.sql("DESC `tab%s`" % table, as_dict=1)
  318. if res: return [r['Field'] for r in res]