diff --git a/frappe/__init__.py b/frappe/__init__.py index 6979bcae14..2361500eab 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -14,7 +14,7 @@ import os, sys, importlib, inspect, json from .exceptions import * from .utils.jinja import get_jenv, get_template, render_template, get_email_from_template -__version__ = '8.7.1' +__version__ = '8.7.2' __title__ = "Frappe Framework" local = Local() diff --git a/frappe/core/doctype/user/test_user.py b/frappe/core/doctype/user/test_user.py index b2206a8d37..c9ed243841 100644 --- a/frappe/core/doctype/user/test_user.py +++ b/frappe/core/doctype/user/test_user.py @@ -200,6 +200,28 @@ class TestUser(unittest.TestCase): clear_limit("expiry") frappe.local.conf = _dict(frappe.get_site_config()) + def test_delete_user(self): + new_user = frappe.get_doc(dict(doctype='User', email='test-for-delete@example.com', + first_name='Tester Delete User')).insert() + self.assertEquals(new_user.user_type, 'Website User') + + # role with desk access + new_user.add_roles('_Test Role 2') + new_user.save() + self.assertEquals(new_user.user_type, 'System User') + + comm = frappe.get_doc({ + "doctype":"Communication", + "subject": "To check user able to delete even if linked with communication", + "content": "To check user able to delete even if linked with communication", + "sent_or_received": "Sent", + "user": new_user.name + }) + comm.insert(ignore_permissions=True) + + frappe.delete_doc('User', new_user.name) + self.assertFalse(frappe.db.exists('User', new_user.name)) + def test_deactivate_additional_users(self): update_limits({'users': get_total_users()+1}) diff --git a/frappe/email/email_body.py b/frappe/email/email_body.py index a94163ed20..12deec3427 100755 --- a/frappe/email/email_body.py +++ b/frappe/email/email_body.py @@ -324,7 +324,7 @@ def add_attachment(fname, fcontent, content_type=None, # Set the filename parameter if fname: attachment_type = 'inline' if inline else 'attachment' - part.add_header(b'Content-Disposition', attachment_type, filename=fname.encode('utf=8')) + part.add_header(b'Content-Disposition', attachment_type, filename=text_type(fname)) if content_id: part.add_header(b'Content-ID', '<{0}>'.format(content_id)) diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index e242a48c88..3e4f60c7f6 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -183,13 +183,18 @@ def check_if_doc_is_linked(doc, method="Delete"): if not issingle: for item in frappe.db.get_values(link_dt, {link_field:doc.name}, ["name", "parent", "parenttype", "docstatus"], as_dict=True): + linked_doctype = item.parenttype if item.parent else link_dt + if linked_doctype in ("Communication", "ToDo", "DocShare", "Email Unsubscribe", 'File', 'Version'): + # don't check for communication and todo! + continue + if item and ((item.parent or item.name) != doc.name) \ and ((method=="Delete" and item.docstatus<2) or (method=="Cancel" and item.docstatus==1)): # raise exception only if # linked to an non-cancelled doc when deleting # or linked to a submitted doc when cancelling frappe.throw(_('Cannot delete or cancel because {0} {1} is linked with {2} {3}') - .format(doc.doctype, doc.name, item.parenttype if item.parent else link_dt, + .format(doc.doctype, doc.name, linked_doctype, item.parent or item.name), frappe.LinkExistsError) def check_if_doc_is_dynamically_linked(doc, method="Delete"):