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.
 
 
 
 
 
 

309 regels
7.7 KiB

  1. """
  2. Transactions are defined as collection of classes, a DocList represents collection of Document
  3. objects for a transaction with main and children.
  4. Group actions like save, etc are performed on doclists
  5. """
  6. import webnotes
  7. from webnotes.utils import cint
  8. class DocList:
  9. """
  10. Collection of Documents with one parent and multiple children
  11. """
  12. def __init__(self, dt=None, dn=None):
  13. self.docs = []
  14. self.obj = None
  15. self.to_docstatus = 0
  16. if dt and dn:
  17. self.load_from_db(dt, dn)
  18. def load_from_db(self, dt, dn, prefix='tab'):
  19. """
  20. Load doclist from dt
  21. """
  22. from webnotes.model.doc import Document, getchildren
  23. doc = Document(dt, dn, prefix=prefix)
  24. # get all children types
  25. tablefields = webnotes.model.meta.get_table_fields(dt)
  26. # load chilren
  27. doclist = [doc,]
  28. for t in tablefields:
  29. doclist += getchildren(doc.name, t[0], t[1], dt, prefix=prefix)
  30. self.docs = doclist
  31. self.doc = doc
  32. self.children = doclist[1:]
  33. def __iter__(self):
  34. """
  35. Make this iterable
  36. """
  37. return self.docs.__iter__()
  38. def from_compressed(self, data, docname):
  39. """
  40. Expand called from client
  41. """
  42. from webnotes.model.utils import expand
  43. self.docs = expand(data)
  44. self.objectify(docname)
  45. def objectify(self, docname=None):
  46. """
  47. Converts self.docs from a list of dicts to list of Documents
  48. """
  49. from webnotes.model.doc import Document
  50. self.docs = [Document(fielddata=d) for d in self.docs]
  51. self.doclist = self.docs
  52. if not docname:
  53. self.doc, self.children = self.docs[0], self.docs[1:]
  54. else:
  55. self.doc = None
  56. self.children = []
  57. for d in self.docs:
  58. if d.name == docname:
  59. self.doc = d
  60. else:
  61. self.children.append(d)
  62. # catch all if no self.doc
  63. if not self.doc:
  64. self.doc, self.children = self.docs[0], self.docs[1:]
  65. def make_obj(self):
  66. """
  67. Create a DocType object
  68. """
  69. if self.obj: return self.obj
  70. from webnotes.model.code import get_obj
  71. self.obj = get_obj(doc=self.doc, doclist=self.children)
  72. return self.obj
  73. def next(self):
  74. """
  75. Next doc
  76. """
  77. return self.docs.next()
  78. def to_dict(self):
  79. """
  80. return as a list of dictionaries
  81. """
  82. return [d.fields for d in self.docs]
  83. def check_if_latest(self):
  84. """
  85. Raises exception if the modified time is not the same as in the database
  86. """
  87. from webnotes.model.meta import is_single
  88. if (not is_single(self.doc.doctype)) and (not cint(self.doc.fields.get('__islocal'))):
  89. tmp = webnotes.conn.sql("""
  90. SELECT modified FROM `tab%s` WHERE name="%s" for update"""
  91. % (self.doc.doctype, self.doc.name))
  92. if tmp and str(tmp[0][0]) != str(self.doc.modified):
  93. webnotes.msgprint("""
  94. Document has been modified after you have opened it.
  95. To maintain the integrity of the data, you will not be able to save your changes.
  96. Please refresh this document. [%s/%s]""" % (tmp[0][0], self.doc.modified), raise_exception=1)
  97. def check_permission(self):
  98. """
  99. Raises exception if permission is not valid
  100. """
  101. if not self.doc.check_perm(verbose=1):
  102. webnotes.msgprint("Not enough permission to save %s" % self.doc.doctype, raise_exception=1)
  103. def check_links(self):
  104. """
  105. Checks integrity of links (throws exception if links are invalid)
  106. """
  107. ref, err_list = {}, []
  108. for d in self.docs:
  109. if not ref.get(d.doctype):
  110. ref[d.doctype] = d.make_link_list()
  111. err_list += d.validate_links(ref[d.doctype])
  112. if err_list:
  113. webnotes.msgprint("""[Link Validation] Could not find the following values: %s.
  114. Please correct and resave. Document Not Saved.""" % ', '.join(err_list), raise_exception=1)
  115. def update_timestamps_and_docstatus(self):
  116. """
  117. Update owner, creation, modified_by, modified, docstatus
  118. """
  119. from webnotes.utils import now
  120. ts = now()
  121. user = webnotes.__dict__.get('session', {}).get('user') or 'Administrator'
  122. for d in self.docs:
  123. if self.doc.__islocal:
  124. d.owner = user
  125. d.creation = ts
  126. d.modified_by = user
  127. d.modified = ts
  128. if d.docstatus != 2: # don't update deleted
  129. d.docstatus = self.to_docstatus
  130. def prepare_for_save(self, check_links):
  131. """
  132. Set owner, modified etc before saving
  133. """
  134. self.check_if_latest()
  135. self.check_permission()
  136. if check_links:
  137. self.check_links()
  138. self.update_timestamps_and_docstatus()
  139. def run_method(self, method):
  140. """
  141. Run a method and custom_method
  142. """
  143. self.make_obj()
  144. if hasattr(self.obj, method):
  145. getattr(self.obj, method)()
  146. if hasattr(self.obj, 'custom_' + method):
  147. getattr(self.obj, 'custom_' + method)()
  148. from webnotes.model.events import trigger
  149. trigger(method, self.doc)
  150. def save_main(self):
  151. """
  152. Save the main doc
  153. """
  154. try:
  155. self.doc.save(cint(self.doc.__islocal))
  156. except NameError, e:
  157. webnotes.msgprint('%s "%s" already exists' % (self.doc.doctype, self.doc.name))
  158. # prompt if cancelled
  159. if webnotes.conn.get_value(self.doc.doctype, self.doc.name, 'docstatus')==2:
  160. webnotes.msgprint('[%s "%s" has been cancelled]' % (self.doc.doctype, self.doc.name))
  161. webnotes.errprint(webnotes.utils.getTraceback())
  162. raise e
  163. def save_children(self):
  164. """
  165. Save Children, with the new parent name
  166. """
  167. for d in self.children:
  168. deleted, local = d.fields.get('__deleted',0), d.fields.get('__islocal',0)
  169. if cint(local) and cint(deleted):
  170. pass
  171. elif d.fields.has_key('parent'):
  172. if d.parent and (not d.parent.startswith('old_parent:')):
  173. d.parent = self.doc.name # rename if reqd
  174. d.parenttype = self.doc.doctype
  175. d.save(new = cint(local))
  176. def save(self, check_links=1):
  177. """
  178. Save the list
  179. """
  180. self.prepare_for_save(check_links)
  181. self.run_method('validate')
  182. self.save_main()
  183. self.save_children()
  184. self.run_method('on_update')
  185. def submit(self):
  186. """
  187. Save & Submit - set docstatus = 1, run "on_submit"
  188. """
  189. if self.doc.docstatus != 0:
  190. msgprint("Only draft can be submitted", raise_exception=1)
  191. self.to_docstatus = 1
  192. self.save()
  193. self.run_method('on_submit')
  194. def cancel(self):
  195. """
  196. Cancel - set docstatus 2, run "on_cancel"
  197. """
  198. if self.doc.docstatus != 1:
  199. msgprint("Only submitted can be cancelled", raise_exception=1)
  200. self.to_docstatus = 2
  201. self.prepare_for_save(1)
  202. self.save_main()
  203. self.save_children()
  204. self.run_method('on_cancel')
  205. def update_after_submit(self):
  206. """
  207. Update after submit - some values changed after submit
  208. """
  209. if self.doc.docstatus != 1:
  210. msgprint("Only to called after submit", raise_exception=1)
  211. self.to_docstatus = 1
  212. self.prepare_for_save(1)
  213. self.save_main()
  214. self.save_children()
  215. self.run_method('on_update_after_submit')
  216. # clone
  217. def clone(source_doclist):
  218. """ Copy previous invoice and change dates"""
  219. from webnotes.model.doc import Document
  220. new_doclist = []
  221. new_parent = Document(fielddata = source_doclist.doc.fields.copy())
  222. new_parent.name = 'Temp/001'
  223. new_parent.fields['__islocal'] = 1
  224. new_parent.fields['docstatus'] = 0
  225. if new_parent.fields.has_key('amended_from'):
  226. new_parent.fields['amended_from'] = None
  227. new_parent.fields['amendment_date'] = None
  228. new_parent.save(1)
  229. new_doclist.append(new_parent)
  230. for d in source_doclist.doclist[1:]:
  231. newd = Document(fielddata = d.fields.copy())
  232. newd.name = None
  233. newd.fields['__islocal'] = 1
  234. newd.fields['docstatus'] = 0
  235. newd.parent = new_parent.name
  236. new_doclist.append(newd)
  237. doclistobj = DocList()
  238. doclistobj.docs = new_doclist
  239. doclistobj.doc = new_doclist[0]
  240. doclistobj.doclist = new_doclist
  241. doclistobj.children = new_doclist[1:]
  242. doclistobj.save()
  243. return doclistobj
  244. # for bc
  245. def getlist(doclist, parentfield):
  246. """
  247. Return child records of a particular type
  248. """
  249. import webnotes.model.utils
  250. return webnotes.model.utils.getlist(doclist, parentfield)
  251. def copy_doclist(doclist, no_copy = []):
  252. """
  253. Make a copy of the doclist
  254. """
  255. import webnotes.model.utils
  256. return webnotes.model.utils.copy_doclist(doclist, no_copy)