您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

1408 行
43 KiB

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