You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

572 lines
21 KiB

  1. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  2. # License: MIT. See LICENSE
  3. import frappe
  4. import datetime
  5. import unittest
  6. from frappe.model.db_query import DatabaseQuery
  7. from frappe.desk.reportview import get_filters_cond
  8. from frappe.query_builder import Column
  9. from frappe.core.page.permission_manager.permission_manager import update, reset, add
  10. from frappe.permissions import add_user_permission, clear_user_permissions_for_doctype
  11. from frappe.custom.doctype.property_setter.property_setter import make_property_setter
  12. from frappe.handler import execute_cmd
  13. from frappe.utils.testutils import add_custom_field, clear_custom_fields
  14. test_dependencies = ['User', 'Blog Post', 'Blog Category', 'Blogger']
  15. class TestReportview(unittest.TestCase):
  16. def setUp(self):
  17. frappe.set_user("Administrator")
  18. def test_basic(self):
  19. self.assertTrue({"name":"DocType"} in DatabaseQuery("DocType").execute(limit_page_length=None))
  20. def test_extract_tables(self):
  21. db_query = DatabaseQuery("DocType")
  22. add_custom_field("DocType", 'test_tab_field', 'Data')
  23. db_query.fields = ["tabNote.creation", "test_tab_field", "tabDocType.test_tab_field"]
  24. db_query.extract_tables()
  25. self.assertIn("`tabNote`", db_query.tables)
  26. self.assertIn("`tabDocType`", db_query.tables)
  27. self.assertNotIn("test_tab_field", db_query.tables)
  28. clear_custom_fields("DocType")
  29. def test_build_match_conditions(self):
  30. clear_user_permissions_for_doctype('Blog Post', 'test2@example.com')
  31. test2user = frappe.get_doc('User', 'test2@example.com')
  32. test2user.add_roles('Blogger')
  33. frappe.set_user('test2@example.com')
  34. # this will get match conditions for Blog Post
  35. build_match_conditions = DatabaseQuery('Blog Post').build_match_conditions
  36. # Before any user permission is applied
  37. # get as filters
  38. self.assertEqual(build_match_conditions(as_condition=False), [])
  39. # get as conditions
  40. self.assertEqual(build_match_conditions(as_condition=True), "")
  41. add_user_permission('Blog Post', '-test-blog-post', 'test2@example.com', True)
  42. add_user_permission('Blog Post', '-test-blog-post-1', 'test2@example.com', True)
  43. # After applying user permission
  44. # get as filters
  45. self.assertTrue({'Blog Post': ['-test-blog-post-1', '-test-blog-post']} in build_match_conditions(as_condition=False))
  46. # get as conditions
  47. self.assertEqual(build_match_conditions(as_condition=True),
  48. """(((ifnull(`tabBlog Post`.`name`, '')='' or `tabBlog Post`.`name` in ('-test-blog-post-1', '-test-blog-post'))))""")
  49. frappe.set_user('Administrator')
  50. def test_fields(self):
  51. self.assertTrue({"name":"DocType", "issingle":0} \
  52. in DatabaseQuery("DocType").execute(fields=["name", "issingle"], limit_page_length=None))
  53. def test_filters_1(self):
  54. self.assertFalse({"name":"DocType"} \
  55. in DatabaseQuery("DocType").execute(filters=[["DocType", "name", "like", "J%"]]))
  56. def test_filters_2(self):
  57. self.assertFalse({"name":"DocType"} \
  58. in DatabaseQuery("DocType").execute(filters=[{"name": ["like", "J%"]}]))
  59. def test_filters_3(self):
  60. self.assertFalse({"name":"DocType"} \
  61. in DatabaseQuery("DocType").execute(filters={"name": ["like", "J%"]}))
  62. def test_filters_4(self):
  63. self.assertTrue({"name":"DocField"} \
  64. in DatabaseQuery("DocType").execute(filters={"name": "DocField"}))
  65. def test_in_not_in_filters(self):
  66. self.assertFalse(DatabaseQuery("DocType").execute(filters={"name": ["in", None]}))
  67. self.assertTrue({"name":"DocType"} \
  68. in DatabaseQuery("DocType").execute(filters={"name": ["not in", None]}))
  69. for result in [{"name":"DocType"}, {"name":"DocField"}]:
  70. self.assertTrue(result
  71. in DatabaseQuery("DocType").execute(filters={"name": ["in", 'DocType,DocField']}))
  72. for result in [{"name":"DocType"}, {"name":"DocField"}]:
  73. self.assertFalse(result
  74. in DatabaseQuery("DocType").execute(filters={"name": ["not in", 'DocType,DocField']}))
  75. def test_none_filter(self):
  76. query = frappe.db.query.get_sql("DocType", fields="name", filters={"restrict_to_domain": None})
  77. sql = str(query).replace('`', '').replace('"', '')
  78. condition = 'restrict_to_domain IS NULL'
  79. self.assertIn(condition, sql)
  80. def test_or_filters(self):
  81. data = DatabaseQuery("DocField").execute(
  82. filters={"parent": "DocType"}, fields=["fieldname", "fieldtype"],
  83. or_filters=[{"fieldtype":"Table"}, {"fieldtype":"Select"}])
  84. self.assertTrue({"fieldtype":"Table", "fieldname":"fields"} in data)
  85. self.assertTrue({"fieldtype":"Select", "fieldname":"document_type"} in data)
  86. self.assertFalse({"fieldtype":"Check", "fieldname":"issingle"} in data)
  87. def test_between_filters(self):
  88. """ test case to check between filter for date fields """
  89. frappe.db.delete("Event")
  90. # create events to test the between operator filter
  91. todays_event = create_event()
  92. event1 = create_event(starts_on="2016-07-05 23:59:59")
  93. event2 = create_event(starts_on="2016-07-06 00:00:00")
  94. event3 = create_event(starts_on="2016-07-07 23:59:59")
  95. event4 = create_event(starts_on="2016-07-08 00:00:01")
  96. # if the values are not passed in filters then event should be filter as current datetime
  97. data = DatabaseQuery("Event").execute(
  98. filters={"starts_on": ["between", None]}, fields=["name"])
  99. self.assertTrue({ "name": event1.name } not in data)
  100. # if both from and to_date values are passed
  101. data = DatabaseQuery("Event").execute(
  102. filters={"starts_on": ["between", ["2016-07-06", "2016-07-07"]]},
  103. fields=["name"])
  104. self.assertTrue({ "name": event2.name } in data)
  105. self.assertTrue({ "name": event3.name } in data)
  106. self.assertTrue({ "name": event1.name } not in data)
  107. self.assertTrue({ "name": event4.name } not in data)
  108. # if only one value is passed in the filter
  109. data = DatabaseQuery("Event").execute(
  110. filters={"starts_on": ["between", ["2016-07-07"]]},
  111. fields=["name"])
  112. self.assertTrue({ "name": event3.name } in data)
  113. self.assertTrue({ "name": event4.name } in data)
  114. self.assertTrue({ "name": todays_event.name } in data)
  115. self.assertTrue({ "name": event1.name } not in data)
  116. self.assertTrue({ "name": event2.name } not in data)
  117. # test between is formatted for creation column
  118. data = DatabaseQuery("Event").execute(
  119. filters={"creation": ["between", ["2016-07-06", "2016-07-07"]]},
  120. fields=["name"])
  121. def test_ignore_permissions_for_get_filters_cond(self):
  122. frappe.set_user('test2@example.com')
  123. self.assertRaises(frappe.PermissionError, get_filters_cond, 'DocType', dict(istable=1), [])
  124. self.assertTrue(get_filters_cond('DocType', dict(istable=1), [], ignore_permissions=True))
  125. frappe.set_user('Administrator')
  126. def test_query_fields_sanitizer(self):
  127. self.assertRaises(frappe.DataError, DatabaseQuery("DocType").execute,
  128. fields=["name", "issingle, version()"], limit_start=0, limit_page_length=1)
  129. self.assertRaises(frappe.DataError, DatabaseQuery("DocType").execute,
  130. fields=["name", "issingle, IF(issingle=1, (select name from tabUser), count(name))"],
  131. limit_start=0, limit_page_length=1)
  132. self.assertRaises(frappe.DataError, DatabaseQuery("DocType").execute,
  133. fields=["name", "issingle, (select count(*) from tabSessions)"],
  134. limit_start=0, limit_page_length=1)
  135. self.assertRaises(frappe.DataError, DatabaseQuery("DocType").execute,
  136. fields=["name", "issingle, SELECT LOCATE('', `tabUser`.`user`) AS user;"],
  137. limit_start=0, limit_page_length=1)
  138. self.assertRaises(frappe.DataError, DatabaseQuery("DocType").execute,
  139. fields=["name", "issingle, IF(issingle=1, (SELECT name from tabUser), count(*))"],
  140. limit_start=0, limit_page_length=1)
  141. self.assertRaises(frappe.DataError, DatabaseQuery("DocType").execute,
  142. fields=["name", "issingle ''"],limit_start=0, limit_page_length=1)
  143. self.assertRaises(frappe.DataError, DatabaseQuery("DocType").execute,
  144. fields=["name", "issingle,'"],limit_start=0, limit_page_length=1)
  145. self.assertRaises(frappe.DataError, DatabaseQuery("DocType").execute,
  146. fields=["name", "select * from tabSessions"],limit_start=0, limit_page_length=1)
  147. self.assertRaises(frappe.DataError, DatabaseQuery("DocType").execute,
  148. fields=["name", "issingle from --"],limit_start=0, limit_page_length=1)
  149. self.assertRaises(frappe.DataError, DatabaseQuery("DocType").execute,
  150. fields=["name", "issingle from tabDocType order by 2 --"],limit_start=0, limit_page_length=1)
  151. self.assertRaises(frappe.DataError, DatabaseQuery("DocType").execute,
  152. fields=["name", "1' UNION SELECT * FROM __Auth --"],limit_start=0, limit_page_length=1)
  153. self.assertRaises(frappe.DataError, DatabaseQuery("DocType").execute,
  154. fields=["@@version"], limit_start=0, limit_page_length=1)
  155. data = DatabaseQuery("DocType").execute(fields=["count(`name`) as count"],
  156. limit_start=0, limit_page_length=1)
  157. self.assertTrue('count' in data[0])
  158. data = DatabaseQuery("DocType").execute(fields=["name", "issingle", "locate('', name) as _relevance"],
  159. limit_start=0, limit_page_length=1)
  160. self.assertTrue('_relevance' in data[0])
  161. data = DatabaseQuery("DocType").execute(fields=["name", "issingle", "date(creation) as creation"],
  162. limit_start=0, limit_page_length=1)
  163. self.assertTrue('creation' in data[0])
  164. if frappe.db.db_type != 'postgres':
  165. # datediff function does not exist in postgres
  166. data = DatabaseQuery("DocType").execute(fields=["name", "issingle",
  167. "datediff(modified, creation) as date_diff"], limit_start=0, limit_page_length=1)
  168. self.assertTrue('date_diff' in data[0])
  169. def test_nested_permission(self):
  170. frappe.set_user('Administrator')
  171. create_nested_doctype()
  172. create_nested_doctype_records()
  173. clear_user_permissions_for_doctype('Nested DocType')
  174. # user permission for only one root folder
  175. add_user_permission('Nested DocType', 'Level 1 A', 'test2@example.com')
  176. from frappe.core.page.permission_manager.permission_manager import update
  177. # to avoid if_owner filter
  178. update('Nested DocType', 'All', 0, 'if_owner', 0)
  179. frappe.set_user('test2@example.com')
  180. data = DatabaseQuery('Nested DocType').execute()
  181. # children of root folder (for which we added user permission) should be accessible
  182. self.assertTrue({'name': 'Level 2 A'} in data)
  183. self.assertTrue({'name': 'Level 2 A'} in data)
  184. # other folders should not be accessible
  185. self.assertFalse({'name': 'Level 1 B'} in data)
  186. self.assertFalse({'name': 'Level 2 B'} in data)
  187. update('Nested DocType', 'All', 0, 'if_owner', 1)
  188. frappe.set_user('Administrator')
  189. def test_filter_sanitizer(self):
  190. self.assertRaises(frappe.DataError, DatabaseQuery("DocType").execute,
  191. fields=["name"], filters={'istable,': 1}, limit_start=0, limit_page_length=1)
  192. self.assertRaises(frappe.DataError, DatabaseQuery("DocType").execute,
  193. fields=["name"], filters={'editable_grid,': 1}, or_filters={'istable,': 1},
  194. limit_start=0, limit_page_length=1)
  195. self.assertRaises(frappe.DataError, DatabaseQuery("DocType").execute,
  196. fields=["name"], filters={'editable_grid,': 1},
  197. or_filters=[['DocType', 'istable,', '=', 1]],
  198. limit_start=0, limit_page_length=1)
  199. self.assertRaises(frappe.DataError, DatabaseQuery("DocType").execute,
  200. fields=["name"], filters={'editable_grid,': 1},
  201. or_filters=[['DocType', 'istable', '=', 1], ['DocType', 'beta and 1=1', '=', 0]],
  202. limit_start=0, limit_page_length=1)
  203. out = DatabaseQuery("DocType").execute(fields=["name"],
  204. filters={'editable_grid': 1, 'module': 'Core'},
  205. or_filters=[['DocType', 'istable', '=', 1]], order_by='creation')
  206. self.assertTrue('DocField' in [d['name'] for d in out])
  207. out = DatabaseQuery("DocType").execute(fields=["name"],
  208. filters={'issingle': 1}, or_filters=[['DocType', 'module', '=', 'Core']],
  209. order_by='creation')
  210. self.assertTrue('Role Permission for Page and Report' in [d['name'] for d in out])
  211. out = DatabaseQuery("DocType").execute(fields=["name"],
  212. filters={'track_changes': 1, 'module': 'Core'},
  213. order_by='creation')
  214. self.assertTrue('File' in [d['name'] for d in out])
  215. out = DatabaseQuery("DocType").execute(fields=["name"],
  216. filters=[
  217. ['DocType', 'ifnull(track_changes, 0)', '=', 0],
  218. ['DocType', 'module', '=', 'Core']
  219. ], order_by='creation')
  220. self.assertTrue('DefaultValue' in [d['name'] for d in out])
  221. def test_of_not_of_descendant_ancestors(self):
  222. frappe.set_user('Administrator')
  223. clear_user_permissions_for_doctype('Nested DocType')
  224. # in descendants filter
  225. data = frappe.get_all('Nested DocType', {'name': ('descendants of', 'Level 2 A')})
  226. self.assertTrue({"name": "Level 3 A"} in data)
  227. data = frappe.get_all('Nested DocType', {'name': ('descendants of', 'Level 1 A')})
  228. self.assertTrue({"name": "Level 3 A"} in data)
  229. self.assertTrue({"name": "Level 2 A"} in data)
  230. self.assertFalse({"name": "Level 2 B"} in data)
  231. self.assertFalse({"name": "Level 1 B"} in data)
  232. self.assertFalse({"name": "Level 1 A"} in data)
  233. self.assertFalse({"name": "Root"} in data)
  234. # in ancestors of filter
  235. data = frappe.get_all('Nested DocType', {'name': ('ancestors of', 'Level 2 A')})
  236. self.assertFalse({"name": "Level 3 A"} in data)
  237. self.assertFalse({"name": "Level 2 A"} in data)
  238. self.assertFalse({"name": "Level 2 B"} in data)
  239. self.assertFalse({"name": "Level 1 B"} in data)
  240. self.assertTrue({"name": "Level 1 A"} in data)
  241. self.assertTrue({"name": "Root"} in data)
  242. data = frappe.get_all('Nested DocType', {'name': ('ancestors of', 'Level 1 A')})
  243. self.assertFalse({"name": "Level 3 A"} in data)
  244. self.assertFalse({"name": "Level 2 A"} in data)
  245. self.assertFalse({"name": "Level 2 B"} in data)
  246. self.assertFalse({"name": "Level 1 B"} in data)
  247. self.assertFalse({"name": "Level 1 A"} in data)
  248. self.assertTrue({"name": "Root"} in data)
  249. # not descendants filter
  250. data = frappe.get_all('Nested DocType', {'name': ('not descendants of', 'Level 2 A')})
  251. self.assertFalse({"name": "Level 3 A"} in data)
  252. self.assertTrue({"name": "Level 2 A"} in data)
  253. self.assertTrue({"name": "Level 2 B"} in data)
  254. self.assertTrue({"name": "Level 1 A"} in data)
  255. self.assertTrue({"name": "Root"} in data)
  256. data = frappe.get_all('Nested DocType', {'name': ('not descendants of', 'Level 1 A')})
  257. self.assertFalse({"name": "Level 3 A"} in data)
  258. self.assertFalse({"name": "Level 2 A"} in data)
  259. self.assertTrue({"name": "Level 2 B"} in data)
  260. self.assertTrue({"name": "Level 1 B"} in data)
  261. self.assertTrue({"name": "Level 1 A"} in data)
  262. self.assertTrue({"name": "Root"} in data)
  263. # not ancestors of filter
  264. data = frappe.get_all('Nested DocType', {'name': ('not ancestors of', 'Level 2 A')})
  265. self.assertTrue({"name": "Level 3 A"} in data)
  266. self.assertTrue({"name": "Level 2 A"} in data)
  267. self.assertTrue({"name": "Level 2 B"} in data)
  268. self.assertTrue({"name": "Level 1 B"} in data)
  269. self.assertTrue({"name": "Level 1 A"} not in data)
  270. self.assertTrue({"name": "Root"} not in data)
  271. data = frappe.get_all('Nested DocType', {'name': ('not ancestors of', 'Level 1 A')})
  272. self.assertTrue({"name": "Level 3 A"} in data)
  273. self.assertTrue({"name": "Level 2 A"} in data)
  274. self.assertTrue({"name": "Level 2 B"} in data)
  275. self.assertTrue({"name": "Level 1 B"} in data)
  276. self.assertTrue({"name": "Level 1 A"} in data)
  277. self.assertFalse({"name": "Root"} in data)
  278. data = frappe.get_all('Nested DocType', {'name': ('ancestors of', 'Root')})
  279. self.assertTrue(len(data) == 0)
  280. self.assertTrue(len(frappe.get_all('Nested DocType', {'name': ('not ancestors of', 'Root')})) == len(frappe.get_all('Nested DocType')))
  281. def test_is_set_is_not_set(self):
  282. res = DatabaseQuery('DocType').execute(filters={'autoname': ['is', 'not set']})
  283. self.assertTrue({'name': 'Integration Request'} in res)
  284. self.assertTrue({'name': 'User'} in res)
  285. self.assertFalse({'name': 'Blogger'} in res)
  286. res = DatabaseQuery('DocType').execute(filters={'autoname': ['is', 'set']})
  287. self.assertTrue({'name': 'DocField'} in res)
  288. self.assertTrue({'name': 'Prepared Report'} in res)
  289. self.assertFalse({'name': 'Property Setter'} in res)
  290. def test_set_field_tables(self):
  291. # Tests _in_standard_sql_methods method in test_set_field_tables
  292. # The following query will break if the above method is broken
  293. data = frappe.db.get_list("Web Form",
  294. filters=[['Web Form Field', 'reqd', '=', 1]],
  295. group_by='amount_field',
  296. fields=['count(*) as count', '`amount_field` as name'],
  297. order_by='count desc',
  298. limit=50,
  299. )
  300. def test_pluck_name(self):
  301. names = DatabaseQuery("DocType").execute(filters={"name": "DocType"}, pluck="name")
  302. self.assertEqual(names, ["DocType"])
  303. def test_pluck_any_field(self):
  304. owners = DatabaseQuery("DocType").execute(filters={"name": "DocType"}, pluck="owner")
  305. self.assertEqual(owners, ["Administrator"])
  306. def test_prepare_select_args(self):
  307. # frappe.get_all inserts modified field into order_by clause
  308. # test to make sure this is inserted into select field when postgres
  309. doctypes = frappe.get_all("DocType",
  310. filters={"docstatus": 0, "document_type": ("!=", "")},
  311. group_by="document_type",
  312. fields=["document_type", "sum(is_submittable) as is_submittable"],
  313. limit=1,
  314. as_list=True,
  315. )
  316. if frappe.conf.db_type == "mariadb":
  317. self.assertTrue(len(doctypes[0]) == 2)
  318. else:
  319. self.assertTrue(len(doctypes[0]) == 3)
  320. self.assertTrue(isinstance(doctypes[0][2], datetime.datetime))
  321. def test_column_comparison(self):
  322. """Test DatabaseQuery.execute to test column comparison
  323. """
  324. users_unedited = frappe.get_all(
  325. "User",
  326. filters={"creation": Column("modified")},
  327. fields=["name", "creation", "modified"],
  328. limit=1,
  329. )
  330. users_edited = frappe.get_all(
  331. "User",
  332. filters={"creation": ("!=", Column("modified"))},
  333. fields=["name", "creation", "modified"],
  334. limit=1,
  335. )
  336. self.assertEqual(users_unedited[0].modified, users_unedited[0].creation)
  337. self.assertNotEqual(users_edited[0].modified, users_edited[0].creation)
  338. def test_reportview_get(self):
  339. user = frappe.get_doc("User", "test@example.com")
  340. add_child_table_to_blog_post()
  341. user_roles = frappe.get_roles()
  342. user.remove_roles(*user_roles)
  343. user.add_roles("Blogger")
  344. make_property_setter("Blog Post", "published", "permlevel", 1, "Int")
  345. reset("Blog Post")
  346. add("Blog Post", "Website Manager", 1)
  347. update("Blog Post", "Website Manager", 1, "write", 1)
  348. frappe.set_user(user.name)
  349. frappe.local.request = frappe._dict()
  350. frappe.local.request.method = "POST"
  351. frappe.local.form_dict = frappe._dict({
  352. "doctype": "Blog Post",
  353. "fields": ["published", "title", "`tabTest Child`.`test_field`"],
  354. })
  355. # even if * is passed, fields which are not accessible should be filtered out
  356. response = execute_cmd("frappe.desk.reportview.get")
  357. self.assertListEqual(response["keys"], ["title"])
  358. frappe.local.form_dict = frappe._dict({
  359. "doctype": "Blog Post",
  360. "fields": ["*"],
  361. })
  362. response = execute_cmd("frappe.desk.reportview.get")
  363. self.assertNotIn("published", response["keys"])
  364. frappe.set_user("Administrator")
  365. user.add_roles("Website Manager")
  366. frappe.set_user(user.name)
  367. frappe.set_user("Administrator")
  368. # Admin should be able to see access all fields
  369. frappe.local.form_dict = frappe._dict({
  370. "doctype": "Blog Post",
  371. "fields": ["published", "title", "`tabTest Child`.`test_field`"],
  372. })
  373. response = execute_cmd("frappe.desk.reportview.get")
  374. self.assertListEqual(response["keys"], ['published', 'title', 'test_field'])
  375. # reset user roles
  376. user.remove_roles("Blogger", "Website Manager")
  377. user.add_roles(*user_roles)
  378. def test_reportview_get_aggregation(self):
  379. # test aggregation based on child table field
  380. frappe.local.form_dict = frappe._dict({
  381. "doctype": "DocType",
  382. "fields": """["`tabDocField`.`label` as field_label","`tabDocField`.`name` as field_name"]""",
  383. "filters": "[]",
  384. "order_by": "_aggregate_column desc",
  385. "start": 0,
  386. "page_length": 20,
  387. "view": "Report",
  388. "with_comment_count": 0,
  389. "group_by": "field_label, field_name",
  390. "aggregate_on_field": "columns",
  391. "aggregate_on_doctype": "DocField",
  392. "aggregate_function": "sum"
  393. })
  394. response = execute_cmd("frappe.desk.reportview.get")
  395. self.assertListEqual(response["keys"], ["field_label", "field_name", "_aggregate_column", 'columns'])
  396. def add_child_table_to_blog_post():
  397. child_table = frappe.get_doc({
  398. 'doctype': 'DocType',
  399. 'istable': 1,
  400. 'custom': 1,
  401. 'name': 'Test Child',
  402. 'module': 'Custom',
  403. 'autoname': 'Prompt',
  404. 'fields': [{
  405. 'fieldname': 'test_field',
  406. 'fieldtype': 'Data',
  407. 'permlevel': 1
  408. }],
  409. })
  410. child_table.insert(ignore_permissions=True, ignore_if_duplicate=True)
  411. clear_custom_fields('Blog Post')
  412. add_custom_field('Blog Post', 'child_table', 'Table', child_table.name)
  413. def create_event(subject="_Test Event", starts_on=None):
  414. """ create a test event """
  415. from frappe.utils import get_datetime
  416. event = frappe.get_doc({
  417. "doctype": "Event",
  418. "subject": subject,
  419. "event_type": "Public",
  420. "starts_on": get_datetime(starts_on),
  421. }).insert(ignore_permissions=True)
  422. return event
  423. def create_nested_doctype():
  424. if frappe.db.exists('DocType', 'Nested DocType'):
  425. return
  426. frappe.get_doc({
  427. 'doctype': 'DocType',
  428. 'name': 'Nested DocType',
  429. 'module': 'Custom',
  430. 'is_tree': 1,
  431. 'custom': 1,
  432. 'autoname': 'Prompt',
  433. 'fields': [
  434. {'label': 'Description', 'fieldname': 'description'}
  435. ],
  436. 'permissions': [
  437. {'role': 'Blogger'}
  438. ]
  439. }).insert()
  440. def create_nested_doctype_records():
  441. '''
  442. Create a structure like:
  443. - Root
  444. - Level 1 A
  445. - Level 2 A
  446. - Level 3 A
  447. - Level 1 B
  448. - Level 2 B
  449. '''
  450. records = [
  451. {'name': 'Root', 'is_group': 1},
  452. {'name': 'Level 1 A', 'parent_nested_doctype': 'Root', 'is_group': 1},
  453. {'name': 'Level 2 A', 'parent_nested_doctype': 'Level 1 A', 'is_group': 1},
  454. {'name': 'Level 3 A', 'parent_nested_doctype': 'Level 2 A'},
  455. {'name': 'Level 1 B', 'parent_nested_doctype': 'Root', 'is_group': 1},
  456. {'name': 'Level 2 B', 'parent_nested_doctype': 'Level 1 B'},
  457. ]
  458. for r in records:
  459. d = frappe.new_doc('Nested DocType')
  460. d.update(r)
  461. d.insert(ignore_permissions=True, ignore_if_duplicate=True)