Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

document.py 15 KiB

11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
11 år sedan
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. from __future__ import unicode_literals
  4. import frappe
  5. from frappe import _, msgprint
  6. from frappe.utils import flt, cint, cstr, now
  7. from frappe.modules import load_doctype_module
  8. from frappe.model.base_document import BaseDocument
  9. from frappe.model.naming import set_new_name
  10. # once_only validation
  11. # methods
  12. def get_doc(arg1, arg2=None):
  13. if isinstance(arg1, BaseDocument):
  14. return arg1
  15. elif isinstance(arg1, basestring):
  16. doctype = arg1
  17. else:
  18. doctype = arg1.get("doctype")
  19. controller = get_controller(doctype)
  20. if controller:
  21. return controller(arg1, arg2)
  22. raise ImportError, arg1
  23. _classes = {}
  24. def get_controller(doctype):
  25. if not doctype in _classes:
  26. module = load_doctype_module(doctype)
  27. classname = doctype.replace(" ", "")
  28. if hasattr(module, classname):
  29. _class = getattr(module, classname)
  30. if issubclass(_class, Document):
  31. _class = getattr(module, classname)
  32. else:
  33. raise ImportError, doctype
  34. else:
  35. raise ImportError, doctype
  36. _classes[doctype] = _class
  37. return _classes[doctype]
  38. class Document(BaseDocument):
  39. def __init__(self, arg1, arg2=None):
  40. self.doctype = self.name = None
  41. if arg1 and isinstance(arg1, basestring):
  42. if not arg2:
  43. # single
  44. self.doctype = self.name = arg1
  45. else:
  46. self.doctype = arg1
  47. if isinstance(arg2, dict):
  48. # filter
  49. self.name = frappe.db.get_value(arg1, arg2, "name")
  50. if self.name is None:
  51. raise frappe.DoesNotExistError, (arg1, arg2)
  52. else:
  53. self.name = arg2
  54. self.load_from_db()
  55. elif isinstance(arg1, dict):
  56. super(Document, self).__init__(arg1)
  57. self.init_valid_columns()
  58. else:
  59. # incorrect arguments. let's not proceed.
  60. raise frappe.DataError("Document({0}, {1})".format(arg1, arg2))
  61. def load_from_db(self):
  62. if not getattr(self, "_metaclass", False) and self.meta.issingle:
  63. self.update(frappe.db.get_singles_dict(self.doctype))
  64. self.init_valid_columns()
  65. self._fix_numeric_types()
  66. else:
  67. d = frappe.db.get_value(self.doctype, self.name, "*", as_dict=1)
  68. if not d:
  69. frappe.throw(("{0} {1} not found").format(_(self.doctype), self.name), frappe.DoesNotExistError)
  70. self.update(d)
  71. if self.name=="DocType" and self.doctype=="DocType":
  72. from frappe.model.meta import doctype_table_fields
  73. table_fields = doctype_table_fields
  74. else:
  75. table_fields = self.meta.get_table_fields()
  76. for df in table_fields:
  77. children = frappe.db.get_values(df.options,
  78. {"parent": self.name, "parenttype": self.doctype, "parentfield": df.fieldname},
  79. "*", as_dict=True, order_by="idx asc")
  80. if children:
  81. self.set(df.fieldname, children)
  82. else:
  83. self.set(df.fieldname, [])
  84. def has_permission(self, permtype):
  85. if getattr(self, "ignore_permissions", False):
  86. return True
  87. return frappe.has_permission(self.doctype, permtype, self)
  88. def insert(self, ignore_permissions=None):
  89. if ignore_permissions!=None:
  90. self.ignore_permissions = ignore_permissions
  91. self.set("__islocal", True)
  92. if not self.has_permission("create"):
  93. raise frappe.PermissionError("No permission to create {} {}".format(self.doctype, self.name))
  94. self._set_defaults()
  95. self._set_docstatus_user_and_timestamp()
  96. self.check_if_latest()
  97. set_new_name(self)
  98. self.run_method("before_insert")
  99. self.run_before_save_methods()
  100. self._validate()
  101. # run validate, on update etc.
  102. # parent
  103. if getattr(self.meta, "issingle", 0):
  104. self.update_single(self.get_valid_dict())
  105. else:
  106. self.db_insert()
  107. # children
  108. for d in self.get_all_children():
  109. d.parent = self.name
  110. d.db_insert()
  111. self.run_method("after_insert")
  112. self.run_post_save_methods()
  113. return self
  114. def save(self, ignore_permissions=None):
  115. if ignore_permissions!=None:
  116. self.ignore_permissions = ignore_permissions
  117. if self.get("__islocal") or not self.get("name"):
  118. self.insert()
  119. return
  120. if not self.has_permission("write"):
  121. raise frappe.PermissionError("No permission to save {} {}".format(self.doctype, self.name))
  122. self._set_docstatus_user_and_timestamp()
  123. self.check_if_latest()
  124. self.run_before_save_methods()
  125. self._validate()
  126. # parent
  127. if self.meta.issingle:
  128. self.update_single(self.get_valid_dict())
  129. else:
  130. self.db_update()
  131. # children
  132. child_map = {}
  133. ignore_children_type = self.get("ignore_children_type") or []
  134. for d in self.get_all_children():
  135. d.parent = self.name # rename if reqd
  136. d.parenttype = self.doctype
  137. d.db_update()
  138. child_map.setdefault(d.doctype, []).append(d.name)
  139. for df in self.meta.get_table_fields():
  140. if df.options not in ignore_children_type:
  141. cnames = child_map.get(df.options) or []
  142. if cnames:
  143. frappe.db.sql("""delete from `tab%s` where parent=%s and parenttype=%s and
  144. name not in (%s)""" % (df.options, '%s', '%s', ','.join(['%s'] * len(cnames))),
  145. tuple([self.name, self.doctype] + cnames))
  146. else:
  147. frappe.db.sql("""delete from `tab%s` where parent=%s and parenttype=%s""" \
  148. % (df.options, '%s', '%s'), (self.name, self.doctype))
  149. self.run_post_save_methods()
  150. return self
  151. def update_single(self, d):
  152. frappe.db.sql("""delete from tabSingles where doctype=%s""", self.doctype)
  153. for field, value in d.iteritems():
  154. if field != "doctype":
  155. frappe.db.sql("""insert into tabSingles(doctype, field, value)
  156. values (%s, %s, %s)""", (self.doctype, field, value))
  157. def _set_docstatus_user_and_timestamp(self):
  158. self._original_modified = self.modified
  159. self.modified = now()
  160. self.modified_by = frappe.session.user
  161. if not self.creation:
  162. self.creation = self.modified
  163. if not self.owner:
  164. self.owner = self.modified_by
  165. if self.docstatus==None:
  166. self.docstatus=0
  167. for d in self.get_all_children():
  168. d.docstatus = self.docstatus
  169. d.modified = self.modified
  170. d.modified_by = self.modified_by
  171. if not d.owner:
  172. d.owner = self.owner
  173. if not d.creation:
  174. d.creation = self.creation
  175. def _validate(self):
  176. self._validate_mandatory()
  177. self._validate_links()
  178. self._validate_constants()
  179. for d in self.get_all_children():
  180. d._validate_constants()
  181. self._extract_images_from_text_editor()
  182. def _set_defaults(self):
  183. if frappe.flags.in_import:
  184. return
  185. new_doc = frappe.new_doc(self.doctype)
  186. self.update_if_missing(new_doc)
  187. # children
  188. for df in self.meta.get_table_fields():
  189. new_doc = frappe.new_doc(df.options)
  190. value = self.get(df.fieldname)
  191. if isinstance(value, list):
  192. for d in value:
  193. d.update_if_missing(new_doc)
  194. def check_if_latest(self):
  195. conflict = False
  196. self._action = "save"
  197. if not self.get('__islocal'):
  198. if self.meta.issingle:
  199. modified = frappe.db.get_value(self.doctype, self.name, "modified")
  200. if cstr(modified) and cstr(modified) != cstr(self._original_modified):
  201. conflict = True
  202. else:
  203. tmp = frappe.db.get_value(self.doctype, self.name,
  204. ["modified", "docstatus"], as_dict=True)
  205. if not tmp:
  206. frappe.throw(_("Record does not exist"))
  207. modified = cstr(tmp.modified)
  208. if modified and modified != cstr(self._original_modified):
  209. conflict = True
  210. self.check_docstatus_transition(tmp.docstatus)
  211. if conflict:
  212. frappe.msgprint(_("Error: Document has been modified after you have opened it") \
  213. + (" (%s, %s). " % (modified, self.modified)) \
  214. + _("Please refresh to get the latest document."),
  215. raise_exception=frappe.TimestampMismatchError)
  216. else:
  217. self.check_docstatus_transition(0)
  218. def check_docstatus_transition(self, docstatus):
  219. if not self.docstatus:
  220. self.docstatus = 0
  221. if docstatus==0:
  222. if self.docstatus==0:
  223. self._action = "save"
  224. elif self.docstatus==1:
  225. self._action = "submit"
  226. if not self.has_permission("submit"):
  227. raise frappe.PermissionError("No permission to submit {} {}".format(self.doctype, self.name))
  228. else:
  229. raise frappe.DocstatusTransitionError("Cannot change docstatus from 0 to 2")
  230. elif docstatus==1:
  231. if self.docstatus==1:
  232. self._action = "update_after_submit"
  233. self.validate_update_after_submit()
  234. if not self.has_permission("submit"):
  235. raise frappe.PermissionError("No permission to submit {} {}".format(self.doctype, self.name))
  236. elif self.docstatus==2:
  237. self._action = "cancel"
  238. if not self.has_permission("cancel"):
  239. raise frappe.PermissionError("No permission to cancel {} {}".format(self.doctype, self.name))
  240. else:
  241. raise frappe.DocstatusTransitionError("Cannot change docstatus from 1 to 0")
  242. elif docstatus==2:
  243. raise frappe.ValidationError
  244. def validate_update_after_submit(self):
  245. if getattr(self, "ignore_validate_update_after_submit", False):
  246. return
  247. self._validate_update_after_submit()
  248. for d in self.get_all_children():
  249. d._validate_update_after_submit()
  250. # TODO check only allowed values are updated
  251. def _validate_mandatory(self):
  252. if self.get("ignore_mandatory"):
  253. return
  254. missing = self._get_missing_mandatory_fields()
  255. for d in self.get_all_children():
  256. missing.extend(d._get_missing_mandatory_fields())
  257. if not missing:
  258. return
  259. for fieldname, msg in missing:
  260. msgprint(msg)
  261. raise frappe.MandatoryError(", ".join((each[0] for each in missing)))
  262. def _validate_links(self):
  263. if self.get("ignore_links"):
  264. return
  265. invalid_links = self.get_invalid_links()
  266. for d in self.get_all_children():
  267. invalid_links.extend(d.get_invalid_links())
  268. if not invalid_links:
  269. return
  270. msg = ", ".join((each[2] for each in invalid_links))
  271. frappe.throw(_("Could not find {0}").format(msg),
  272. frappe.LinkValidationError)
  273. def get_all_children(self, parenttype=None):
  274. ret = []
  275. for df in self.meta.get("fields", {"fieldtype": "Table"}):
  276. if parenttype:
  277. if df.options==parenttype:
  278. return self.get(df.fieldname)
  279. value = self.get(df.fieldname)
  280. if isinstance(value, list):
  281. ret.extend(value)
  282. return ret
  283. def _extract_images_from_text_editor(self):
  284. from frappe.utils.file_manager import extract_images_from_html
  285. if self.doctype != "DocType":
  286. for df in self.meta.get("fields", {"fieldtype":"Text Editor"}):
  287. extract_images_from_html(self, df.fieldname)
  288. def run_method(self, method, *args, **kwargs):
  289. """run standard triggers, plus those in frappe"""
  290. if hasattr(self, method):
  291. fn = lambda self, *args, **kwargs: getattr(self, method)(*args, **kwargs)
  292. fn.__name__ = method.encode("utf-8")
  293. return Document.hook(fn)(self, *args, **kwargs)
  294. def submit(self):
  295. self.docstatus = 1
  296. self.save()
  297. def cancel(self):
  298. self.docstatus = 2
  299. self.save()
  300. def run_before_save_methods(self):
  301. if getattr(self, "ignore_validate", False):
  302. return
  303. if self._action=="save":
  304. self.run_method("validate")
  305. self.run_method("before_save")
  306. elif self._action=="submit":
  307. self.run_method("validate")
  308. self.run_method("before_submit")
  309. elif self._action=="cancel":
  310. self.run_method("before_cancel")
  311. elif self._action=="update_after_submit":
  312. self.run_method("before_update_after_submit")
  313. def run_post_save_methods(self):
  314. if self._action=="save":
  315. self.run_method("on_update")
  316. elif self._action=="submit":
  317. self.run_method("on_update")
  318. self.run_method("on_submit")
  319. elif self._action=="cancel":
  320. self.run_method("on_cancel")
  321. elif self._action=="update_after_submit":
  322. self.run_method("on_update_after_submit")
  323. @staticmethod
  324. def hook(f):
  325. def add_to_return_value(self, new_return_value):
  326. if isinstance(new_return_value, dict):
  327. if not self.get("_return_value"):
  328. self._return_value = {}
  329. self._return_value.update(new_return_value)
  330. else:
  331. self._return_value = new_return_value or self.get("_return_value")
  332. def compose(fn, *hooks):
  333. def runner(self, method, *args, **kwargs):
  334. add_to_return_value(self, fn(self, *args, **kwargs))
  335. for f in hooks:
  336. add_to_return_value(self, f(self, method, *args, **kwargs))
  337. return self._return_value
  338. return runner
  339. def composer(self, *args, **kwargs):
  340. hooks = []
  341. method = f.__name__
  342. for handler in frappe.get_hooks("doc_event:" + self.doctype + ":" + method) \
  343. + frappe.get_hooks("doc_event:*:" + method):
  344. hooks.append(frappe.get_attr(handler))
  345. composed = compose(f, *hooks)
  346. return composed(self, method, *args, **kwargs)
  347. return composer
  348. def validate_value(self, fieldname, condition, val2, doc=None, raise_exception=None):
  349. """check that value of fieldname should be 'condition' val2
  350. else throw exception"""
  351. error_condition_map = {
  352. "in": _("one of"),
  353. "not in": _("none of"),
  354. "^": _("beginning with"),
  355. }
  356. if not doc:
  357. doc = self
  358. df = doc.meta.get_field(fieldname)
  359. val1 = doc.get(fieldname)
  360. if df.fieldtype in ("Currency", "Float"):
  361. val1 = flt(val1, self.precision(df.fieldname, doc.parentfield or None))
  362. val2 = flt(val2, self.precision(df.fieldname, doc.parentfield or None))
  363. elif df.fieldtype in ("Int", "Check"):
  364. val1 = cint(val1)
  365. val2 = cint(val2)
  366. elif df.fieldtype in ("Data", "Text", "Small Text", "Long Text",
  367. "Text Editor", "Select", "Link"):
  368. val1 = cstr(val1)
  369. val2 = cstr(val2)
  370. if not frappe.compare(val1, condition, val2):
  371. label = doc.meta.get_label(fieldname)
  372. condition_str = error_condition_map.get(condition, condition)
  373. if doc.parentfield:
  374. msg = _("Incorrect value in row {0}: {1} must be {2} {3}".format(doc.idx, label, condition_str, val2))
  375. else:
  376. msg = _("Incorrect value: {1} must be {2} {3}".format(label, condition_str, val2))
  377. # raise passed exception or True
  378. msgprint(msg, raise_exception=raise_exception or True)
  379. def validate_table_has_rows(self, parentfield, raise_exception=None):
  380. if not (isinstance(self.get(parentfield), list) and len(self.get(parentfield)) > 0):
  381. label = self.meta.get_label(parentfield)
  382. frappe.throw(_("Table {0} cannot be empty").format(label), raise_exception or frappe.EmptyTableError)
  383. def round_floats_in(self, doc, fieldnames=None):
  384. if not fieldnames:
  385. fieldnames = (df.fieldname for df in
  386. doc.meta.get("fields", {"fieldtype": ["in", ["Currency", "Float"]]}))
  387. for fieldname in fieldnames:
  388. doc.set(fieldname, flt(doc.get(fieldname), self.precision(fieldname, doc.parentfield)))
  389. def precision(self, fieldname, parentfield=None):
  390. from frappe.model.meta import get_field_precision
  391. if parentfield and not isinstance(parentfield, basestring):
  392. parentfield = parentfield.parentfield
  393. if not hasattr(self, "_precision"):
  394. self._precision = frappe._dict({
  395. "default": cint(frappe.db.get_default("float_precision")) or 3,
  396. "options": {}
  397. })
  398. if self._precision.setdefault(parentfield or "main", {}).get(fieldname) is None:
  399. meta = frappe.get_meta(self.meta.get_field(parentfield).options if parentfield else self.doctype)
  400. df = meta.get_field(fieldname)
  401. if df.fieldtype == "Currency" and df.options and not self._precision.options.get(df.options):
  402. self._precision.options[df.options] = get_field_precision(df, self)
  403. if df.fieldtype == "Currency":
  404. self._precision[parentfield or "main"][fieldname] = cint(self._precision.options.get(df.options)) or \
  405. self._precision.default
  406. elif df.fieldtype == "Float":
  407. self._precision[parentfield or "main"][fieldname] = self._precision.default
  408. return self._precision[parentfield or "main"][fieldname]