diff --git a/frappe/__version__.py b/frappe/__version__.py index 8af646c67a..2403419fc1 100644 --- a/frappe/__version__.py +++ b/frappe/__version__.py @@ -1,2 +1,2 @@ from __future__ import unicode_literals -__version__ = "4.13.0" +__version__ = "4.13.1" diff --git a/frappe/app.py b/frappe/app.py index 822c061b67..76018e391e 100644 --- a/frappe/app.py +++ b/frappe/app.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import sys, os import json import logging +import MySQLdb from werkzeug.wrappers import Request, Response from werkzeug.local import LocalManager @@ -70,6 +71,14 @@ def application(request): except Exception, e: http_status_code = getattr(e, "http_status_code", 500) + if (http_status_code==500 + and isinstance(e, MySQLdb.OperationalError) + and e.args[0] in (1205, 1213)): + # 1205 = lock wait timeout + # 1213 = deadlock + # code 409 represents conflict + http_status_code = 409 + if frappe.local.is_ajax: response = frappe.utils.response.report_error(http_status_code) else: diff --git a/frappe/hooks.py b/frappe/hooks.py index 5215476b9e..9eedca2f8e 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -4,7 +4,7 @@ app_title = "Frappe Framework" app_publisher = "Web Notes Technologies Pvt. Ltd." app_description = "Full Stack Web Application Framework in Python" app_icon = "assets/frappe/images/frappe.svg" -app_version = "4.13.0" +app_version = "4.13.1" app_color = "#3498db" app_email = "support@frappe.io" diff --git a/frappe/model/rename_doc.py b/frappe/model/rename_doc.py index a4a8b73c09..d4e11252dd 100644 --- a/frappe/model/rename_doc.py +++ b/frappe/model/rename_doc.py @@ -82,7 +82,9 @@ def rename_parent_and_child(doctype, old, new, meta): update_child_docs(old, new, meta) def validate_rename(doctype, new, meta, merge, force, ignore_permissions): - exists = frappe.db.get_value(doctype, new) + # using for update so that it gets locked and someone else cannot edit it while this rename is going on! + exists = frappe.db.sql("select name from `tab{doctype}` where name=%s for update".format(doctype=doctype), new) + exists = exists[0][0] if exists else None if merge and not exists: frappe.msgprint(_("{0} {1} does not exist, select a new target to merge").format(doctype, new), raise_exception=1) diff --git a/frappe/public/js/frappe/misc/number_format.js b/frappe/public/js/frappe/misc/number_format.js index 86d61fd7b9..97241d5bb1 100644 --- a/frappe/public/js/frappe/misc/number_format.js +++ b/frappe/public/js/frappe/misc/number_format.js @@ -178,7 +178,7 @@ function _round(num, precision) { var m = Math.pow(10, d); var n = +(d ? num * m : num).toFixed(8); // Avoid rounding errors var i = Math.floor(n), f = n - i; - var r = (f == 0.5) ? ((i % 2 == 0) ? i : i + 1) : Math.round(n); + var r = (!precision && f == 0.5) ? ((i % 2 == 0) ? i : i + 1) : Math.round(n); return d ? r / m : r; } diff --git a/frappe/public/js/frappe/request.js b/frappe/public/js/frappe/request.js index 1618d213e5..50145a155d 100644 --- a/frappe/public/js/frappe/request.js +++ b/frappe/public/js/frappe/request.js @@ -66,6 +66,9 @@ frappe.request.call = function(opts) { msgprint(__("Not permitted")); }, + 409: function(xhr) { + msgprint(__("Another transaction is blocking this one. Please try again in a few seconds.")); + }, 417: function(data, xhr) { if(typeof data === "string") data = JSON.parse(data); opts.error_callback && opts.error_callback(data, xhr.responseText); diff --git a/frappe/utils/data.py b/frappe/utils/data.py index c7c880d57a..b667912f13 100644 --- a/frappe/utils/data.py +++ b/frappe/utils/data.py @@ -185,12 +185,14 @@ def flt(s, precision=None): """Convert to float (ignore commas)""" if isinstance(s, basestring): s = s.replace(',','') + try: num = float(s) if precision is not None: num = rounded(num, precision) except Exception: num = 0 + return num def cint(s): @@ -210,7 +212,7 @@ def cstr(s): return unicode(s) def rounded(num, precision=0): - """round method for round halfs to nearest even algorithm""" + """round method for round halfs to nearest even algorithm aka banker's rounding - compatible with python3""" precision = cint(precision) multiplier = 10 ** precision @@ -220,7 +222,7 @@ def rounded(num, precision=0): floor = math.floor(num) decimal_part = num - floor - if decimal_part == 0.5: + if not precision and decimal_part == 0.5: num = floor if (floor % 2 == 0) else floor + 1 else: num = round(num) diff --git a/frappe/widgets/form/meta.py b/frappe/widgets/form/meta.py index c751305d90..417c2d39b9 100644 --- a/frappe/widgets/form/meta.py +++ b/frappe/widgets/form/meta.py @@ -68,6 +68,7 @@ class FormMeta(Meta): self.set("__listview_template", get_html_format(listview_template)) self.add_code_via_hook("doctype_js", "__js") + self.add_code_via_hook("doctype_list_js", "__list_js") self.add_custom_script() def _add_code(self, path, fieldname): diff --git a/requirements.txt b/requirements.txt index 32f65b6db7..1a431ed337 100644 --- a/requirements.txt +++ b/requirements.txt @@ -24,3 +24,4 @@ selenium pdfkit babel ipython +click diff --git a/setup.py b/setup.py index 9b6a065773..17d78b34ec 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup, find_packages import os -version = "4.13.0" +version = "4.13.1" with open("requirements.txt", "r") as f: install_requires = f.readlines()