選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 
 
 

542 行
18 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. from frappe import _, bold
  6. from frappe.utils import cint
  7. from frappe.model.naming import validate_name
  8. from frappe.model.dynamic_links import get_dynamic_link_map
  9. from frappe.utils.password import rename_password
  10. from frappe.model.utils.user_settings import sync_user_settings, update_user_settings_data
  11. @frappe.whitelist()
  12. def update_document_title(doctype, docname, title_field=None, old_title=None, new_title=None, new_name=None, merge=False):
  13. """
  14. Update title from header in form view
  15. """
  16. if docname and new_name and not docname == new_name:
  17. docname = rename_doc(doctype=doctype, old=docname, new=new_name, merge=merge)
  18. if old_title and new_title and not old_title == new_title:
  19. frappe.db.set_value(doctype, docname, title_field, new_title)
  20. frappe.msgprint(_('Saved'), alert=True, indicator='green')
  21. return docname
  22. def rename_doc(doctype, old, new, force=False, merge=False, ignore_permissions=False, ignore_if_exists=False, show_alert=True):
  23. """
  24. Renames a doc(dt, old) to doc(dt, new) and
  25. updates all linked fields of type "Link"
  26. """
  27. if not frappe.db.exists(doctype, old):
  28. return
  29. if ignore_if_exists and frappe.db.exists(doctype, new):
  30. return
  31. if old==new:
  32. frappe.msgprint(_('Please select a new name to rename'))
  33. return
  34. force = cint(force)
  35. merge = cint(merge)
  36. meta = frappe.get_meta(doctype)
  37. # call before_rename
  38. old_doc = frappe.get_doc(doctype, old)
  39. out = old_doc.run_method("before_rename", old, new, merge) or {}
  40. new = (out.get("new") or new) if isinstance(out, dict) else (out or new)
  41. if doctype != "DocType":
  42. new = validate_rename(doctype, new, meta, merge, force, ignore_permissions)
  43. if not merge:
  44. rename_parent_and_child(doctype, old, new, meta)
  45. else:
  46. update_assignments(old, new, doctype)
  47. # update link fields' values
  48. link_fields = get_link_fields(doctype)
  49. update_link_field_values(link_fields, old, new, doctype)
  50. rename_dynamic_links(doctype, old, new)
  51. # save the user settings in the db
  52. update_user_settings(old, new, link_fields)
  53. if doctype=='DocType':
  54. rename_doctype(doctype, old, new, force)
  55. update_attachments(doctype, old, new)
  56. rename_versions(doctype, old, new)
  57. # call after_rename
  58. new_doc = frappe.get_doc(doctype, new)
  59. # copy any flags if required
  60. new_doc._local = getattr(old_doc, "_local", None)
  61. new_doc.run_method("after_rename", old, new, merge)
  62. if not merge:
  63. rename_password(doctype, old, new)
  64. # update user_permissions
  65. frappe.db.sql("""UPDATE `tabDefaultValue` SET `defvalue`=%s WHERE `parenttype`='User Permission'
  66. AND `defkey`=%s AND `defvalue`=%s""", (new, doctype, old))
  67. if merge:
  68. new_doc.add_comment('Edit', _("merged {0} into {1}").format(frappe.bold(old), frappe.bold(new)))
  69. else:
  70. new_doc.add_comment('Edit', _("renamed from {0} to {1}").format(frappe.bold(old), frappe.bold(new)))
  71. if merge:
  72. frappe.delete_doc(doctype, old)
  73. frappe.clear_cache()
  74. frappe.enqueue('frappe.utils.global_search.rebuild_for_doctype', doctype=doctype)
  75. if show_alert:
  76. frappe.msgprint(_('Document renamed from {0} to {1}').format(bold(old), bold(new)), alert=True, indicator='green')
  77. return new
  78. def update_assignments(old, new, doctype):
  79. old_assignments = frappe.parse_json(frappe.db.get_value(doctype, old, '_assign')) or []
  80. new_assignments = frappe.parse_json(frappe.db.get_value(doctype, new, '_assign')) or []
  81. common_assignments = list(set(old_assignments).intersection(new_assignments))
  82. for user in common_assignments:
  83. # delete todos linked to old doc
  84. todos = frappe.db.get_all('ToDo',
  85. {
  86. 'owner': user,
  87. 'reference_type': doctype,
  88. 'reference_name': old,
  89. },
  90. ['name', 'description']
  91. )
  92. for todo in todos:
  93. frappe.delete_doc('ToDo', todo.name)
  94. unique_assignments = list(set(old_assignments + new_assignments))
  95. frappe.db.set_value(doctype, new, '_assign', frappe.as_json(unique_assignments, indent=0))
  96. def update_user_settings(old, new, link_fields):
  97. '''
  98. Update the user settings of all the linked doctypes while renaming.
  99. '''
  100. # store the user settings data from the redis to db
  101. sync_user_settings()
  102. if not link_fields: return
  103. # find the user settings for the linked doctypes
  104. linked_doctypes = set([d.parent for d in link_fields if not d.issingle])
  105. user_settings_details = frappe.db.sql('''SELECT `user`, `doctype`, `data`
  106. FROM `__UserSettings`
  107. WHERE `data` like %s
  108. AND `doctype` IN ('{doctypes}')'''.format(doctypes="', '".join(linked_doctypes)), (old), as_dict=1)
  109. # create the dict using the doctype name as key and values as list of the user settings
  110. from collections import defaultdict
  111. user_settings_dict = defaultdict(list)
  112. for user_setting in user_settings_details:
  113. user_settings_dict[user_setting.doctype].append(user_setting)
  114. # update the name in linked doctype whose user settings exists
  115. for fields in link_fields:
  116. user_settings = user_settings_dict.get(fields.parent)
  117. if user_settings:
  118. for user_setting in user_settings:
  119. update_user_settings_data(user_setting, "value", old, new, "docfield", fields.fieldname)
  120. else:
  121. continue
  122. def update_attachments(doctype, old, new):
  123. try:
  124. if old != "File Data" and doctype != "DocType":
  125. frappe.db.sql("""update `tabFile` set attached_to_name=%s
  126. where attached_to_name=%s and attached_to_doctype=%s""", (new, old, doctype))
  127. except frappe.db.ProgrammingError as e:
  128. if not frappe.db.is_column_missing(e):
  129. raise
  130. def rename_versions(doctype, old, new):
  131. frappe.db.sql("""UPDATE `tabVersion` SET `docname`=%s WHERE `ref_doctype`=%s AND `docname`=%s""",
  132. (new, doctype, old))
  133. def rename_parent_and_child(doctype, old, new, meta):
  134. # rename the doc
  135. frappe.db.sql("UPDATE `tab{0}` SET `name`={1} WHERE `name`={1}".format(doctype, '%s'), (new, old))
  136. update_autoname_field(doctype, new, meta)
  137. update_child_docs(old, new, meta)
  138. def update_autoname_field(doctype, new, meta):
  139. # update the value of the autoname field on rename of the docname
  140. if meta.get('autoname'):
  141. field = meta.get('autoname').split(':')
  142. if field and field[0] == "field":
  143. frappe.db.sql("UPDATE `tab{0}` SET `{1}`={2} WHERE `name`={2}".format(doctype, field[1], '%s'), (new, new))
  144. def validate_rename(doctype, new, meta, merge, force, ignore_permissions):
  145. # using for update so that it gets locked and someone else cannot edit it while this rename is going on!
  146. exists = frappe.db.sql("select name from `tab{doctype}` where name=%s for update".format(doctype=doctype), new)
  147. exists = exists[0][0] if exists else None
  148. if merge and not exists:
  149. frappe.msgprint(_("{0} {1} does not exist, select a new target to merge").format(doctype, new), raise_exception=1)
  150. if exists and exists != new:
  151. # for fixing case, accents
  152. exists = None
  153. if (not merge) and exists:
  154. frappe.msgprint(_("Another {0} with name {1} exists, select another name").format(doctype, new), raise_exception=1)
  155. if not (ignore_permissions or frappe.permissions.has_permission(doctype, "write", raise_exception=False)):
  156. frappe.msgprint(_("You need write permission to rename"), raise_exception=1)
  157. if not (force or ignore_permissions) and not meta.allow_rename:
  158. frappe.msgprint(_("{0} not allowed to be renamed").format(_(doctype)), raise_exception=1)
  159. # validate naming like it's done in doc.py
  160. new = validate_name(doctype, new, merge=merge)
  161. return new
  162. def rename_doctype(doctype, old, new, force=False):
  163. # change options for fieldtype Table, Table MultiSelect and Link
  164. fields_with_options = ("Link",) + frappe.model.table_fields
  165. for fieldtype in fields_with_options:
  166. update_options_for_fieldtype(fieldtype, old, new)
  167. # change options where select options are hardcoded i.e. listed
  168. select_fields = get_select_fields(old, new)
  169. update_link_field_values(select_fields, old, new, doctype)
  170. update_select_field_values(old, new)
  171. # change parenttype for fieldtype Table
  172. update_parenttype_values(old, new)
  173. def update_child_docs(old, new, meta):
  174. # update "parent"
  175. for df in meta.get_table_fields():
  176. frappe.db.sql("update `tab%s` set parent=%s where parent=%s" \
  177. % (df.options, '%s', '%s'), (new, old))
  178. def update_link_field_values(link_fields, old, new, doctype):
  179. for field in link_fields:
  180. if field['issingle']:
  181. try:
  182. single_doc = frappe.get_doc(field['parent'])
  183. if single_doc.get(field['fieldname'])==old:
  184. single_doc.set(field['fieldname'], new)
  185. # update single docs using ORM rather then query
  186. # as single docs also sometimes sets defaults!
  187. single_doc.flags.ignore_mandatory = True
  188. single_doc.save(ignore_permissions=True)
  189. except ImportError:
  190. # fails in patches where the doctype has been renamed
  191. # or no longer exists
  192. pass
  193. else:
  194. # because the table hasn't been renamed yet!
  195. parent = field['parent'] if field['parent']!=new else old
  196. frappe.db.sql("""
  197. update `tab{table_name}` set `{fieldname}`=%s
  198. where `{fieldname}`=%s""".format(
  199. table_name=parent,
  200. fieldname=field['fieldname']), (new, old))
  201. # update cached link_fields as per new
  202. if doctype=='DocType' and field['parent'] == old:
  203. field['parent'] = new
  204. def get_link_fields(doctype):
  205. # get link fields from tabDocField
  206. if not frappe.flags.link_fields:
  207. frappe.flags.link_fields = {}
  208. if not doctype in frappe.flags.link_fields:
  209. link_fields = frappe.db.sql("""\
  210. select parent, fieldname,
  211. (select issingle from tabDocType dt
  212. where dt.name = df.parent) as issingle
  213. from tabDocField df
  214. where
  215. df.options=%s and df.fieldtype='Link'""", (doctype,), as_dict=1)
  216. # get link fields from tabCustom Field
  217. custom_link_fields = frappe.db.sql("""\
  218. select dt as parent, fieldname,
  219. (select issingle from tabDocType dt
  220. where dt.name = df.dt) as issingle
  221. from `tabCustom Field` df
  222. where
  223. df.options=%s and df.fieldtype='Link'""", (doctype,), as_dict=1)
  224. # add custom link fields list to link fields list
  225. link_fields += custom_link_fields
  226. # remove fields whose options have been changed using property setter
  227. property_setter_link_fields = frappe.db.sql("""\
  228. select ps.doc_type as parent, ps.field_name as fieldname,
  229. (select issingle from tabDocType dt
  230. where dt.name = ps.doc_type) as issingle
  231. from `tabProperty Setter` ps
  232. where
  233. ps.property_type='options' and
  234. ps.field_name is not null and
  235. ps.value=%s""", (doctype,), as_dict=1)
  236. link_fields += property_setter_link_fields
  237. frappe.flags.link_fields[doctype] = link_fields
  238. return frappe.flags.link_fields[doctype]
  239. def update_options_for_fieldtype(fieldtype, old, new):
  240. if frappe.conf.developer_mode:
  241. for name in frappe.db.sql_list("""select parent from
  242. tabDocField where options=%s""", old):
  243. doctype = frappe.get_doc("DocType", name)
  244. save = False
  245. for f in doctype.fields:
  246. if f.options == old:
  247. f.options = new
  248. save = True
  249. if save:
  250. doctype.save()
  251. else:
  252. frappe.db.sql("""update `tabDocField` set options=%s
  253. where fieldtype=%s and options=%s""", (new, fieldtype, old))
  254. frappe.db.sql("""update `tabCustom Field` set options=%s
  255. where fieldtype=%s and options=%s""", (new, fieldtype, old))
  256. frappe.db.sql("""update `tabProperty Setter` set value=%s
  257. where property='options' and value=%s""", (new, old))
  258. def get_select_fields(old, new):
  259. """
  260. get select type fields where doctype's name is hardcoded as
  261. new line separated list
  262. """
  263. # get link fields from tabDocField
  264. select_fields = frappe.db.sql("""
  265. select parent, fieldname,
  266. (select issingle from tabDocType dt
  267. where dt.name = df.parent) as issingle
  268. from tabDocField df
  269. where
  270. df.parent != %s and df.fieldtype = 'Select' and
  271. df.options like {0} """.format(frappe.db.escape('%' + old + '%')), (new,), as_dict=1)
  272. # get link fields from tabCustom Field
  273. custom_select_fields = frappe.db.sql("""
  274. select dt as parent, fieldname,
  275. (select issingle from tabDocType dt
  276. where dt.name = df.dt) as issingle
  277. from `tabCustom Field` df
  278. where
  279. df.dt != %s and df.fieldtype = 'Select' and
  280. df.options like {0} """ .format(frappe.db.escape('%' + old + '%')), (new,), as_dict=1)
  281. # add custom link fields list to link fields list
  282. select_fields += custom_select_fields
  283. # remove fields whose options have been changed using property setter
  284. property_setter_select_fields = frappe.db.sql("""
  285. select ps.doc_type as parent, ps.field_name as fieldname,
  286. (select issingle from tabDocType dt
  287. where dt.name = ps.doc_type) as issingle
  288. from `tabProperty Setter` ps
  289. where
  290. ps.doc_type != %s and
  291. ps.property_type='options' and
  292. ps.field_name is not null and
  293. ps.value like {0} """.format(frappe.db.escape('%' + old + '%')), (new,), as_dict=1)
  294. select_fields += property_setter_select_fields
  295. return select_fields
  296. def update_select_field_values(old, new):
  297. frappe.db.sql("""
  298. update `tabDocField` set options=replace(options, %s, %s)
  299. where
  300. parent != %s and fieldtype = 'Select' and
  301. (options like {0} or options like {1})"""
  302. .format(frappe.db.escape('%' + '\n' + old + '%'), frappe.db.escape('%' + old + '\n' + '%')), (old, new, new))
  303. frappe.db.sql("""
  304. update `tabCustom Field` set options=replace(options, %s, %s)
  305. where
  306. dt != %s and fieldtype = 'Select' and
  307. (options like {0} or options like {1})"""
  308. .format(frappe.db.escape('%' + '\n' + old + '%'), frappe.db.escape('%' + old + '\n' + '%')), (old, new, new))
  309. frappe.db.sql("""
  310. update `tabProperty Setter` set value=replace(value, %s, %s)
  311. where
  312. doc_type != %s and field_name is not null and
  313. property='options' and
  314. (value like {0} or value like {1})"""
  315. .format(frappe.db.escape('%' + '\n' + old + '%'), frappe.db.escape('%' + old + '\n' + '%')), (old, new, new))
  316. def update_parenttype_values(old, new):
  317. child_doctypes = frappe.db.get_all('DocField',
  318. fields=['options', 'fieldname'],
  319. filters={
  320. 'parent': new,
  321. 'fieldtype': ['in', frappe.model.table_fields]
  322. }
  323. )
  324. custom_child_doctypes = frappe.db.get_all('Custom Field',
  325. fields=['options', 'fieldname'],
  326. filters={
  327. 'dt': new,
  328. 'fieldtype': ['in', frappe.model.table_fields]
  329. }
  330. )
  331. child_doctypes += custom_child_doctypes
  332. fields = [d['fieldname'] for d in child_doctypes]
  333. property_setter_child_doctypes = frappe.db.sql("""\
  334. select value as options from `tabProperty Setter`
  335. where doc_type=%s and property='options' and
  336. field_name in ("%s")""" % ('%s', '", "'.join(fields)),
  337. (new,))
  338. child_doctypes += property_setter_child_doctypes
  339. child_doctypes = (d['options'] for d in child_doctypes)
  340. for doctype in child_doctypes:
  341. frappe.db.sql("""\
  342. update `tab%s` set parenttype=%s
  343. where parenttype=%s""" % (doctype, '%s', '%s'),
  344. (new, old))
  345. def rename_dynamic_links(doctype, old, new):
  346. for df in get_dynamic_link_map().get(doctype, []):
  347. # dynamic link in single, just one value to check
  348. if frappe.get_meta(df.parent).issingle:
  349. refdoc = frappe.db.get_singles_dict(df.parent)
  350. if refdoc.get(df.options)==doctype and refdoc.get(df.fieldname)==old:
  351. frappe.db.sql("""update tabSingles set value=%s where
  352. field=%s and value=%s and doctype=%s""", (new, df.fieldname, old, df.parent))
  353. else:
  354. # because the table hasn't been renamed yet!
  355. parent = df.parent if df.parent != new else old
  356. frappe.db.sql("""update `tab{parent}` set {fieldname}=%s
  357. where {options}=%s and {fieldname}=%s""".format(parent = parent,
  358. fieldname=df.fieldname, options=df.options), (new, doctype, old))
  359. def bulk_rename(doctype, rows=None, via_console = False):
  360. """Bulk rename documents
  361. :param doctype: DocType to be renamed
  362. :param rows: list of documents as `((oldname, newname), ..)`"""
  363. if not rows:
  364. frappe.throw(_("Please select a valid csv file with data"))
  365. if not via_console:
  366. max_rows = 500
  367. if len(rows) > max_rows:
  368. frappe.throw(_("Maximum {0} rows allowed").format(max_rows))
  369. rename_log = []
  370. for row in rows:
  371. # if row has some content
  372. if len(row) > 1 and row[0] and row[1]:
  373. try:
  374. if rename_doc(doctype, row[0], row[1]):
  375. msg = _("Successful: {0} to {1}").format(row[0], row[1])
  376. frappe.db.commit()
  377. else:
  378. msg = _("Ignored: {0} to {1}").format(row[0], row[1])
  379. except Exception as e:
  380. msg = _("** Failed: {0} to {1}: {2}").format(row[0], row[1], repr(e))
  381. frappe.db.rollback()
  382. if via_console:
  383. print(msg)
  384. else:
  385. rename_log.append(msg)
  386. frappe.enqueue('frappe.utils.global_search.rebuild_for_doctype', doctype=doctype)
  387. if not via_console:
  388. return rename_log
  389. def update_linked_doctypes(doctype, docname, linked_to, value, ignore_doctypes=None):
  390. """
  391. linked_doctype_info_list = list formed by get_fetch_fields() function
  392. docname = Master DocType's name in which modification are made
  393. value = Value for the field thats set in other DocType's by fetching from Master DocType
  394. """
  395. linked_doctype_info_list = get_fetch_fields(doctype, linked_to, ignore_doctypes)
  396. for d in linked_doctype_info_list:
  397. frappe.db.sql("""
  398. update
  399. `tab{doctype}`
  400. set
  401. {linked_to_fieldname} = "{value}"
  402. where
  403. {master_fieldname} = {docname}
  404. and {linked_to_fieldname} != "{value}"
  405. """.format(
  406. doctype = d['doctype'],
  407. linked_to_fieldname = d['linked_to_fieldname'],
  408. value = value,
  409. master_fieldname = d['master_fieldname'],
  410. docname = frappe.db.escape(docname)
  411. ))
  412. def get_fetch_fields(doctype, linked_to, ignore_doctypes=None):
  413. """
  414. doctype = Master DocType in which the changes are being made
  415. linked_to = DocType name of the field thats being updated in Master
  416. This function fetches list of all DocType where both doctype and linked_to is found
  417. as link fields.
  418. Forms a list of dict in the form -
  419. [{doctype: , master_fieldname: , linked_to_fieldname: ]
  420. where
  421. doctype = DocType where changes need to be made
  422. master_fieldname = Fieldname where options = doctype
  423. linked_to_fieldname = Fieldname where options = linked_to
  424. """
  425. master_list = get_link_fields(doctype)
  426. linked_to_list = get_link_fields(linked_to)
  427. out = []
  428. from itertools import product
  429. product_list = product(master_list, linked_to_list)
  430. for d in product_list:
  431. linked_doctype_info = frappe._dict()
  432. if d[0]['parent'] == d[1]['parent'] \
  433. and (not ignore_doctypes or d[0]['parent'] not in ignore_doctypes) \
  434. and not d[1]['issingle']:
  435. linked_doctype_info['doctype'] = d[0]['parent']
  436. linked_doctype_info['master_fieldname'] = d[0]['fieldname']
  437. linked_doctype_info['linked_to_fieldname'] = d[1]['fieldname']
  438. out.append(linked_doctype_info)
  439. return out