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.
 
 
 
 
 
 

322 lines
9.7 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. """
  23. Merges (syncs) incoming doclist into the database
  24. Called when:
  25. importing .txt files
  26. importing bulk records from .csv files
  27. For regular types, deletes the record and recreates it
  28. for special types: `DocType`, `Module Def`, `DocType Mapper` there are subclasses
  29. To use::
  30. set_doc(doclist, ovr=1, ingore=1, noupdate=1)
  31. """
  32. import webnotes
  33. from webnotes.model.doc import Document
  34. # this variable is a flag that transfer process is on, to the on_update
  35. # method so that if there are other processes on import, it can do so
  36. in_transfer = 0
  37. def set_doc(doclist, ovr=0, ignore=1, onupdate=1):
  38. """
  39. Wrapper function to sync a record
  40. """
  41. global in_transfer
  42. dt = doclist[0]['doctype']
  43. if webnotes.conn.exists(doclist[0]['doctype'], doclist[0]['name']):
  44. # exists, merge if possible
  45. if dt=='DocType':
  46. ud = UpdateDocType(doclist)
  47. elif dt == 'DocType Mapper':
  48. ud = UpdateDocTypeMapper(doclist)
  49. else:
  50. ud = UpdateDocument(doclist)
  51. else:
  52. ud = UpdateDocument(doclist)
  53. in_transfer = 1
  54. ud.sync()
  55. in_transfer = 0
  56. return '\n'.join(ud.log)
  57. #
  58. # Class to sync incoming document
  59. #
  60. class UpdateDocument:
  61. def __init__(self, in_doclist=[]):
  62. self.in_doclist = in_doclist
  63. self.doc = Document(fielddata = in_doclist[0])
  64. self.modified = self.doc.modified # make a copy
  65. self.doclist = []
  66. self.log = []
  67. self.exists = 0
  68. # sync
  69. def sync(self):
  70. is_mod = self.is_modified()
  71. if (not self.exists) or (is_mod):
  72. webnotes.conn.begin()
  73. if self.exists:
  74. self.delete_existing()
  75. self.save()
  76. self.update_modified()
  77. self.run_on_update()
  78. webnotes.conn.commit()
  79. # check modified
  80. def is_modified(self):
  81. try:
  82. timestamp = webnotes.conn.sql("select modified from `tab%s` where name=%s" % (self.doc.doctype, '%s'), self.doc.name)
  83. except Exception ,e:
  84. if(e.args[0]==1146):
  85. return
  86. else:
  87. raise e
  88. if timestamp:
  89. self.exists = 1
  90. if str(timestamp[0][0]) == self.doc.modified:
  91. self.log.append('%s %s, No change' % (self.doc.doctype, self.doc.name))
  92. else: return 1
  93. # delete existing
  94. def delete_existing(self):
  95. from webnotes.model import delete_doc
  96. delete_doc(self.doc.doctype, self.doc.name, force=1)
  97. # update modified timestamp
  98. def update_modified(self):
  99. webnotes.conn.set(self.doc, 'modified', self.modified)
  100. def save(self):
  101. # parent
  102. self.doc.save(new = 1, ignore_fields = 1, check_links=0)
  103. self.doclist = [self.doc]
  104. self.save_children()
  105. def save_children(self):
  106. for df in self.in_doclist[1:]:
  107. self.save_one_doc(df)
  108. def save_one_doc(self, df, as_new=1):
  109. d = Document(fielddata = df)
  110. d.save(new = as_new, ignore_fields = 1, check_links=0)
  111. self.doclist.append(d)
  112. def run_on_update(self):
  113. from webnotes.model.code import get_server_obj
  114. so = get_server_obj(self.doc, self.doclist)
  115. if hasattr(so, 'on_update'):
  116. so.on_update()
  117. class UpdateDocumentMerge(UpdateDocument):
  118. def __init__(self, in_doclist):
  119. self.to_update_doctype = []
  120. UpdateDocument.__init__(self, in_doclist)
  121. def delete_existing(self):
  122. pass
  123. def get_id(self, d):
  124. pass
  125. def to_update(self, d):
  126. return 1
  127. def child_exists(self, d):
  128. return self.get_id(d)
  129. def on_save(self):
  130. pass
  131. def save(self):
  132. if self.exists:
  133. # save main doc
  134. self.keep_values(self.doc)
  135. self.doc.save(ignore_fields = 1, check_links=0)
  136. self.doclist.append(self.doc)
  137. self.save_children()
  138. self.on_save()
  139. self.log.append('Updated %s' % self.doc.name)
  140. else:
  141. UpdateDocument.save(self)
  142. def save_children(self):
  143. for df in self.in_doclist[1:]:
  144. d = Document(fielddata = df)
  145. # update doctype?
  146. if d.doctype in self.to_update_doctype:
  147. # update this record?
  148. if self.to_update(d):
  149. # is it new?
  150. if self.child_exists(d):
  151. self.keep_values(d)
  152. d.save(ignore_fields = 1, check_links=0)
  153. self.log.append('updated %s, %s' % (d.doctype, d.name))
  154. else:
  155. d.save(1, ignore_fields = 1, check_links=0)
  156. self.log.append('new %s' % d.doctype)
  157. self.doclist.append(d)
  158. def keep_values(self, d):
  159. if hasattr(self, 'get_orignal_values'):
  160. ov = self.get_orignal_values(d)
  161. if ov:
  162. d.fields.update(ov)
  163. class UpdateDocType(UpdateDocumentMerge):
  164. """
  165. Import a doctype from txt to database
  166. """
  167. def __init__(self, in_doclist):
  168. UpdateDocumentMerge.__init__(self, in_doclist)
  169. self.to_update_doctype = ['DocType', 'DocField']
  170. def to_update(self, d):
  171. if (d.fieldtype not in ['Section Break', 'Column Break', 'HTML']) and (d.fieldname or d.label):
  172. return 1
  173. def get_id(self, d):
  174. key = d.fieldname and 'fieldname' or 'label'
  175. if d.fields.get(key):
  176. return webnotes.conn.sql("""select name, options, permlevel, reqd, print_hide, hidden, fieldtype
  177. from tabDocField where %s=%s and parent=%s""" % (key, '%s', '%s'), (d.fields[key], d.parent))
  178. def on_save(self):
  179. self.renum()
  180. def child_exists(self, d):
  181. if d.doctype=='DocField':
  182. return self.get_id(d)
  183. def get_orignal_values(self, d):
  184. if d.doctype=='DocField':
  185. t = self.get_id(d)[0]
  186. return {'name': t[0], 'options': t[1], 'fieldtype':t[6]}
  187. if d.doctype=='DocType':
  188. return webnotes.conn.sql("select server_code, client_script from `tabDocType` where name=%s", d.name, as_dict = 1)[0]
  189. # renumber the indexes
  190. def renum(self):
  191. extra = self.get_extra_fields()
  192. self.clear_section_breaks()
  193. self.add_section_breaks_and_renum()
  194. self.fix_extra_fields(extra)
  195. # get fields not in the incoming list (to preserve order)
  196. def get_extra_fields(self):
  197. prev_field, prev_field_key, extra = '', '', []
  198. # get new fields and labels
  199. fieldnames = [d.get('fieldname') for d in self.in_doclist]
  200. labels = [d.get('label') for d in self.in_doclist]
  201. # check if all existing are present
  202. for f in webnotes.conn.sql("select fieldname, label, idx from tabDocField where parent=%s and fieldtype not in ('Section Break', 'Column Break', 'HTML') order by idx asc", self.doc.name):
  203. if f[0] and not f[0] in fieldnames:
  204. extra.append([f[0], f[1], prev_field, prev_field_key])
  205. elif f[1] and not f[1] in labels:
  206. extra.append([f[0], f[1], prev_field, prev_field_key])
  207. prev_field, prev_field_key = f[0] or f[1], f[0] and 'fieldname' or 'label'
  208. return extra
  209. # clear section breaks
  210. def clear_section_breaks(self):
  211. webnotes.conn.sql("delete from tabDocField where fieldtype in ('Section Break', 'Column Break', 'HTML') and parent=%s and ifnull(options,'')!='Custom'", self.doc.name)
  212. # add section breaks
  213. def add_section_breaks_and_renum(self):
  214. for d in self.in_doclist:
  215. if d.get('parentfield')=='fields':
  216. if d.get('fieldtype') in ('Section Break', 'Column Break', 'HTML'):
  217. tmp = Document(fielddata = d)
  218. tmp.fieldname = ''
  219. tmp.name = None
  220. tmp.save(1, ignore_fields = 1, check_links=0)
  221. else:
  222. webnotes.conn.sql("update tabDocField set idx=%s where %s=%s and parent=%s" % \
  223. ('%s', d.get('fieldname') and 'fieldname' or 'label', '%s', '%s'), (d.get('idx'), d.get('fieldname') or d.get('label'), self.doc.name))
  224. # adjust the extra fields
  225. def fix_extra_fields(self, extra):
  226. # push fields down at new idx
  227. for e in extra:
  228. # get idx of the prev to extra field
  229. idx = 0
  230. if e[2]:
  231. idx = webnotes.conn.sql("select idx from tabDocField where %s=%s and parent=%s" % (e[3], '%s', '%s'), (e[2], self.doc.name))
  232. idx = idx and idx[0][0] or 0
  233. if idx:
  234. webnotes.conn.sql("update tabDocField set idx=idx+1 where idx>%s and parent=%s", (idx, self.doc.name))
  235. webnotes.conn.sql("update tabDocField set idx=%s where %s=%s and parent=%s" % \
  236. ('%s', e[0] and 'fieldname' or 'label', '%s', '%s'), (idx+1, e[0] or e[1], self.doc.name))
  237. def run_on_update(self):
  238. from webnotes.model.code import get_server_obj
  239. so = get_server_obj(self.doc, self.doclist)
  240. if hasattr(so, 'on_update'):
  241. so.on_update()
  242. class UpdateDocTypeMapper(UpdateDocumentMerge):
  243. """
  244. Merge `DocType Mapper`
  245. """
  246. def __init__(self, in_doclist):
  247. UpdateDocumentMerge.__init__(self, in_doclist)
  248. self.to_update_doctype = ['Field Mapper Detail', 'Table Mapper Detail']
  249. def get_id(self, d):
  250. if d.doctype=='Field Mapper Detail':
  251. return webnotes.conn.sql("select name from `tabField Mapper Detail` where from_field=%s and to_field=%s and match_id=%s and parent=%s", (d.from_field, d.to_field, d.match_id, d.parent))
  252. elif d.doctype=='Table Mapper Detail':
  253. return webnotes.conn.sql("select name from `tabTable Mapper Detail` where from_table=%s and to_table = %s and match_id=%s and validation_logic=%s and parent=%s", (d.from_table, d.to_table, d.match_id, d.validation_logic, d.parent))
  254. def get_orignal_values(self, d):
  255. if d.doctype in ['Field Mapper Detail', 'Table Mapper Detail']:
  256. return {'name': self.get_id(d)[0][0]}