Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 
 
 
 

489 Zeilen
13 KiB

  1. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  2. # License: MIT. See LICENSE
  3. import json
  4. import os
  5. from typing import TYPE_CHECKING
  6. import frappe
  7. import frappe.model
  8. import frappe.utils
  9. from frappe import _
  10. from frappe.desk.reportview import validate_args
  11. from frappe.model.db_query import check_parent_permission
  12. from frappe.utils import get_safe_filters
  13. if TYPE_CHECKING:
  14. from frappe.model.document import Document
  15. """
  16. Handle RESTful requests that are mapped to the `/api/resource` route.
  17. Requests via FrappeClient are also handled here.
  18. """
  19. @frappe.whitelist()
  20. def get_list(
  21. doctype,
  22. fields=None,
  23. filters=None,
  24. order_by=None,
  25. limit_start=None,
  26. limit_page_length=20,
  27. parent=None,
  28. debug=False,
  29. as_dict=True,
  30. or_filters=None,
  31. ):
  32. """Returns a list of records by filters, fields, ordering and limit
  33. :param doctype: DocType of the data to be queried
  34. :param fields: fields to be returned. Default is `name`
  35. :param filters: filter list by this dict
  36. :param order_by: Order by this fieldname
  37. :param limit_start: Start at this index
  38. :param limit_page_length: Number of records to be returned (default 20)"""
  39. if frappe.is_table(doctype):
  40. check_parent_permission(parent, doctype)
  41. args = frappe._dict(
  42. doctype=doctype,
  43. parent_doctype=parent,
  44. fields=fields,
  45. filters=filters,
  46. or_filters=or_filters,
  47. order_by=order_by,
  48. limit_start=limit_start,
  49. limit_page_length=limit_page_length,
  50. debug=debug,
  51. as_list=not as_dict,
  52. )
  53. validate_args(args)
  54. return frappe.get_list(**args)
  55. @frappe.whitelist()
  56. def get_count(doctype, filters=None, debug=False, cache=False):
  57. return frappe.db.count(doctype, get_safe_filters(filters), debug, cache)
  58. @frappe.whitelist()
  59. def get(doctype, name=None, filters=None, parent=None):
  60. """Returns a document by name or filters
  61. :param doctype: DocType of the document to be returned
  62. :param name: return document of this `name`
  63. :param filters: If name is not set, filter by these values and return the first match"""
  64. if frappe.is_table(doctype):
  65. check_parent_permission(parent, doctype)
  66. if filters and not name:
  67. name = frappe.db.get_value(doctype, frappe.parse_json(filters))
  68. if not name:
  69. frappe.throw(_("No document found for given filters"))
  70. doc = frappe.get_doc(doctype, name)
  71. if not doc.has_permission("read"):
  72. raise frappe.PermissionError
  73. return frappe.get_doc(doctype, name).as_dict()
  74. @frappe.whitelist()
  75. def get_value(doctype, fieldname, filters=None, as_dict=True, debug=False, parent=None):
  76. """Returns a value form a document
  77. :param doctype: DocType to be queried
  78. :param fieldname: Field to be returned (default `name`)
  79. :param filters: dict or string for identifying the record"""
  80. if frappe.is_table(doctype):
  81. check_parent_permission(parent, doctype)
  82. if not frappe.has_permission(doctype, parent_doctype=parent):
  83. frappe.throw(_("No permission for {0}").format(_(doctype)), frappe.PermissionError)
  84. filters = get_safe_filters(filters)
  85. if isinstance(filters, str):
  86. filters = {"name": filters}
  87. try:
  88. fields = frappe.parse_json(fieldname)
  89. except (TypeError, ValueError):
  90. # name passed, not json
  91. fields = [fieldname]
  92. # check whether the used filters were really parseable and usable
  93. # and did not just result in an empty string or dict
  94. if not filters:
  95. filters = None
  96. if frappe.get_meta(doctype).issingle:
  97. value = frappe.db.get_values_from_single(fields, filters, doctype, as_dict=as_dict, debug=debug)
  98. else:
  99. value = get_list(
  100. doctype,
  101. filters=filters,
  102. fields=fields,
  103. debug=debug,
  104. limit_page_length=1,
  105. parent=parent,
  106. as_dict=as_dict,
  107. )
  108. if as_dict:
  109. return value[0] if value else {}
  110. if not value:
  111. return
  112. return value[0] if len(fields) > 1 else value[0][0]
  113. @frappe.whitelist()
  114. def get_single_value(doctype, field):
  115. if not frappe.has_permission(doctype):
  116. frappe.throw(_("No permission for {0}").format(_(doctype)), frappe.PermissionError)
  117. value = frappe.db.get_single_value(doctype, field)
  118. return value
  119. @frappe.whitelist(methods=["POST", "PUT"])
  120. def set_value(doctype, name, fieldname, value=None):
  121. """Set a value using get_doc, group of values
  122. :param doctype: DocType of the document
  123. :param name: name of the document
  124. :param fieldname: fieldname string or JSON / dict with key value pair
  125. :param value: value if fieldname is JSON / dict"""
  126. if fieldname in (frappe.model.default_fields + frappe.model.child_table_fields):
  127. frappe.throw(_("Cannot edit standard fields"))
  128. if not value:
  129. values = fieldname
  130. if isinstance(fieldname, str):
  131. try:
  132. values = json.loads(fieldname)
  133. except ValueError:
  134. values = {fieldname: ""}
  135. else:
  136. values = {fieldname: value}
  137. # check for child table doctype
  138. if not frappe.get_meta(doctype).istable:
  139. doc = frappe.get_doc(doctype, name)
  140. doc.update(values)
  141. else:
  142. doc = frappe.db.get_value(doctype, name, ["parenttype", "parent"], as_dict=True)
  143. doc = frappe.get_doc(doc.parenttype, doc.parent)
  144. child = doc.getone({"doctype": doctype, "name": name})
  145. child.update(values)
  146. doc.save()
  147. return doc.as_dict()
  148. @frappe.whitelist(methods=["POST", "PUT"])
  149. def insert(doc=None):
  150. """Insert a document
  151. :param doc: JSON or dict object to be inserted"""
  152. if isinstance(doc, str):
  153. doc = json.loads(doc)
  154. return insert_doc(doc).as_dict()
  155. @frappe.whitelist(methods=["POST", "PUT"])
  156. def insert_many(docs=None):
  157. """Insert multiple documents
  158. :param docs: JSON or list of dict objects to be inserted in one request"""
  159. if isinstance(docs, str):
  160. docs = json.loads(docs)
  161. if len(docs) > 200:
  162. frappe.throw(_("Only 200 inserts allowed in one request"))
  163. out = set()
  164. for doc in docs:
  165. out.add(insert_doc(doc).name)
  166. return out
  167. @frappe.whitelist(methods=["POST", "PUT"])
  168. def save(doc):
  169. """Update (save) an existing document
  170. :param doc: JSON or dict object with the properties of the document to be updated"""
  171. if isinstance(doc, str):
  172. doc = json.loads(doc)
  173. doc = frappe.get_doc(doc)
  174. doc.save()
  175. return doc.as_dict()
  176. @frappe.whitelist(methods=["POST", "PUT"])
  177. def rename_doc(doctype, old_name, new_name, merge=False):
  178. """Rename document
  179. :param doctype: DocType of the document to be renamed
  180. :param old_name: Current `name` of the document to be renamed
  181. :param new_name: New `name` to be set"""
  182. new_name = frappe.rename_doc(doctype, old_name, new_name, merge=merge)
  183. return new_name
  184. @frappe.whitelist(methods=["POST", "PUT"])
  185. def submit(doc):
  186. """Submit a document
  187. :param doc: JSON or dict object to be submitted remotely"""
  188. if isinstance(doc, str):
  189. doc = json.loads(doc)
  190. doc = frappe.get_doc(doc)
  191. doc.submit()
  192. return doc.as_dict()
  193. @frappe.whitelist(methods=["POST", "PUT"])
  194. def cancel(doctype, name):
  195. """Cancel a document
  196. :param doctype: DocType of the document to be cancelled
  197. :param name: name of the document to be cancelled"""
  198. wrapper = frappe.get_doc(doctype, name)
  199. wrapper.cancel()
  200. return wrapper.as_dict()
  201. @frappe.whitelist(methods=["DELETE", "POST"])
  202. def delete(doctype, name):
  203. """Delete a remote document
  204. :param doctype: DocType of the document to be deleted
  205. :param name: name of the document to be deleted"""
  206. frappe.delete_doc(doctype, name, ignore_missing=False)
  207. @frappe.whitelist(methods=["POST", "PUT"])
  208. def set_default(key, value, parent=None):
  209. """set a user default value"""
  210. frappe.db.set_default(key, value, parent or frappe.session.user)
  211. frappe.clear_cache(user=frappe.session.user)
  212. @frappe.whitelist(methods=["POST", "PUT"])
  213. def make_width_property_setter(doc):
  214. """Set width Property Setter
  215. :param doc: Property Setter document with `width` property"""
  216. if isinstance(doc, str):
  217. doc = json.loads(doc)
  218. if doc["doctype"] == "Property Setter" and doc["property"] == "width":
  219. frappe.get_doc(doc).insert(ignore_permissions=True)
  220. @frappe.whitelist(methods=["POST", "PUT"])
  221. def bulk_update(docs):
  222. """Bulk update documents
  223. :param docs: JSON list of documents to be updated remotely. Each document must have `docname` property"""
  224. docs = json.loads(docs)
  225. failed_docs = []
  226. for doc in docs:
  227. doc.pop("flags", None)
  228. try:
  229. existing_doc = frappe.get_doc(doc["doctype"], doc["docname"])
  230. existing_doc.update(doc)
  231. existing_doc.save()
  232. except Exception:
  233. failed_docs.append({"doc": doc, "exc": frappe.utils.get_traceback()})
  234. return {"failed_docs": failed_docs}
  235. @frappe.whitelist()
  236. def has_permission(doctype, docname, perm_type="read"):
  237. """Returns a JSON with data whether the document has the requested permission
  238. :param doctype: DocType of the document to be checked
  239. :param docname: `name` of the document to be checked
  240. :param perm_type: one of `read`, `write`, `create`, `submit`, `cancel`, `report`. Default is `read`"""
  241. # perm_type can be one of read, write, create, submit, cancel, report
  242. return {"has_permission": frappe.has_permission(doctype, perm_type.lower(), docname)}
  243. @frappe.whitelist()
  244. def get_password(doctype, name, fieldname):
  245. """Return a password type property. Only applicable for System Managers
  246. :param doctype: DocType of the document that holds the password
  247. :param name: `name` of the document that holds the password
  248. :param fieldname: `fieldname` of the password property
  249. """
  250. frappe.only_for("System Manager")
  251. return frappe.get_doc(doctype, name).get_password(fieldname)
  252. @frappe.whitelist()
  253. def get_js(items):
  254. """Load JS code files. Will also append translations
  255. and extend `frappe._messages`
  256. :param items: JSON list of paths of the js files to be loaded."""
  257. items = json.loads(items)
  258. out = []
  259. for src in items:
  260. src = src.strip("/").split("/")
  261. if ".." in src or src[0] != "assets":
  262. frappe.throw(_("Invalid file path: {0}").format("/".join(src)))
  263. contentpath = os.path.join(frappe.local.sites_path, *src)
  264. with open(contentpath) as srcfile:
  265. code = frappe.utils.cstr(srcfile.read())
  266. if frappe.local.lang != "en":
  267. messages = frappe.get_lang_dict("jsfile", contentpath)
  268. messages = json.dumps(messages)
  269. code += f"\n\n$.extend(frappe._messages, {messages})"
  270. out.append(code)
  271. return out
  272. @frappe.whitelist(allow_guest=True)
  273. def get_time_zone():
  274. """Returns default time zone"""
  275. return {"time_zone": frappe.defaults.get_defaults().get("time_zone")}
  276. @frappe.whitelist(methods=["POST", "PUT"])
  277. def attach_file(
  278. filename=None,
  279. filedata=None,
  280. doctype=None,
  281. docname=None,
  282. folder=None,
  283. decode_base64=False,
  284. is_private=None,
  285. docfield=None,
  286. ):
  287. """Attach a file to Document
  288. :param filename: filename e.g. test-file.txt
  289. :param filedata: base64 encode filedata which must be urlencoded
  290. :param doctype: Reference DocType to attach file to
  291. :param docname: Reference DocName to attach file to
  292. :param folder: Folder to add File into
  293. :param decode_base64: decode filedata from base64 encode, default is False
  294. :param is_private: Attach file as private file (1 or 0)
  295. :param docfield: file to attach to (optional)"""
  296. doc = frappe.get_doc(doctype, docname)
  297. doc.check_permission()
  298. file = frappe.get_doc(
  299. {
  300. "doctype": "File",
  301. "file_name": filename,
  302. "attached_to_doctype": doctype,
  303. "attached_to_name": docname,
  304. "attached_to_field": docfield,
  305. "folder": folder,
  306. "is_private": is_private,
  307. "content": filedata,
  308. "decode": decode_base64,
  309. }
  310. ).save()
  311. if docfield and doctype:
  312. doc.set(docfield, file.file_url)
  313. doc.save()
  314. return file
  315. @frappe.whitelist()
  316. def get_hooks(hook, app_name=None):
  317. return frappe.get_hooks(hook, app_name)
  318. @frappe.whitelist()
  319. def is_document_amended(doctype, docname):
  320. if frappe.permissions.has_permission(doctype):
  321. try:
  322. return frappe.db.exists(doctype, {"amended_from": docname})
  323. except frappe.db.InternalError:
  324. pass
  325. return False
  326. @frappe.whitelist()
  327. def validate_link(doctype: str, docname: str, fields=None):
  328. if not isinstance(doctype, str):
  329. frappe.throw(_("DocType must be a string"))
  330. if not isinstance(docname, str):
  331. frappe.throw(_("Document Name must be a string"))
  332. if doctype != "DocType" and not (
  333. frappe.has_permission(doctype, "select") or frappe.has_permission(doctype, "read")
  334. ):
  335. frappe.throw(
  336. _("You do not have Read or Select Permissions for {}").format(frappe.bold(doctype)),
  337. frappe.PermissionError,
  338. )
  339. values = frappe._dict()
  340. values.name = frappe.db.get_value(doctype, docname, cache=True)
  341. fields = frappe.parse_json(fields)
  342. if not values.name or not fields:
  343. return values
  344. try:
  345. values.update(get_value(doctype, fields, docname))
  346. except frappe.PermissionError:
  347. frappe.clear_last_message()
  348. frappe.msgprint(
  349. _("You need {0} permission to fetch values from {1} {2}").format(
  350. frappe.bold(_("Read")), frappe.bold(doctype), frappe.bold(docname)
  351. ),
  352. title=_("Cannot Fetch Values"),
  353. indicator="orange",
  354. )
  355. return values
  356. def insert_doc(doc) -> "Document":
  357. """Inserts document and returns parent document object with appended child document
  358. if `doc` is child document else returns the inserted document object
  359. :param doc: doc to insert (dict)"""
  360. doc = frappe._dict(doc)
  361. if frappe.is_table(doc.doctype):
  362. if not (doc.parenttype and doc.parent and doc.parentfield):
  363. frappe.throw(_("Parenttype, Parent and Parentfield are required to insert a child record"))
  364. # inserting a child record
  365. parent = frappe.get_doc(doc.parenttype, doc.parent)
  366. parent.append(doc.parentfield, doc)
  367. parent.save()
  368. return parent
  369. return frappe.get_doc(doc).insert()