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.
 
 
 
 
 
 

464 line
18 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. import webnotes
  24. def import_docs(docs = []):
  25. from webnotes.model.doc import Document
  26. import webnotes.model.code
  27. doc_list = {}
  28. created_docs = []
  29. already_exists = []
  30. out, tmp ="", ""
  31. for d in docs:
  32. cur_doc = Document(fielddata = d)
  33. if not cur_doc.parent in already_exists: # parent should not exist
  34. try:
  35. cur_doc.save(1)
  36. out += "Created: " + cur_doc.name + "\n"
  37. created_docs.append(cur_doc)
  38. # make in groups
  39. if cur_doc.parent:
  40. if not doc_list.has_key(cur_doc.parent):
  41. doc_list[cur_doc.parent] = []
  42. doc_list[cur_doc.parent].append(cur_doc)
  43. except Exception, e:
  44. out += "Creation Warning/Error: " + cur_doc.name + " :"+ str(e) + "\n"
  45. already_exists.append(cur_doc.name)
  46. # Run scripts for main docs
  47. for m in created_docs:
  48. if doc_list.has_key(m.name):
  49. tmp = webnotes.model.code.run_server_obj(webnotes.model.code.get_server_obj(m, doc_list.get(m.name, [])),'on_update')
  50. # update database (in case of DocType)
  51. if m.doctype=='DocType':
  52. import webnotes.model.doctype
  53. try: webnotes.model.doctype.update_doctype(doc_list.get(m.name, []))
  54. except: pass
  55. out += 'Executed: '+ str(m.name) + ', Err:' + str(tmp) + "\n"
  56. return out
  57. #======================================================================================================================================
  58. import webnotes
  59. import webnotes.utils
  60. sql = webnotes.conn.sql
  61. flt = webnotes.utils.flt
  62. cint = webnotes.utils.cint
  63. cstr = webnotes.utils.cstr
  64. class CSVImport:
  65. def __init__(self):
  66. self.msg = []
  67. self.csv_data = None
  68. self.import_date_format = None
  69. self.deleted_records = []
  70. def validate_doctype(self, dt_list):
  71. cl, tables, self.dt_list, self.prompt_autoname_flag = 0, [t[0] for t in sql("show tables")], [], 0
  72. self.msg.append('<p><b>Identifying Documents</b></p>')
  73. dtd = sql("select name, istable, autoname from `tabDocType` where name = '%s' " % dt_list[0])
  74. if dtd and dtd[0][0]:
  75. self.msg.append('<div style="color: GREEN">Identified Document: ' + dt_list[0] + '</div>')
  76. self.dt_list.append(dt_list[0])
  77. if dtd[0][2] and 'Prompt' in dtd[0][2]: self.prompt_autoname_flag = 1
  78. if flt(dtd[0][1]):
  79. res1 = sql("select parent, fieldname from tabDocField where options='%s' and fieldtype='Table' and docstatus!=2" % self.dt_list[0])
  80. if res1 and res1[0][0] == dt_list[1]:
  81. self.msg.append('<div style="color: GREEN">Identified Document: ' + dt_list[1] + '</div>')
  82. self.dt_list.append(dt_list[1])
  83. else :
  84. self.msg.append('<div style="color:RED"> Error: At Row 1, Column 2 => %s is not a valid Document </div>' % dt_list[1])
  85. self.validate_success = 0
  86. if res1 and res1[0][1] == dt_list[2]:
  87. self.msg.append('<div style="color: GREEN" >Identified Document Fieldname: ' + dt_list[2] + '</div>')
  88. self.dt_list.append(dt_list[2])
  89. else :
  90. self.msg.append('<div style="color:RED"> Error: At Row 1, Column 3 => %s is not a valid Fieldname </div>' % dt_list[2])
  91. self.validate_success = 0
  92. elif dt_list[1]:
  93. self.msg.append('<div style="color:RED"> Error: At Row 1, Column 1 => %s is not a Table. </div>' % dt_list[0])
  94. self.validate_success = 0
  95. else:
  96. self.msg.append('<div style="color:RED"> Error: At Row 1, Column 1 => %s is not a valid Document </div>' % dt_list[0])
  97. self.validate_success = 0
  98. def validate_fields(self, lb_list):
  99. self.msg.append('<p><b>Checking fieldnames for %s</b></p>' % self.dt_list[0])
  100. if self.overwrite and len(self.dt_list) == 1 and 'Name' != lb_list[0]:
  101. self.msg.append('<div style="color:RED"> Error : At Row 4 and Column 1: To Overwrite fieldname should be Name </div>')
  102. self.validate_success = 0
  103. return
  104. # labelnames
  105. res = self.validate_success and [d[0] for d in sql("select label from tabDocField where parent='%s' and docstatus!=2 and ifnull(hidden,'') in ('',0)" % self.dt_list[0])] or []
  106. if len(self.dt_list) > 1 and self.dt_list[1]:
  107. if self.dt_list[1] not in lb_list:
  108. self.msg.append('<div style="color:RED"> Error : At Row 4: There should be one column named "'+self.dt_list[1]+'"</div>')
  109. self.validate_success = 0
  110. return
  111. if 'Name' in lb_list:
  112. self.msg.append('<div style="color:RED"> Error : At Row 4: "Name" column should not be there</div>')
  113. self.validate_success = 0
  114. return
  115. self.fields.append('parent')
  116. lb_list.pop(lb_list.index(self.dt_list[1]))
  117. dtd = sql("select autoname from `tabDocType` where name = '%s' " % self.dt_list[0])[0][0]
  118. if (self.prompt_autoname_flag or self.overwrite) and len(self.dt_list) == 1:
  119. self.fields.append('name')
  120. res.append('Name')
  121. lb_list.pop(lb_list.index('Name'))
  122. cl = 1
  123. for l in lb_list:
  124. try:
  125. if l:
  126. if not (l in res):
  127. self.msg.append('<div style="color: RED">Error : At Row 4 and Column %s Field %s is not present in %s</div>' % (cl, l, self.dt_list[0]))
  128. self.validate_success = 0
  129. # this condition is for child doctype
  130. else: self.fields.append(sql("select fieldname from tabDocField where parent ='%s' and label = '%s' and ifnull(fieldname,'') !='' " % (self.dt_list[0], l))[0][0] or '')
  131. except Exception, e:
  132. self.msg.append('<div style="color: RED"> At Row 4 and Column %s : =>ERROR: %s </div>' % ( cl, e))
  133. self.validate_success = 0
  134. cl = cl + 1
  135. if not self.overwrite:
  136. # get_reqd_fields
  137. reqd_list = [d[0] for d in sql("select label from `tabDocField` where parent = '%s' and ifnull(reqd,'') not in ('', 0) and docstatus !=2" % self.dt_list[0]) if d[0] not in lb_list] or []
  138. # Check if Reqd field not present in self.fields
  139. if reqd_list:
  140. self.msg.append('<div style="color: RED"> Error : At Row 4 Mandatory Fields %s of Document %s are Required. </div>' %(reqd_list , self.dt_list[0]))
  141. self.validate_success = 0
  142. if self.validate_success:
  143. self.msg.append('<div style="color: GREEN">Fields OK for %s</div>' % self.dt_list[0])
  144. def validate_headers(self):
  145. self.validate_doctype(self.doctype_data)
  146. if self.validate_success:
  147. self.validate_fields(self.labels)
  148. # Date parsing
  149. # --------------------------------------------------------------------
  150. def parse_date(self, r, c, d):
  151. out = ''
  152. try:
  153. if self.import_date_format=='yyyy-mm-dd':
  154. tmpd = d.split('-')
  155. if len(tmpd)==3:
  156. out = tmpd[0] + '-'+tmpd[1] + '-' + tmpd[2]
  157. elif d and self.import_date_format=='dd-mm-yyyy':
  158. tmpd = d.split('-')
  159. if len(tmpd)==3:
  160. out = tmpd[2]+'-'+tmpd[1]+'-'+tmpd[0]
  161. elif d and self.import_date_format=='mm/dd/yyyy':
  162. tmpd = d.split('/')
  163. if len(tmpd)==3:
  164. out = tmpd[2]+'-'+tmpd[0]+'-'+tmpd[1]
  165. elif d and self.import_date_format=='mm/dd/yy':
  166. tmpd = d.split('/')
  167. if len(tmpd)==3:
  168. out = '20'+tmpd[2]+'-'+tmpd[0]+'-'+tmpd[1]
  169. elif d and self.import_date_format=='dd/mm/yyyy':
  170. tmpd = d.split('/')
  171. if len(tmpd)==3:
  172. out = tmpd[2]+'-'+tmpd[1]+'-'+tmpd[0]
  173. elif d and self.import_date_format=='dd/mm/yy':
  174. tmpd = d.split('/')
  175. if len(tmpd)==3:
  176. out = '20'+tmpd[2]+'-'+tmpd[1]+'-'+tmpd[0]
  177. if len(tmpd) != 3:
  178. self.msg.append('<div style="color: RED"> At Row %s and Column %s : => Date Format selected as %s does not match with Date Format in File</div>' % (r, c, str(self.import_date_format)))
  179. self.validate_success = 0
  180. else:
  181. import datetime
  182. dt = out.split('-')
  183. datetime.date(int(dt[0]),int(dt[1]), int(dt[2]))
  184. except Exception, e:
  185. self.msg.append('<div style="color: RED"> At Row %s and Column %s : =>ERROR: %s </div>' % (r, c, e))
  186. self.validate_success = 0
  187. self.msg.append(out)
  188. return out
  189. def check_select_link_data(self, r, c, f, d, s = '', l = ''):
  190. from webnotes.model.doctype import get_field_property
  191. options = ''
  192. try:
  193. if d and f:
  194. dt = get_field_property(self.dt_list[0], f, 'options')
  195. lbl = get_field_property(self.dt_list[0], f, 'label')
  196. if dt:
  197. options = l and dt and [n[0] for n in sql("select name from `tab%s` " % (('link:' in dt and dt[5:]) or dt))] or s and dt.split('\n') or ''
  198. if options and d not in options :
  199. msg = '<div style="color: RED">At Row ' + str(r) + ' and Column ' + str(c)+ ' : => Data "' + str(d) + '" in field ['+ str(lbl) +'] Not Found in '
  200. msg = msg.__add__( s and str( 'Select Options [' +str(dt.replace('\n', ',')) +']' ) or str('Master ' + str('link:' in dt and dt[5:] or dt)))
  201. msg = msg.__add__('</div>\n')
  202. self.msg.append(msg)
  203. self.validate_success = 0
  204. except Exception, e:
  205. self.msg.append('<div style="color: RED"> ERROR: %s </div>' % (str(webnotes.utils.getTraceback())))
  206. self.validate_success = 0
  207. return d
  208. def get_field_type_list(self):
  209. # get_date_fields
  210. date_list = [d[0] for d in sql("select fieldname from `tabDocField` where parent = '%s' and fieldtype='Date' and docstatus !=2" % self.dt_list[0])]
  211. # get_link_fields
  212. link_list = [d[0] for d in sql("select fieldname from `tabDocField` where parent = '%s' and ((fieldtype='Link' and ifnull(options,'') != '') or (fieldtype='Select' and ifnull(options,'') like '%%link:%%')) and docstatus !=2 " % self.dt_list[0])]
  213. # get_select_fileds
  214. select_list = [d[0] for d in sql("select fieldname from `tabDocField` where parent = '%s' and fieldtype='Select' and ifnull(options,'') not like '%%link:%%' and docstatus !=2" % self.dt_list[0])]
  215. # get_reqd_fields
  216. reqd_list = self.overwrite and ['name'] or [d[0] for d in sql("select fieldname from `tabDocField` where parent = '%s' and ifnull(reqd,'') not in ('', 0) and docstatus !=2" % self.dt_list[0])]
  217. if len(self.dt_list)> 1 and 'parent' not in reqd_list: reqd_list.append('parent')
  218. if self.prompt_autoname_flag and 'name' not in reqd_list: reqd_list.append('name')
  219. return date_list, link_list, select_list, reqd_list
  220. def validate_data(self):
  221. self.msg.append('<p><b>Checking Data for %s</b></p>' % self.dt_list[0])
  222. date_list, link_list, select_list, reqd_list = self.get_field_type_list()
  223. # load data
  224. row = 5
  225. for d in self.data:
  226. self.validate_success, fd, col = 1, {}, 1
  227. self.msg.append('<p><b>Checking Row %s </b></p>' % (row))
  228. for i in range(len(d)):
  229. if i < len(self.fields):
  230. f = self.fields[i]
  231. try:
  232. # Check Reqd Fields
  233. if (f in reqd_list) and not d[i]:
  234. self.msg.append('<div style="color: RED">Error: At Row %s and Column %s, Field %s is Mandatory.</div>' % (row, col, f))
  235. self.validate_success = 0
  236. # Check Date Fields
  237. if d[i] and f and f in date_list : fd[f] = self.parse_date(row, col, d[i])
  238. # Check Link Fields
  239. elif d[i] and f in link_list:
  240. fd[f] = self.check_select_link_data(row, col, f, d[i], l='Link')
  241. # Check Select Fields
  242. elif d[i] and f in select_list:
  243. fd[f] = self.check_select_link_data(row, col, f, d[i], s= 'Select')
  244. # Need To Perform Check For Other Data Type Too
  245. else: fd[f] = d[i]
  246. except Exception:
  247. self.msg.append('<div style="color: RED"> ERROR: %sData:%s and %s and %s and %s</div>' % (str(webnotes.utils.getTraceback()) + '\n', str(d), str(f), str(date_list), str(link_list)))
  248. self.validate_success = 0
  249. elif d[i]:
  250. self.validate_success = 0
  251. self.msg.append('<div style="color: RED">At Row %s and Column %s</div>' % (row,col))
  252. self.msg.append('<div style="color: ORANGE">Ignored</div>')
  253. col = col + 1
  254. if self.validate_success:
  255. self.msg.append('<div style="color: GREEN">At Row %s and Column %s, Data Verification Completed </div>' % (row,col))
  256. self.update_data(fd,row)
  257. row = row + 1
  258. def update_data(self, fd, row):
  259. # load metadata
  260. from webnotes.model.doc import Document
  261. cur_doc = Document(fielddata = fd)
  262. cur_doc.doctype, cur_doc.parenttype, cur_doc.parentfield = self.dt_list[0], len(self.dt_list) > 1 and self.dt_list[1] or '', len(self.dt_list) > 1 and self.dt_list[2] or ''
  263. obj = ''
  264. webnotes.message_log = []
  265. # save the document
  266. try:
  267. # Delete data of child tables before over-writing
  268. if len(self.dt_list) > 1 and self.overwrite and cur_doc.parent and cur_doc.parent not in self.deleted_records:
  269. webnotes.conn.sql("delete from `tab%s` where parent = '%s'" % (self.dt_list[0], cur_doc.parent))
  270. self.deleted_records.append(cur_doc.parent)
  271. self.msg.append('<div style="color: ORANGE">Deleted %s data of %s : %s before re-importing</div>' % (self.dt_list[0], self.dt_list[1], cur_doc.parent))
  272. if webnotes.conn.in_transaction:
  273. sql("COMMIT")
  274. sql("START TRANSACTION")
  275. if cur_doc.name and webnotes.conn.exists(self.dt_list[0], cur_doc.name):
  276. if self.overwrite:
  277. cur_doc.save()
  278. obj = webnotes.model.code.get_obj(cur_doc.parent and cur_doc.parent_type or cur_doc.doctype, cur_doc.parent or cur_doc.name, with_children = 1)
  279. self.msg.append('<div style="color: ORANGE">Row %s => Over-written: %s</div>' % (row, cur_doc.name))
  280. else:
  281. self.msg.append('<div style="color: ORANGE">Row %s => Ignored: %s</div>' % (row, cur_doc.name))
  282. elif cur_doc.parent and webnotes.conn.exists(cur_doc.parenttype, cur_doc.parent) or not cur_doc.parent:
  283. cur_doc.save(1)
  284. obj = webnotes.model.code.get_obj(cur_doc.parent and cur_doc.parenttype or cur_doc.doctype, cur_doc.parent or cur_doc.name, with_children = 1)
  285. self.msg.append('<div style="color: GREEN">Row %s => Created: %s</div>' % (row, cur_doc.name))
  286. else:
  287. self.msg.append('<div style="color: RED">Row %s => Invalid %s : %s</div>' % (row, cur_doc.parenttype, cur_doc.parent))
  288. except Exception:
  289. self.msg.append('<div style="color: RED"> Validation: %s</div>' % str(webnotes.utils.getTraceback()))
  290. try:
  291. if obj:
  292. if hasattr(obj, 'validate') : obj.validate()
  293. if hasattr(obj, 'on_update') : obj.on_update()
  294. if hasattr(obj, 'on_submit') : obj.on_submit()
  295. sql("COMMIT")
  296. except Exception:
  297. sql("ROLLBACK")
  298. self.msg.append('<div style="color: RED"> Validation Error: %s</div>' % str((webnotes.message_log and webnotes.message_log[0]) or webnotes.utils.getTraceback()))
  299. self.msg.append('<div style="color: RED"> Did not import</div>')
  300. # do import
  301. # --------------------------------------------------------------------
  302. def import_csv(self, csv_data, import_date_format = 'yyyy-mm-dd', overwrite = 0):
  303. import csv
  304. self.validate_success = 1
  305. self.csv_data = self.convert_csv_data_into_list(csv.reader(csv_data.splitlines()))
  306. self.import_date_format, self.overwrite = import_date_format, overwrite
  307. if len(self.csv_data) > 4:
  308. self.doctype_data, self.labels, self.data = self.csv_data[0][:4], self.csv_data[3], self.csv_data[4:]
  309. self.fields = []
  310. import webnotes.model.code
  311. from webnotes.model.doc import Document
  312. sql = webnotes.conn.sql
  313. self.validate_headers()
  314. if self.validate_success:
  315. self.validate_data()
  316. else:
  317. self.msg.append('<p><b>No data entered in file.</b></p>')
  318. try:
  319. out_utf8 = '\n'.join([m.encode('utf-8') for m in self.msg])
  320. except UnicodeEncodeError, e:
  321. out_utf8 = """<div>We are unable to detect the encoding of the
  322. given .csv file. Please save the .csv file with UTF-8
  323. encoding. (See Data Import Guide -- Do you have Non-English data?)</div>"""
  324. return out_utf8
  325. def convert_csv_data_into_list(self,csv_data):
  326. st_list = []
  327. for s in csv_data:
  328. st_list.append([d.strip() for d in s])
  329. return st_list
  330. # Get Template method
  331. # -----------------------------------------------------------------
  332. def get_template():
  333. import webnotes.model
  334. from webnotes.utils import getCSVelement
  335. form = webnotes.form
  336. sql = webnotes.conn.sql
  337. # get form values
  338. dt = form.getvalue('dt')
  339. overwrite = cint(form.getvalue('overwrite')) or 0
  340. pt, pf = '', ''
  341. tmp_lbl, tmp_ml = [],[]
  342. # is table?
  343. dtd = sql("select istable, autoname from tabDocType where name='%s'" % dt)
  344. if dtd and dtd[0][0]:
  345. res1 = sql("select parent, fieldname from tabDocField where options='%s' and fieldtype='Table' and docstatus!=2" % dt)
  346. if res1:
  347. pt, pf = res1[0][0], res1[0][1]
  348. # line 1
  349. dset = []
  350. if pt and pf:
  351. lbl, ml = [pt], ['[Mandatory]']
  352. line1 = '%s,%s,%s' % (getCSVelement(dt), getCSVelement(pt), getCSVelement(pf))
  353. line2 = ',,,,,,Please fill valid %(p)s No in %(p)s column.' % {'p':getCSVelement(pt)}
  354. else:
  355. if dtd[0][1]=='Prompt' or overwrite:
  356. lbl, ml= ['Name'], ['[Mandatory][Special Characters are not allowed]']
  357. else:
  358. lbl, ml= [], []
  359. line1 = '%s' % getCSVelement(dt)
  360. line2 = (overwrite and ',,,,,,Please fill valid %(d)s No in %(n)s' % {'d':dt,'n': 'Name'}) or ',,'
  361. # Help on Line
  362. line1 = line1 + ',,,Please fill columns which are Mandatory., Please do not modify the structure'
  363. # standard fields
  364. res = sql("select fieldname, fieldtype, label, reqd, hidden from tabDocField where parent='%s' and docstatus!=2" % dt)
  365. for r in res:
  366. # restrict trash_reason field, hidden and required fields
  367. if not r[1] in webnotes.model.no_value_fields and r[0] != 'trash_reason' and not r[4] and not r[3]:
  368. tmp_lbl.append(getCSVelement(r[2]))
  369. tmp_ml.append('')
  370. # restrict trash_reason field and hidden fields and add Mandatory indicator for required fields
  371. elif not r[1] in webnotes.model.no_value_fields and r[0] != 'trash_reason' and not r[4] and r[3]:
  372. lbl.append(getCSVelement(r[2]))
  373. ml.append(getCSVelement('[Mandatory]'))
  374. dset.append(line1)
  375. dset.append(line2)
  376. dset.append(','.join(ml + tmp_ml))
  377. dset.append(','.join(lbl + tmp_lbl))
  378. txt = '\n'.join(dset)
  379. webnotes.response['result'] = txt
  380. webnotes.response['type'] = 'csv'
  381. webnotes.response['doctype'] = dt