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.
 
 
 
 
 
 

504 lines
15 KiB

  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]