diff --git a/frappe/__init__.py b/frappe/__init__.py index 8ea5148873..dc524cd908 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -23,7 +23,7 @@ if sys.version[0] == '2': reload(sys) sys.setdefaultencoding("utf-8") -__version__ = '12.0.14' +__version__ = '12.0.15' __title__ = "Frappe Framework" local = Local() @@ -1039,7 +1039,13 @@ def get_newargs(fn, kwargs): if hasattr(fn, 'fnargs'): fnargs = fn.fnargs else: - fnargs, varargs, varkw, defaults = inspect.getargspec(fn) + try: + fnargs, varargs, varkw, defaults = inspect.getargspec(fn) + except ValueError: + fnargs = inspect.getfullargspec(fn).args + varargs = inspect.getfullargspec(fn).varargs + varkw = inspect.getfullargspec(fn).varkw + defaults = inspect.getfullargspec(fn).defaults newargs = {} for a in kwargs: @@ -1409,8 +1415,9 @@ def publish_progress(*args, **kwargs): :param percent: Percent progress :param title: Title - :param doctype: Optional, for DocType - :param name: Optional, for Document name + :param doctype: Optional, for document type + :param docname: Optional, for document name + :param description: Optional description """ import frappe.realtime return frappe.realtime.publish_progress(*args, **kwargs) diff --git a/frappe/core/doctype/role/test_role.py b/frappe/core/doctype/role/test_role.py index 513a68c04d..31efb5d4e8 100644 --- a/frappe/core/doctype/role/test_role.py +++ b/frappe/core/doctype/role/test_role.py @@ -10,20 +10,16 @@ test_records = frappe.get_test_records('Role') class TestUser(unittest.TestCase): def test_disable_role(self): frappe.get_doc("User", "test@example.com").add_roles("_Test Role 3") - + role = frappe.get_doc("Role", "_Test Role 3") role.disabled = 1 role.save() - + self.assertTrue("_Test Role 3" not in frappe.get_roles("test@example.com")) - - frappe.get_doc("User", "test@example.com").add_roles("_Test Role 3") - self.assertTrue("_Test Role 3" not in frappe.get_roles("test@example.com")) - + role = frappe.get_doc("Role", "_Test Role 3") role.disabled = 0 role.save() - + frappe.get_doc("User", "test@example.com").add_roles("_Test Role 3") self.assertTrue("_Test Role 3" in frappe.get_roles("test@example.com")) - \ No newline at end of file diff --git a/frappe/core/doctype/user/user.json b/frappe/core/doctype/user/user.json index 9376c8d0b5..b98935b4f3 100644 --- a/frappe/core/doctype/user/user.json +++ b/frappe/core/doctype/user/user.json @@ -38,7 +38,6 @@ "mute_sounds", "change_password", "new_password", - "send_password_update_notification", "logout_all_sessions", "reset_password_key", "last_password_reset_date", @@ -299,13 +298,6 @@ "label": "Set New Password", "no_copy": 1 }, - { - "default": "0", - "depends_on": "eval:!doc.__islocal", - "fieldname": "send_password_update_notification", - "fieldtype": "Check", - "label": "Send Password Update Notification" - }, { "default": "0", "fieldname": "logout_all_sessions", @@ -593,7 +585,7 @@ "idx": 413, "image_field": "user_image", "max_attachments": 5, - "modified": "2019-08-09 10:34:56.912283", + "modified": "2019-09-18 14:14:01.233124", "modified_by": "Administrator", "module": "Core", "name": "User", diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index cd754aef3a..dcbf3ee35a 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -153,10 +153,6 @@ class User(Document): if new_password and not self.flags.in_insert: _update_password(user=self.name, pwd=new_password, logout_all_sessions=self.logout_all_sessions) - if self.send_password_update_notification and self.enabled: - self.password_update_mail(new_password) - frappe.msgprint(_("New password emailed")) - def set_system_user(self): '''Set as System User if any of the given roles has desk_access''' if self.has_desk_access() or self.name == 'Administrator': @@ -250,10 +246,6 @@ class User(Document): self.send_login_mail(_("Password Reset"), "password_reset", {"link": link}, now=True) - def password_update_mail(self, password): - self.send_login_mail(_("Password Update"), - "password_update", {"new_password": password}, now=True) - def send_welcome_mail_to_user(self): from frappe.utils import get_url link = self.reset_password() diff --git a/frappe/database/database.py b/frappe/database/database.py index 412051c76f..a1b8d390a9 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -607,7 +607,7 @@ class Database(object): """Update multiple values. Alias for `set_value`.""" return self.set_value(*args, **kwargs) - def set_value(self, dt, dn, field, val, modified=None, modified_by=None, + def set_value(self, dt, dn, field, val=None, modified=None, modified_by=None, update_modified=True, debug=False): """Set a single value in the database, do not call the ORM triggers but update the modified timestamp (unless specified not to). diff --git a/frappe/desk/form/run_method.py b/frappe/desk/form/run_method.py index 0a973c35ed..7952f3b68d 100644 --- a/frappe/desk/form/run_method.py +++ b/frappe/desk/form/run_method.py @@ -31,7 +31,14 @@ def runserverobj(method, docs=None, dt=None, dn=None, arg=None, args=None): except ValueError: args = args - fnargs, varargs, varkw, defaults = inspect.getargspec(getattr(doc, method)) + try: + fnargs, varargs, varkw, defaults = inspect.getargspec(getattr(doc, method)) + except ValueError: + fnargs = inspect.getfullargspec(getattr(doc, method)).args + varargs = inspect.getfullargspec(getattr(doc, method)).varargs + varkw = inspect.getfullargspec(getattr(doc, method)).varkw + defaults = inspect.getfullargspec(getattr(doc, method)).defaults + if not fnargs or (len(fnargs)==1 and fnargs[0]=="self"): r = doc.run_method(method) diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py index 9d6f3561cb..5d1829abb2 100644 --- a/frappe/desk/query_report.py +++ b/frappe/desk/query_report.py @@ -562,7 +562,7 @@ def get_linked_doctypes(columns, data): for idx, col in enumerate(columns): df = columns_dict[idx] if df.get("fieldtype")=="Link": - if isinstance(col, string_types): + if data and isinstance(data[0], (list, tuple)): linked_doctypes[df["options"]] = idx else: # dict diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 6eb2efce6b..3d5b002c75 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -454,6 +454,18 @@ class BaseDocument(object): doctype = df.options if not doctype: frappe.throw(_("Options not set for link field {0}").format(df.fieldname)) + + meta = frappe.get_meta(doctype) + if meta.has_field('disabled'): + if not ( + frappe.flags.in_import + or frappe.flags.in_migrate + or frappe.flags.in_install + or frappe.flags.in_patch + ): + disabled = frappe.get_value(doctype, self.get(df.fieldname), 'disabled') + if disabled: + frappe.throw(_("{0} is disabled").format(frappe.bold(self.get(df.fieldname)))) else: doctype = self.get(df.options) if not doctype: diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index a26c0f2012..2ab0ee8549 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -458,18 +458,6 @@ class DatabaseQuery(object): value = get_between_date_filter(f.value, df) fallback = "'0001-01-01 00:00:00'" - elif df and df.fieldtype=="Date": - value = frappe.db.format_date(f.value) - fallback = "'0001-01-01'" - - elif (df and df.fieldtype=="Datetime") or isinstance(f.value, datetime): - value = frappe.db.format_datetime(f.value) - fallback = "'0001-01-01 00:00:00'" - - elif df and df.fieldtype=="Time": - value = get_time(f.value).strftime("%H:%M:%S.%f") - fallback = "'00:00:00'" - elif f.operator.lower() == "is": if f.value == 'set': f.operator = '!=' @@ -483,6 +471,18 @@ class DatabaseQuery(object): if 'ifnull' not in column_name: column_name = 'ifnull({}, {})'.format(column_name, fallback) + elif df and df.fieldtype=="Date": + value = frappe.db.format_date(f.value) + fallback = "'0001-01-01'" + + elif (df and df.fieldtype=="Datetime") or isinstance(f.value, datetime): + value = frappe.db.format_datetime(f.value) + fallback = "'0001-01-01 00:00:00'" + + elif df and df.fieldtype=="Time": + value = get_time(f.value).strftime("%H:%M:%S.%f") + fallback = "'00:00:00'" + elif f.operator.lower() in ("like", "not like") or (isinstance(f.value, string_types) and (not df or df.fieldtype not in ["Float", "Int", "Currency", "Percent", "Check"])): value = "" if f.value==None else f.value diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index 83ccf2dc87..04a77f65ec 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -75,7 +75,7 @@ def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reloa delete_from_table(doctype, name, ignore_doctypes, None) - if not (frappe.flags.in_migrate or frappe.flags.in_install or frappe.flags.in_test): + if not (for_reload or frappe.flags.in_migrate or frappe.flags.in_install or frappe.flags.in_test): try: delete_controllers(name, doc.module) except (FileNotFoundError, OSError): diff --git a/frappe/model/document.py b/frappe/model/document.py index fd1ffec483..f93c366ffb 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -1232,6 +1232,18 @@ class Document(BaseDocument): frappe.bold(self.meta.get_label(from_date_field)), ), frappe.exceptions.InvalidDates) + def get_assigned_users(self): + assignments = frappe.get_all('ToDo', + fields=['owner'], + filters={ + 'reference_type': self.doctype, + 'reference_name': self.name, + 'status': ('!=', 'Cancelled'), + }) + + users = set([assignment.owner for assignment in assignments]) + return users + def execute_action(doctype, name, action, **kwargs): '''Execute an action on a document (called by background worker)''' doc = frappe.get_doc(doctype, name) diff --git a/frappe/modules/utils.py b/frappe/modules/utils.py index b06297023e..528ca6e88b 100644 --- a/frappe/modules/utils.py +++ b/frappe/modules/utils.py @@ -119,7 +119,7 @@ def sync_customizations_for_doctype(data, folder): custom_doctype, doctype_fieldname), doc_type) for d in data[key]: - _insert(data) + _insert(d) else: for d in data[key]: diff --git a/frappe/social/doctype/energy_point_log/test_energy_point_log.py b/frappe/social/doctype/energy_point_log/test_energy_point_log.py index c44b3b14df..8aef5a3801 100644 --- a/frappe/social/doctype/energy_point_log/test_energy_point_log.py +++ b/frappe/social/doctype/energy_point_log/test_energy_point_log.py @@ -7,6 +7,7 @@ import frappe import unittest from .energy_point_log import get_energy_points as _get_energy_points, create_review_points_log, review from frappe.utils.testutils import add_custom_field, clear_custom_fields +from frappe.desk.form.assign_to import add as assign_to class TestEnergyPointLog(unittest.TestCase): def tearDown(self): @@ -185,7 +186,31 @@ class TestEnergyPointLog(unittest.TestCase): self.assertEquals(points_after_todo_creation, points_before_todo_creation + todo_point_rule.points) -def create_energy_point_rule_for_todo(multiplier_field=None, for_doc_event='Custom', max_points=None): + def test_point_allocation_for_assigned_users(self): + todo = create_a_todo() + + assign_users_to_todo(todo.name, ['test@example.com', 'test2@example.com']) + + test_user_before_points = get_points('test@example.com') + test2_user_before_points = get_points('test2@example.com') + + rule = create_energy_point_rule_for_todo(for_assigned_users=1) + + todo.status = 'Closed' + todo.save() + + test_user_after_points = get_points('test@example.com') + test2_user_after_points = get_points('test2@example.com') + + self.assertEquals(test_user_after_points, + test_user_before_points + rule.points) + + self.assertEquals(test2_user_after_points, + test2_user_before_points + rule.points) + + +def create_energy_point_rule_for_todo(multiplier_field=None, for_doc_event='Custom', + max_points=None, for_assigned_users=0): name = 'ToDo Closed' point_rule = frappe.db.get_all( 'Energy Point Rule', @@ -204,6 +229,7 @@ def create_energy_point_rule_for_todo(multiplier_field=None, for_doc_event='Cust 'condition': 'doc.status == "Closed"', 'for_doc_event': for_doc_event, 'user_field': 'owner', + 'for_assigned_users': for_assigned_users, 'multiplier_field': multiplier_field, 'max_points': max_points }).insert(ignore_permissions=1) @@ -216,4 +242,12 @@ def create_a_todo(): def get_points(user, point_type='energy_points'): - return _get_energy_points(user).get(point_type) or 0 \ No newline at end of file + return _get_energy_points(user).get(point_type) or 0 + +def assign_users_to_todo(todo_name, users): + for user in users: + assign_to({ + 'assign_to': user, + 'doctype': 'ToDo', + 'name': todo_name + }) \ No newline at end of file diff --git a/frappe/social/doctype/energy_point_rule/energy_point_rule.json b/frappe/social/doctype/energy_point_rule/energy_point_rule.json index 7a599701dc..43714cd5be 100644 --- a/frappe/social/doctype/energy_point_rule/energy_point_rule.json +++ b/frappe/social/doctype/energy_point_rule/energy_point_rule.json @@ -12,6 +12,7 @@ "for_doc_event", "condition", "points", + "for_assigned_users", "user_field", "multiplier_field", "max_points" @@ -56,11 +57,11 @@ "reqd": 1 }, { + "depends_on": "eval:!doc.for_assigned_users || doc.for_doc_event==='New'", "description": "The user from this field will be rewarded points", "fieldname": "user_field", "fieldtype": "Select", - "label": "User Field", - "reqd": 1 + "label": "User Field" }, { "fieldname": "multiplier_field", @@ -69,7 +70,7 @@ }, { "depends_on": "eval:doc.multiplier_field", - "description": "Maximum points allowed after multiplying points with the multiplier value\n(Note: For no limit set value as 0)", + "description": "Maximum points allowed after multiplying points with the multiplier value\n(Note: For no limit leave this field empty or set 0)", "fieldname": "max_points", "fieldtype": "Int", "label": "Maximum Points" @@ -84,9 +85,17 @@ "fieldtype": "Select", "label": "For Document Event", "options": "New\nSubmit\nCancel\nCustom" + }, + { + "default": "0", + "depends_on": "eval:doc.for_doc_event !=='New'", + "description": "Users assigned to the reference document will get points.", + "fieldname": "for_assigned_users", + "fieldtype": "Check", + "label": "Allot Points To Assigned Users" } ], - "modified": "2019-09-05 14:22:27.664645", + "modified": "2019-09-20 11:26:26.101557", "modified_by": "Administrator", "module": "Social", "name": "Energy Point Rule", diff --git a/frappe/social/doctype/energy_point_rule/energy_point_rule.py b/frappe/social/doctype/energy_point_rule/energy_point_rule.py index 1641e05f5e..9bd97dd281 100644 --- a/frappe/social/doctype/energy_point_rule/energy_point_rule.py +++ b/frappe/social/doctype/energy_point_rule/energy_point_rule.py @@ -31,21 +31,24 @@ class EnergyPointRule(Document): reference_doctype = doc.doctype reference_name = doc.name - user = doc.get(self.user_field) + users = [] + if self.for_assigned_users: + users = doc.get_assigned_users() + else: + users = [doc.get(self.user_field)] rule = self.name # incase of zero as result after roundoff if not points: return - # if user_field has no value - if not user or user == 'Administrator': return - try: - create_energy_points_log(reference_doctype, reference_name, { - 'points': points, - 'user': user, - 'rule': rule - }) + for user in users: + if not user or user == 'Administrator': continue + create_energy_points_log(reference_doctype, reference_name, { + 'points': points, + 'user': user, + 'rule': rule + }) except Exception as e: frappe.log_error(frappe.get_traceback(), 'apply_energy_point') diff --git a/frappe/social/doctype/energy_point_settings/energy_point_settings.py b/frappe/social/doctype/energy_point_settings/energy_point_settings.py index 65b8a2626c..737aab587c 100644 --- a/frappe/social/doctype/energy_point_settings/energy_point_settings.py +++ b/frappe/social/doctype/energy_point_settings/energy_point_settings.py @@ -21,17 +21,21 @@ def allocate_review_points(): settings.point_allocation_periodicity): return + user_point_map = {} + for level in settings.review_levels: - create_review_points(level) + users = get_users_with_role(level.role) + for user in users: + user_point_map.setdefault(user, 0) + # to avoid duplicate point allocation + user_point_map[user] = max([user_point_map[user], level.review_points]) + + for user, points in user_point_map.items(): + create_review_points_log(user, points) settings.last_point_allocation_date = today() settings.save(ignore_permissions=True) -def create_review_points(level): - users = get_users_with_role(level.role) - for user in users: - create_review_points_log(user, level.review_points) - def can_allocate_today(last_date, periodicity): if not last_date: return True diff --git a/frappe/templates/emails/password_update.html b/frappe/templates/emails/password_update.html deleted file mode 100644 index 7730526b4d..0000000000 --- a/frappe/templates/emails/password_update.html +++ /dev/null @@ -1,4 +0,0 @@ -

{{_("Dear")}} {{ first_name }}{% if last_name %} {{ last_name}}{% endif %},

-

{{_("Your password has been updated. Here is your new password")}}: {{ new_password }}

-

{{_("Thank you")}},
-{{ user_fullname }}

\ No newline at end of file diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index 8452e6a2d0..df96111f20 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -59,7 +59,7 @@ def get_email_address(user=None): if not user: user = frappe.session.user - return frappe.db.get_value("User", user, ["email"], as_dict=True).get("email") + return frappe.db.get_value("User", user, "email") def get_formatted_email(user): """get Email Address of user formatted as: `John Doe `""" diff --git a/frappe/www/login.html b/frappe/www/login.html index 8c470ac6dd..53e16d14c3 100644 --- a/frappe/www/login.html +++ b/frappe/www/login.html @@ -13,12 +13,12 @@
diff --git a/frappe/www/login.py b/frappe/www/login.py index 6745ddea56..38beebf625 100644 --- a/frappe/www/login.py +++ b/frappe/www/login.py @@ -41,7 +41,7 @@ def get_context(context): ldap_settings = LDAPSettings.get_ldap_client_settings() context["ldap_settings"] = ldap_settings - login_name_placeholder = [_("Email address")] + login_name_placeholder = [_("Email Address")] if frappe.utils.cint(frappe.get_system_settings("allow_login_using_mobile_number")): login_name_placeholder.append(_("Mobile number"))