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

1231 lines
37 KiB

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