25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1052 lines
32 KiB

  1. # Copyright (c) 2015, Frappe 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, cstr, now, get_datetime_str
  7. from frappe.utils.background_jobs import enqueue
  8. from frappe.model.base_document import BaseDocument, get_controller
  9. from frappe.model.naming import set_new_name
  10. from werkzeug.exceptions import NotFound, Forbidden
  11. import hashlib, json
  12. from frappe.model import optional_fields
  13. # once_only validation
  14. # methods
  15. def get_doc(arg1, arg2=None):
  16. """returns a frappe.model.Document object.
  17. :param arg1: Document dict or DocType name.
  18. :param arg2: [optional] document name.
  19. There are two ways to call `get_doc`
  20. # will fetch the latest user object (with child table) from the database
  21. user = get_doc("User", "test@example.com")
  22. # create a new object
  23. user = get_doc({
  24. "doctype":"User"
  25. "email_id": "test@example.com",
  26. "user_roles: [
  27. {"role": "System Manager"}
  28. ]
  29. })
  30. """
  31. if isinstance(arg1, BaseDocument):
  32. return arg1
  33. elif isinstance(arg1, basestring):
  34. doctype = arg1
  35. else:
  36. doctype = arg1.get("doctype")
  37. controller = get_controller(doctype)
  38. if controller:
  39. return controller(arg1, arg2)
  40. raise ImportError, arg1
  41. class Document(BaseDocument):
  42. """All controllers inherit from `Document`."""
  43. def __init__(self, arg1, arg2=None):
  44. """Constructor.
  45. :param arg1: DocType name as string or document **dict**
  46. :param arg2: Document name, if `arg1` is DocType name.
  47. If DocType name and document name are passed, the object will load
  48. all values (including child documents) from the database.
  49. """
  50. self.doctype = self.name = None
  51. self._default_new_docs = {}
  52. self.flags = frappe._dict()
  53. if arg1 and isinstance(arg1, basestring):
  54. if not arg2:
  55. # single
  56. self.doctype = self.name = arg1
  57. else:
  58. self.doctype = arg1
  59. if isinstance(arg2, dict):
  60. # filter
  61. self.name = frappe.db.get_value(arg1, arg2, "name")
  62. if self.name is None:
  63. frappe.throw(_("{0} {1} not found").format(_(arg1), arg2), frappe.DoesNotExistError)
  64. else:
  65. self.name = arg2
  66. self.load_from_db()
  67. elif isinstance(arg1, dict):
  68. super(Document, self).__init__(arg1)
  69. self.init_valid_columns()
  70. else:
  71. # incorrect arguments. let's not proceed.
  72. raise frappe.DataError("Document({0}, {1})".format(arg1, arg2))
  73. def reload(self):
  74. """Reload document from database"""
  75. self.load_from_db()
  76. def load_from_db(self):
  77. """Load document and children from database and create properties
  78. from fields"""
  79. if not getattr(self, "_metaclass", False) and self.meta.issingle:
  80. single_doc = frappe.db.get_singles_dict(self.doctype)
  81. if not single_doc:
  82. single_doc = frappe.new_doc(self.doctype).as_dict()
  83. single_doc["name"] = self.doctype
  84. del single_doc["__islocal"]
  85. super(Document, self).__init__(single_doc)
  86. self.init_valid_columns()
  87. self._fix_numeric_types()
  88. else:
  89. d = frappe.db.get_value(self.doctype, self.name, "*", as_dict=1)
  90. if not d:
  91. frappe.throw(_("{0} {1} not found").format(_(self.doctype), self.name), frappe.DoesNotExistError)
  92. super(Document, self).__init__(d)
  93. if self.name=="DocType" and self.doctype=="DocType":
  94. from frappe.model.meta import doctype_table_fields
  95. table_fields = doctype_table_fields
  96. else:
  97. table_fields = self.meta.get_table_fields()
  98. for df in table_fields:
  99. children = frappe.db.get_values(df.options,
  100. {"parent": self.name, "parenttype": self.doctype, "parentfield": df.fieldname},
  101. "*", as_dict=True, order_by="idx asc")
  102. if children:
  103. self.set(df.fieldname, children)
  104. else:
  105. self.set(df.fieldname, [])
  106. # sometimes __setup__ can depend on child values, hence calling again at the end
  107. if hasattr(self, "__setup__"):
  108. self.__setup__()
  109. def get_latest(self):
  110. if not getattr(self, "latest", None):
  111. self.latest = frappe.get_doc(self.doctype, self.name)
  112. return self.latest
  113. def check_permission(self, permtype='read', permlabel=None):
  114. """Raise `frappe.PermissionError` if not permitted"""
  115. if not self.has_permission(permtype):
  116. self.raise_no_permission_to(permlabel or permtype)
  117. def has_permission(self, permtype="read", verbose=False):
  118. """Call `frappe.has_permission` if `self.flags.ignore_permissions`
  119. is not set.
  120. :param permtype: one of `read`, `write`, `submit`, `cancel`, `delete`"""
  121. if self.flags.ignore_permissions:
  122. return True
  123. return frappe.has_permission(self.doctype, permtype, self, verbose=verbose)
  124. def raise_no_permission_to(self, perm_type):
  125. """Raise `frappe.PermissionError`."""
  126. msg = _("No permission to {0} {1} {2}".format(perm_type, self.doctype, self.name or ""))
  127. frappe.msgprint(msg)
  128. raise frappe.PermissionError(msg)
  129. def lock(self):
  130. '''Will set docstatus to 3 + the current docstatus and mark it as queued
  131. 3 = queued for saving
  132. 4 = queued for submission
  133. 5 = queued for cancellation
  134. '''
  135. self.db_set('docstatus', 3 + self.docstatus, update_modified = False)
  136. def unlock(self):
  137. '''set the original docstatus at the time it was locked in the controller'''
  138. current_docstatus = self.db_get('docstatus') - 4
  139. if current_docstatus < 0:
  140. current_docstatus = 0
  141. self.db_set('docstatus', current_docstatus, update_modified = False)
  142. def insert(self, ignore_permissions=None):
  143. """Insert the document in the database (as a new document).
  144. This will check for user permissions and execute `before_insert`,
  145. `validate`, `on_update`, `after_insert` methods if they are written.
  146. :param ignore_permissions: Do not check permissions if True."""
  147. if self.flags.in_print:
  148. return
  149. self.flags.email_alerts_executed = []
  150. if ignore_permissions!=None:
  151. self.flags.ignore_permissions = ignore_permissions
  152. self.set("__islocal", True)
  153. self.check_permission("create")
  154. self._set_defaults()
  155. self.set_user_and_timestamp()
  156. self.set_docstatus()
  157. self.check_if_latest()
  158. self.run_method("before_insert")
  159. self.set_new_name()
  160. self.set_parent_in_children()
  161. self.validate_higher_perm_levels()
  162. self.flags.in_insert = True
  163. self.run_before_save_methods()
  164. self._validate()
  165. self.set_docstatus()
  166. self.flags.in_insert = False
  167. # run validate, on update etc.
  168. # parent
  169. if getattr(self.meta, "issingle", 0):
  170. self.update_single(self.get_valid_dict())
  171. else:
  172. self.db_insert()
  173. # children
  174. for d in self.get_all_children():
  175. d.db_insert()
  176. self.run_method("after_insert")
  177. self.flags.in_insert = True
  178. self.run_post_save_methods()
  179. self.flags.in_insert = False
  180. # delete __islocal
  181. if hasattr(self, "__islocal"):
  182. delattr(self, "__islocal")
  183. return self
  184. def save(self, *args, **kwargs):
  185. """Wrapper for _save"""
  186. return self._save(*args, **kwargs)
  187. def _save(self, ignore_permissions=None):
  188. """Save the current document in the database in the **DocType**'s table or
  189. `tabSingles` (for single types).
  190. This will check for user permissions and execute
  191. `validate` before updating, `on_update` after updating triggers.
  192. :param ignore_permissions: Do not check permissions if True."""
  193. if self.flags.in_print:
  194. return
  195. self.flags.email_alerts_executed = []
  196. if ignore_permissions!=None:
  197. self.flags.ignore_permissions = ignore_permissions
  198. if self.get("__islocal") or not self.get("name"):
  199. self.insert()
  200. return
  201. self.check_permission("write", "save")
  202. self.set_user_and_timestamp()
  203. self.set_docstatus()
  204. self.check_if_latest()
  205. self.set_parent_in_children()
  206. self.validate_higher_perm_levels()
  207. self.run_before_save_methods()
  208. if self._action != "cancel":
  209. self._validate()
  210. if self._action == "update_after_submit":
  211. self.validate_update_after_submit()
  212. self.set_docstatus()
  213. # parent
  214. if self.meta.issingle:
  215. self.update_single(self.get_valid_dict())
  216. else:
  217. self.db_update()
  218. self.update_children()
  219. self.run_post_save_methods()
  220. return self
  221. def update_children(self):
  222. '''update child tables'''
  223. for df in self.meta.get_table_fields():
  224. self.update_child_table(df.fieldname, df)
  225. def update_child_table(self, fieldname, df=None):
  226. '''sync child table for given fieldname'''
  227. rows = []
  228. if not df:
  229. df = self.meta.get_field(fieldname)
  230. for d in self.get(df.fieldname):
  231. d.db_update()
  232. rows.append(d.name)
  233. if df.options in (self.flags.ignore_children_type or []):
  234. # do not delete rows for this because of flags
  235. # hack for docperm :(
  236. return
  237. if rows:
  238. # delete rows that do not match the ones in the
  239. # document
  240. frappe.db.sql("""delete from `tab{0}` where parent=%s
  241. and parenttype=%s and parentfield=%s
  242. and name not in ({1})""".format(df.options, ','.join(['%s'] * len(rows))),
  243. [self.name, self.doctype, fieldname] + rows)
  244. else:
  245. # no rows found, delete all rows
  246. frappe.db.sql("""delete from `tab{0}` where parent=%s
  247. and parenttype=%s and parentfield=%s""".format(df.options),
  248. (self.name, self.doctype, fieldname))
  249. def set_new_name(self):
  250. """Calls `frappe.naming.se_new_name` for parent and child docs."""
  251. set_new_name(self)
  252. # set name for children
  253. for d in self.get_all_children():
  254. set_new_name(d)
  255. def set_title_field(self):
  256. """Set title field based on template"""
  257. def get_values():
  258. values = self.as_dict()
  259. # format values
  260. for key, value in values.iteritems():
  261. if value==None:
  262. values[key] = ""
  263. return values
  264. if self.meta.get("title_field")=="title":
  265. df = self.meta.get_field(self.meta.title_field)
  266. if df.options:
  267. self.set(df.fieldname, df.options.format(**get_values()))
  268. elif self.is_new() and not self.get(df.fieldname) and df.default:
  269. # set default title for new transactions (if default)
  270. self.set(df.fieldname, df.default.format(**get_values()))
  271. def update_single(self, d):
  272. """Updates values for Single type Document in `tabSingles`."""
  273. frappe.db.sql("""delete from tabSingles where doctype=%s""", self.doctype)
  274. for field, value in d.iteritems():
  275. if field != "doctype":
  276. frappe.db.sql("""insert into tabSingles(doctype, field, value)
  277. values (%s, %s, %s)""", (self.doctype, field, value))
  278. if self.doctype in frappe.db.value_cache:
  279. del frappe.db.value_cache[self.doctype]
  280. def set_user_and_timestamp(self):
  281. self._original_modified = self.modified
  282. self.modified = now()
  283. self.modified_by = frappe.session.user
  284. if not self.creation:
  285. self.creation = self.modified
  286. if not self.owner:
  287. self.owner = self.modified_by
  288. for d in self.get_all_children():
  289. d.modified = self.modified
  290. d.modified_by = self.modified_by
  291. if not d.owner:
  292. d.owner = self.owner
  293. if not d.creation:
  294. d.creation = self.creation
  295. frappe.flags.currently_saving.append((self.doctype, self.name))
  296. def set_docstatus(self):
  297. if self.docstatus==None:
  298. self.docstatus=0
  299. for d in self.get_all_children():
  300. d.docstatus = self.docstatus
  301. def _validate(self):
  302. self._validate_mandatory()
  303. self._validate_links()
  304. self._validate_selects()
  305. self._validate_constants()
  306. self._validate_length()
  307. self._extract_images_from_text_editor()
  308. self._sanitize_content()
  309. self._save_passwords()
  310. children = self.get_all_children()
  311. for d in children:
  312. d._validate_selects()
  313. d._validate_constants()
  314. d._validate_length()
  315. d._extract_images_from_text_editor()
  316. d._sanitize_content()
  317. d._save_passwords()
  318. if self.is_new():
  319. # don't set fields like _assign, _comments for new doc
  320. for fieldname in optional_fields:
  321. self.set(fieldname, None)
  322. def apply_fieldlevel_read_permissions(self):
  323. '''Remove values the user is not allowed to read (called when loading in desk)'''
  324. has_higher_permlevel = False
  325. for p in self.get_permissions():
  326. if p.permlevel > 0:
  327. has_higher_permlevel = True
  328. break
  329. if not has_higher_permlevel:
  330. return
  331. has_access_to = self.get_permlevel_access('read')
  332. for df in self.meta.fields:
  333. if df.permlevel and not df.permlevel in has_access_to:
  334. self.set(df.fieldname, None)
  335. for table_field in self.meta.get_table_fields():
  336. for df in frappe.get_meta(table_field.options).fields or []:
  337. if df.permlevel and not df.permlevel in has_access_to:
  338. for child in self.get(table_field.fieldname) or []:
  339. child.set(df.fieldname, None)
  340. def validate_higher_perm_levels(self):
  341. """If the user does not have permissions at permlevel > 0, then reset the values to original / default"""
  342. if self.flags.ignore_permissions or frappe.flags.in_install:
  343. return
  344. has_access_to = self.get_permlevel_access()
  345. high_permlevel_fields = self.meta.get_high_permlevel_fields()
  346. if high_permlevel_fields:
  347. self.reset_values_if_no_permlevel_access(has_access_to, high_permlevel_fields)
  348. # check for child tables
  349. for df in self.meta.get_table_fields():
  350. high_permlevel_fields = frappe.get_meta(df.options).meta.get_high_permlevel_fields()
  351. if high_permlevel_fields:
  352. for d in self.get(df.fieldname):
  353. d.reset_values_if_no_permlevel_access(has_access_to, high_permlevel_fields)
  354. def get_permlevel_access(self, permission_type='write'):
  355. if not hasattr(self, "_has_access_to"):
  356. user_roles = frappe.get_roles()
  357. self._has_access_to = []
  358. for perm in self.get_permissions():
  359. if perm.role in user_roles and perm.permlevel > 0 and perm.get(permission_type):
  360. if perm.permlevel not in self._has_access_to:
  361. self._has_access_to.append(perm.permlevel)
  362. return self._has_access_to
  363. def has_permlevel_access_to(self, fieldname, df=None, permission_type='read'):
  364. if not df:
  365. df = self.meta.get_field(fieldname)
  366. return df.permlevel in self.get_permlevel_access()
  367. def get_permissions(self):
  368. if self.meta.istable:
  369. # use parent permissions
  370. permissions = frappe.get_meta(self.parenttype).permissions
  371. else:
  372. permissions = self.meta.permissions
  373. return permissions
  374. def _set_defaults(self):
  375. if frappe.flags.in_import:
  376. return
  377. new_doc = frappe.new_doc(self.doctype, as_dict=True)
  378. self.update_if_missing(new_doc)
  379. # children
  380. for df in self.meta.get_table_fields():
  381. new_doc = frappe.new_doc(df.options, as_dict=True)
  382. value = self.get(df.fieldname)
  383. if isinstance(value, list):
  384. for d in value:
  385. d.update_if_missing(new_doc)
  386. def check_if_latest(self):
  387. """Checks if `modified` timestamp provided by document being updated is same as the
  388. `modified` timestamp in the database. If there is a different, the document has been
  389. updated in the database after the current copy was read. Will throw an error if
  390. timestamps don't match.
  391. Will also validate document transitions (Save > Submit > Cancel) calling
  392. `self.check_docstatus_transition`."""
  393. conflict = False
  394. self._action = "save"
  395. if not self.get('__islocal'):
  396. if self.meta.issingle:
  397. modified = frappe.db.get_value(self.doctype, self.name, "modified")
  398. if cstr(modified) and cstr(modified) != cstr(self._original_modified):
  399. conflict = True
  400. else:
  401. tmp = frappe.db.sql("""select modified, docstatus from `tab{0}`
  402. where name = %s for update""".format(self.doctype), self.name, as_dict=True)
  403. if not tmp:
  404. frappe.throw(_("Record does not exist"))
  405. else:
  406. tmp = tmp[0]
  407. modified = cstr(tmp.modified)
  408. if modified and modified != cstr(self._original_modified):
  409. conflict = True
  410. self.check_docstatus_transition(tmp.docstatus)
  411. if conflict:
  412. frappe.msgprint(_("Error: Document has been modified after you have opened it") \
  413. + (" (%s, %s). " % (modified, self.modified)) \
  414. + _("Please refresh to get the latest document."),
  415. raise_exception=frappe.TimestampMismatchError)
  416. else:
  417. self.check_docstatus_transition(0)
  418. def check_docstatus_transition(self, docstatus):
  419. """Ensures valid `docstatus` transition.
  420. Valid transitions are (number in brackets is `docstatus`):
  421. - Save (0) > Save (0)
  422. - Save (0) > Submit (1)
  423. - Submit (1) > Submit (1)
  424. - Submit (1) > Cancel (2)
  425. If docstatus is > 2, it will throw exception as document is deemed queued
  426. """
  427. if self.docstatus > 2:
  428. frappe.throw(_('This document is currently queued for execution. Please try again'),
  429. title=_('Document Queued'), indicator='red')
  430. if not self.docstatus:
  431. self.docstatus = 0
  432. if docstatus==0:
  433. if self.docstatus==0:
  434. self._action = "save"
  435. elif self.docstatus==1:
  436. self._action = "submit"
  437. self.check_permission("submit")
  438. else:
  439. raise frappe.DocstatusTransitionError, _("Cannot change docstatus from 0 to 2")
  440. elif docstatus==1:
  441. if self.docstatus==1:
  442. self._action = "update_after_submit"
  443. self.check_permission("submit")
  444. elif self.docstatus==2:
  445. self._action = "cancel"
  446. self.check_permission("cancel")
  447. else:
  448. raise frappe.DocstatusTransitionError, _("Cannot change docstatus from 1 to 0")
  449. elif docstatus==2:
  450. raise frappe.ValidationError, _("Cannot edit cancelled document")
  451. def set_parent_in_children(self):
  452. """Updates `parent` and `parenttype` property in all children."""
  453. for d in self.get_all_children():
  454. d.parent = self.name
  455. d.parenttype = self.doctype
  456. def validate_update_after_submit(self):
  457. if self.flags.ignore_validate_update_after_submit:
  458. return
  459. self._validate_update_after_submit()
  460. for d in self.get_all_children():
  461. if d.is_new() and self.meta.get_field(d.parentfield).allow_on_submit:
  462. # in case of a new row, don't validate allow on submit, if table is allow on submit
  463. continue
  464. d._validate_update_after_submit()
  465. # TODO check only allowed values are updated
  466. def _validate_mandatory(self):
  467. if self.flags.ignore_mandatory:
  468. return
  469. missing = self._get_missing_mandatory_fields()
  470. for d in self.get_all_children():
  471. missing.extend(d._get_missing_mandatory_fields())
  472. if not missing:
  473. return
  474. for fieldname, msg in missing:
  475. msgprint(msg)
  476. if frappe.flags.print_messages:
  477. print self.as_json().encode("utf-8")
  478. raise frappe.MandatoryError('[{doctype}, {name}]: {fields}'.format(
  479. fields=", ".join((each[0] for each in missing)),
  480. doctype=self.doctype,
  481. name=self.name))
  482. def _validate_links(self):
  483. if self.flags.ignore_links:
  484. return
  485. invalid_links, cancelled_links = self.get_invalid_links()
  486. for d in self.get_all_children():
  487. result = d.get_invalid_links(is_submittable=self.meta.is_submittable)
  488. invalid_links.extend(result[0])
  489. cancelled_links.extend(result[1])
  490. if invalid_links:
  491. msg = ", ".join((each[2] for each in invalid_links))
  492. frappe.throw(_("Could not find {0}").format(msg),
  493. frappe.LinkValidationError)
  494. if cancelled_links:
  495. msg = ", ".join((each[2] for each in cancelled_links))
  496. frappe.throw(_("Cannot link cancelled document: {0}").format(msg),
  497. frappe.CancelledLinkError)
  498. def get_all_children(self, parenttype=None):
  499. """Returns all children documents from **Table** type field in a list."""
  500. ret = []
  501. for df in self.meta.get("fields", {"fieldtype": "Table"}):
  502. if parenttype:
  503. if df.options==parenttype:
  504. return self.get(df.fieldname)
  505. value = self.get(df.fieldname)
  506. if isinstance(value, list):
  507. ret.extend(value)
  508. return ret
  509. def run_method(self, method, *args, **kwargs):
  510. """run standard triggers, plus those in hooks"""
  511. if "flags" in kwargs:
  512. del kwargs["flags"]
  513. if hasattr(self, method) and hasattr(getattr(self, method), "__call__"):
  514. fn = lambda self, *args, **kwargs: getattr(self, method)(*args, **kwargs)
  515. else:
  516. # hack! to run hooks even if method does not exist
  517. fn = lambda self, *args, **kwargs: None
  518. fn.__name__ = method.encode("utf-8")
  519. out = Document.hook(fn)(self, *args, **kwargs)
  520. self.run_email_alerts(method)
  521. return out
  522. def run_trigger(self, method, *args, **kwargs):
  523. return self.run_method(method, *args, **kwargs)
  524. def run_email_alerts(self, method):
  525. '''Run email alerts for this method'''
  526. if frappe.flags.in_import or frappe.flags.in_patch or frappe.flags.in_install:
  527. return
  528. if self.flags.email_alerts_executed==None:
  529. self.flags.email_alerts_executed = []
  530. from frappe.email.doctype.email_alert.email_alert import evaluate_alert
  531. if self.flags.email_alerts == None:
  532. alerts = frappe.cache().hget('email_alerts', self.doctype)
  533. if alerts==None:
  534. alerts = frappe.get_all('Email Alert', fields=['name', 'event', 'method'],
  535. filters={'enabled': 1, 'document_type': self.doctype})
  536. frappe.cache().hset('email_alerts', self.doctype, alerts)
  537. self.flags.email_alerts = alerts
  538. if not self.flags.email_alerts:
  539. return
  540. def _evaluate_alert(alert):
  541. if not alert.name in self.flags.email_alerts_executed:
  542. evaluate_alert(self, alert.name, alert.event)
  543. event_map = {
  544. "on_update": "Save",
  545. "after_insert": "New",
  546. "on_submit": "Submit",
  547. "on_cancel": "Cancel"
  548. }
  549. if not self.flags.in_insert:
  550. # value change is not applicable in insert
  551. event_map['validate'] = 'Value Change'
  552. for alert in self.flags.email_alerts:
  553. event = event_map.get(method, None)
  554. if event and alert.event == event:
  555. _evaluate_alert(alert)
  556. elif alert.event=='Method' and method == alert.method:
  557. _evaluate_alert(alert)
  558. @staticmethod
  559. def whitelist(f):
  560. f.whitelisted = True
  561. return f
  562. @whitelist.__func__
  563. def _submit(self):
  564. """Submit the document. Sets `docstatus` = 1, then saves."""
  565. self.docstatus = 1
  566. self.save()
  567. @whitelist.__func__
  568. def _cancel(self):
  569. """Cancel the document. Sets `docstatus` = 2, then saves."""
  570. self.docstatus = 2
  571. self.save()
  572. @whitelist.__func__
  573. def submit(self):
  574. """Submit the document. Sets `docstatus` = 1, then saves."""
  575. self._submit()
  576. @whitelist.__func__
  577. def cancel(self):
  578. """Cancel the document. Sets `docstatus` = 2, then saves."""
  579. self._cancel()
  580. def delete(self):
  581. """Delete document."""
  582. frappe.delete_doc(self.doctype, self.name, flags=self.flags)
  583. def run_before_save_methods(self):
  584. """Run standard methods before `INSERT` or `UPDATE`. Standard Methods are:
  585. - `validate`, `before_save` for **Save**.
  586. - `validate`, `before_submit` for **Submit**.
  587. - `before_cancel` for **Cancel**
  588. - `before_update_after_submit` for **Update after Submit**
  589. Will also update title_field if set"""
  590. self.set_title_field()
  591. self.reset_seen()
  592. if self.flags.ignore_validate:
  593. return
  594. if self._action=="save":
  595. self.run_method("validate")
  596. self.run_method("before_save")
  597. elif self._action=="submit":
  598. self.run_method("validate")
  599. self.run_method("before_submit")
  600. elif self._action=="cancel":
  601. self.run_method("before_cancel")
  602. elif self._action=="update_after_submit":
  603. self.run_method("before_update_after_submit")
  604. def run_post_save_methods(self):
  605. """Run standard methods after `INSERT` or `UPDATE`. Standard Methods are:
  606. - `on_update` for **Save**.
  607. - `on_update`, `on_submit` for **Submit**.
  608. - `on_cancel` for **Cancel**
  609. - `update_after_submit` for **Update after Submit**"""
  610. if self._action=="save":
  611. self.run_method("on_update")
  612. elif self._action=="submit":
  613. self.run_method("on_update")
  614. self.run_method("on_submit")
  615. if not self.flags.ignore_submit_comment:
  616. self.add_comment("Submitted")
  617. elif self._action=="cancel":
  618. self.run_method("on_cancel")
  619. self.check_no_back_links_exist()
  620. if not self.flags.ignore_submit_comment:
  621. self.add_comment("Cancelled")
  622. elif self._action=="update_after_submit":
  623. self.run_method("on_update_after_submit")
  624. self.run_method('on_change')
  625. self.update_timeline_doc()
  626. self.clear_cache()
  627. self.notify_update()
  628. if (self.doctype, self.name) in frappe.flags.currently_saving:
  629. frappe.flags.currently_saving.remove((self.doctype, self.name))
  630. self.latest = None
  631. def clear_cache(self):
  632. frappe.cache().hdel("last_modified", self.doctype)
  633. self.clear_linked_with_cache()
  634. def clear_linked_with_cache(self):
  635. cache = frappe.cache()
  636. def _clear_cache(d):
  637. for df in (d.meta.get_link_fields() + d.meta.get_dynamic_link_fields()):
  638. if d.get(df.fieldname):
  639. doctype = df.options if df.fieldtype=="Link" else d.get(df.options)
  640. name = d.get(df.fieldname)
  641. if df.fieldtype=="Dynamic Link":
  642. # clear linked doctypes list
  643. cache.hdel("linked_doctypes", doctype)
  644. # for all users, delete linked with cache and per doctype linked with cache
  645. cache.delete_value("user:*:linked_with:{doctype}:{name}".format(doctype=doctype, name=name))
  646. cache.delete_value("user:*:linked_with:{doctype}:{name}:*".format(doctype=doctype, name=name))
  647. _clear_cache(self)
  648. for d in self.get_all_children():
  649. _clear_cache(d)
  650. def reset_seen(self):
  651. '''Clear _seen property and set current user as seen'''
  652. if getattr(self.meta, 'track_seen', False):
  653. self._seen = json.dumps([frappe.session.user])
  654. def notify_update(self):
  655. """Publish realtime that the current document is modified"""
  656. frappe.publish_realtime("doc_update", {"modified": self.modified, "doctype": self.doctype, "name": self.name},
  657. doctype=self.doctype, docname=self.name, after_commit=True)
  658. if not self.meta.get("read_only") and not self.meta.get("issingle") and \
  659. not self.meta.get("istable"):
  660. frappe.publish_realtime("list_update", {"doctype": self.doctype}, after_commit=True)
  661. def check_no_back_links_exist(self):
  662. """Check if document links to any active document before Cancel."""
  663. from frappe.model.delete_doc import check_if_doc_is_linked, check_if_doc_is_dynamically_linked
  664. if not self.flags.ignore_links:
  665. check_if_doc_is_linked(self, method="Cancel")
  666. check_if_doc_is_dynamically_linked(self, method="Cancel")
  667. @staticmethod
  668. def whitelist(f):
  669. """Decorator: Whitelist method to be called remotely via REST API."""
  670. f.whitelisted = True
  671. return f
  672. @staticmethod
  673. def hook(f):
  674. """Decorator: Make method `hookable` (i.e. extensible by another app).
  675. Note: If each hooked method returns a value (dict), then all returns are
  676. collated in one dict and returned. Ideally, don't return values in hookable
  677. methods, set properties in the document."""
  678. def add_to_return_value(self, new_return_value):
  679. if isinstance(new_return_value, dict):
  680. if not self.get("_return_value"):
  681. self._return_value = {}
  682. self._return_value.update(new_return_value)
  683. else:
  684. self._return_value = new_return_value or self.get("_return_value")
  685. def compose(fn, *hooks):
  686. def runner(self, method, *args, **kwargs):
  687. add_to_return_value(self, fn(self, *args, **kwargs))
  688. for f in hooks:
  689. add_to_return_value(self, f(self, method, *args, **kwargs))
  690. return self._return_value
  691. return runner
  692. def composer(self, *args, **kwargs):
  693. hooks = []
  694. method = f.__name__
  695. doc_events = frappe.get_doc_hooks()
  696. for handler in doc_events.get(self.doctype, {}).get(method, []) \
  697. + doc_events.get("*", {}).get(method, []):
  698. hooks.append(frappe.get_attr(handler))
  699. composed = compose(f, *hooks)
  700. return composed(self, method, *args, **kwargs)
  701. return composer
  702. def is_whitelisted(self, method):
  703. fn = getattr(self, method, None)
  704. if not fn:
  705. raise NotFound("Method {0} not found".format(method))
  706. elif not getattr(fn, "whitelisted", False):
  707. raise Forbidden("Method {0} not whitelisted".format(method))
  708. def validate_value(self, fieldname, condition, val2, doc=None, raise_exception=None):
  709. """Check that value of fieldname should be 'condition' val2
  710. else throw Exception."""
  711. error_condition_map = {
  712. "in": _("one of"),
  713. "not in": _("none of"),
  714. "^": _("beginning with"),
  715. }
  716. if not doc:
  717. doc = self
  718. val1 = doc.get_value(fieldname)
  719. df = doc.meta.get_field(fieldname)
  720. val2 = doc.cast(val2, df)
  721. if not frappe.compare(val1, condition, val2):
  722. label = doc.meta.get_label(fieldname)
  723. condition_str = error_condition_map.get(condition, condition)
  724. if doc.parentfield:
  725. msg = _("Incorrect value in row {0}: {1} must be {2} {3}".format(doc.idx, label, condition_str, val2))
  726. else:
  727. msg = _("Incorrect value: {0} must be {1} {2}".format(label, condition_str, val2))
  728. # raise passed exception or True
  729. msgprint(msg, raise_exception=raise_exception or True)
  730. def validate_table_has_rows(self, parentfield, raise_exception=None):
  731. """Raise exception if Table field is empty."""
  732. if not (isinstance(self.get(parentfield), list) and len(self.get(parentfield)) > 0):
  733. label = self.meta.get_label(parentfield)
  734. frappe.throw(_("Table {0} cannot be empty").format(label), raise_exception or frappe.EmptyTableError)
  735. def round_floats_in(self, doc, fieldnames=None):
  736. """Round floats for all `Currency`, `Float`, `Percent` fields for the given doc.
  737. :param doc: Document whose numeric properties are to be rounded.
  738. :param fieldnames: [Optional] List of fields to be rounded."""
  739. if not fieldnames:
  740. fieldnames = (df.fieldname for df in
  741. doc.meta.get("fields", {"fieldtype": ["in", ["Currency", "Float", "Percent"]]}))
  742. for fieldname in fieldnames:
  743. doc.set(fieldname, flt(doc.get(fieldname), self.precision(fieldname, doc.parentfield)))
  744. def get_url(self):
  745. """Returns Desk URL for this document. `/desk#Form/{doctype}/{name}`"""
  746. return "/desk#Form/{doctype}/{name}".format(doctype=self.doctype, name=self.name)
  747. def add_comment(self, comment_type, text=None, comment_by=None, link_doctype=None, link_name=None):
  748. """Add a comment to this document.
  749. :param comment_type: e.g. `Comment`. See Communication for more info."""
  750. comment = frappe.get_doc({
  751. "doctype":"Communication",
  752. "communication_type": "Comment",
  753. "sender": comment_by or frappe.session.user,
  754. "comment_type": comment_type,
  755. "reference_doctype": self.doctype,
  756. "reference_name": self.name,
  757. "content": text or comment_type,
  758. "link_doctype": link_doctype,
  759. "link_name": link_name
  760. }).insert(ignore_permissions=True)
  761. return comment
  762. def add_seen(self, user=None):
  763. '''add the given/current user to list of users who have seen this document (_seen)'''
  764. if not user:
  765. user = frappe.session.user
  766. if self.meta.track_seen:
  767. if self._seen:
  768. _seen = json.loads(self._seen)
  769. else:
  770. _seen = []
  771. if user not in _seen:
  772. _seen.append(user)
  773. self.db_set('_seen', json.dumps(_seen), update_modified=False)
  774. frappe.local.flags.commit = True
  775. def get_signature(self):
  776. """Returns signature (hash) for private URL."""
  777. return hashlib.sha224(get_datetime_str(self.creation)).hexdigest()
  778. def get_liked_by(self):
  779. liked_by = getattr(self, "_liked_by", None)
  780. if liked_by:
  781. return json.loads(liked_by)
  782. else:
  783. return []
  784. def set_onload(self, key, value):
  785. if not self.get("__onload"):
  786. self.set("__onload", frappe._dict())
  787. self.get("__onload")[key] = value
  788. def update_timeline_doc(self):
  789. if frappe.flags.in_install or not self.meta.get("timeline_field"):
  790. return
  791. timeline_doctype = self.meta.get_link_doctype(self.meta.timeline_field)
  792. timeline_name = self.get(self.meta.timeline_field)
  793. if not (timeline_doctype and timeline_name):
  794. return
  795. # update timeline doc in communication if it is different than current timeline doc
  796. frappe.db.sql("""update `tabCommunication`
  797. set timeline_doctype=%(timeline_doctype)s, timeline_name=%(timeline_name)s
  798. where
  799. reference_doctype=%(doctype)s and reference_name=%(name)s
  800. and (timeline_doctype is null or timeline_doctype != %(timeline_doctype)s
  801. or timeline_name is null or timeline_name != %(timeline_name)s)""",
  802. {
  803. "doctype": self.doctype,
  804. "name": self.name,
  805. "timeline_doctype": timeline_doctype,
  806. "timeline_name": timeline_name
  807. })
  808. def queue_action(self, action, **kwargs):
  809. '''Run an action in background. If the action has an inner function,
  810. like _submit for submit, it will call that instead'''
  811. if action in ('save', 'submit', 'cancel'):
  812. # set docstatus explicitly again due to inconsistent action
  813. self.docstatus = {'save':0, 'submit':1, 'cancel': 2}[action]
  814. else:
  815. raise 'Action must be one of save, submit, cancel'
  816. # call _submit instead of submit, so you can override submit to call
  817. # run_delayed based on some action
  818. # See: Stock Reconciliation
  819. if hasattr(self, '_' + action):
  820. action = '_' + action
  821. self.lock()
  822. frappe.db.commit()
  823. enqueue('frappe.model.document.execute_action', doctype=self.doctype, name=self.name,
  824. action=action, **kwargs)
  825. def execute_action(doctype, name, action, **kwargs):
  826. '''Execute an action on a document (called by background worker)'''
  827. doc = frappe.get_doc(doctype, name)
  828. doc.unlock()
  829. frappe.db.commit()
  830. try:
  831. getattr(doc, action)(**kwargs)
  832. except Exception:
  833. frappe.db.rollback()
  834. # add a comment (?)
  835. if frappe.local.message_log:
  836. msg = json.loads(frappe.local.message_log[-1]).get('message')
  837. else:
  838. msg = '<pre><code>' + frappe.get_traceback() + '</pre></code>'
  839. doc.add_comment('Comment', _('Action Failed') + '<br><br>' + msg)
  840. doc.notify_update()