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.
 
 
 
 
 
 

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