@@ -41,7 +41,19 @@ def run_server_script_for_doc_event(doc, event): | |||||
if scripts: | if scripts: | ||||
# run all scripts for this doctype + event | # run all scripts for this doctype + event | ||||
for script_name in scripts: | for script_name in scripts: | ||||
frappe.get_doc('Server Script', script_name).execute_doc(doc) | |||||
try: | |||||
frappe.get_doc('Server Script', script_name).execute_doc(doc) | |||||
except Exception as e: | |||||
message = frappe._('Error executing Server Script {0}. Open Browser Console to see traceback.').format( | |||||
frappe.utils.get_link_to_form('Server Script', script_name) | |||||
) | |||||
exception = type(e) | |||||
if getattr(frappe, 'request', None): | |||||
# all exceptions throw 500 which is internal server error | |||||
# however server script error is a user error | |||||
# so we should throw 417 which is expectation failed | |||||
exception.http_status_code = 417 | |||||
frappe.throw(title=frappe._('Server Script Error'), msg=message, exc=exception) | |||||
def get_server_script_map(): | def get_server_script_map(): | ||||
# fetch cached server script methods | # fetch cached server script methods | ||||
@@ -120,6 +120,11 @@ class Meta(Document): | |||||
or (not no_nulls and value is None)): | or (not no_nulls and value is None)): | ||||
out[key] = value | out[key] = value | ||||
# set empty lists for unset table fields | |||||
for table_field in DOCTYPE_TABLE_FIELDS: | |||||
if not out.get(table_field.fieldname): | |||||
out[table_field.fieldname] = [] | |||||
return out | return out | ||||
return serialize(self) | return serialize(self) | ||||
@@ -882,7 +882,8 @@ number_format_info = { | |||||
"#,##,###.##": (".", ",", 2), | "#,##,###.##": (".", ",", 2), | ||||
"#,###.###": (".", ",", 3), | "#,###.###": (".", ",", 3), | ||||
"#.###": ("", ".", 0), | "#.###": ("", ".", 0), | ||||
"#,###": ("", ",", 0) | |||||
"#,###": ("", ",", 0), | |||||
"#.########": (".", "", 8) | |||||
} | } | ||||
def get_number_format_info(format): | def get_number_format_info(format): | ||||
@@ -184,10 +184,32 @@ def get_safe_globals(): | |||||
# allow iterators and list comprehension | # allow iterators and list comprehension | ||||
out._getiter_ = iter | out._getiter_ = iter | ||||
out._iter_unpack_sequence_ = RestrictedPython.Guards.guarded_iter_unpack_sequence | out._iter_unpack_sequence_ = RestrictedPython.Guards.guarded_iter_unpack_sequence | ||||
out.sorted = sorted | |||||
# add common python builtins | |||||
out.update(get_python_builtins()) | |||||
return out | return out | ||||
def get_python_builtins(): | |||||
return { | |||||
'abs': abs, | |||||
'all': all, | |||||
'any': any, | |||||
'bool': bool, | |||||
'dict': dict, | |||||
'enumerate': enumerate, | |||||
'isinstance': isinstance, | |||||
'issubclass': issubclass, | |||||
'list': list, | |||||
'max': max, | |||||
'min': min, | |||||
'range': range, | |||||
'set': set, | |||||
'sorted': sorted, | |||||
'sum': sum, | |||||
'tuple': tuple, | |||||
} | |||||
def get_hooks(hook=None, default=None, app_name=None): | def get_hooks(hook=None, default=None, app_name=None): | ||||
hooks = frappe.get_hooks(hook=hook, default=default, app_name=app_name) | hooks = frappe.get_hooks(hook=hook, default=default, app_name=app_name) | ||||
return copy.deepcopy(hooks) | return copy.deepcopy(hooks) | ||||