@@ -13,7 +13,7 @@ import os, sys, importlib, inspect, json | |||||
from .exceptions import * | from .exceptions import * | ||||
from .utils.jinja import get_jenv, get_template, render_template | from .utils.jinja import get_jenv, get_template, render_template | ||||
__version__ = '8.0.65' | |||||
__version__ = '8.0.66' | |||||
__title__ = "Frappe Framework" | __title__ = "Frappe Framework" | ||||
local = Local() | local = Local() | ||||
@@ -149,10 +149,12 @@ scheduler_events = { | |||||
"frappe.utils.scheduler.restrict_scheduler_events_if_dormant", | "frappe.utils.scheduler.restrict_scheduler_events_if_dormant", | ||||
"frappe.email.doctype.auto_email_report.auto_email_report.send_daily", | "frappe.email.doctype.auto_email_report.auto_email_report.send_daily", | ||||
"frappe.core.doctype.feedback_request.feedback_request.delete_feedback_request", | "frappe.core.doctype.feedback_request.feedback_request.delete_feedback_request", | ||||
"frappe.core.doctype.authentication_log.authentication_log.clear_authentication_logs", | |||||
"frappe.core.doctype.authentication_log.authentication_log.clear_authentication_logs" | |||||
], | |||||
"daily_long": [ | |||||
"frappe.integrations.doctype.dropbox_settings.dropbox_settings.take_backups_daily" | "frappe.integrations.doctype.dropbox_settings.dropbox_settings.take_backups_daily" | ||||
], | ], | ||||
"weekly": [ | |||||
"weekly_long": [ | |||||
"frappe.integrations.doctype.dropbox_settings.dropbox_settings.take_backups_weekly" | "frappe.integrations.doctype.dropbox_settings.dropbox_settings.take_backups_weekly" | ||||
], | ], | ||||
"monthly": [ | "monthly": [ | ||||
@@ -5,10 +5,17 @@ | |||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
import frappe | import frappe | ||||
import requests | |||||
import socket | |||||
from frappe.model.document import Document | from frappe.model.document import Document | ||||
from frappe import _ | from frappe import _ | ||||
try: | |||||
from urllib.parse import urlparse | |||||
except ImportError: | |||||
from urlparse import urlparse | |||||
class SocialLoginKeys(Document): | class SocialLoginKeys(Document): | ||||
def validate(self): | def validate(self): | ||||
self.validate_frappe_server_url() | self.validate_frappe_server_url() | ||||
@@ -17,10 +24,16 @@ class SocialLoginKeys(Document): | |||||
if self.frappe_server_url: | if self.frappe_server_url: | ||||
if self.frappe_server_url.endswith('/'): | if self.frappe_server_url.endswith('/'): | ||||
self.frappe_server_url = self.frappe_server_url[:-1] | self.frappe_server_url = self.frappe_server_url[:-1] | ||||
import requests | |||||
try: | try: | ||||
r = requests.get(self.frappe_server_url + "/api/method/frappe.handler.version", timeout=5) | |||||
frappe_server_hostname = urlparse(self.frappe_server_url).netloc | |||||
except: | except: | ||||
frappe.throw(_("Unable to make request to the Frappe Server URL")) | |||||
if r.status_code != 200: | |||||
frappe.throw(_("Check Frappe Server URL")) | frappe.throw(_("Check Frappe Server URL")) | ||||
if socket.gethostname() != frappe_server_hostname or \ | |||||
(frappe.local.conf.domains is not None) and \ | |||||
(frappe_server_hostname not in frappe.local.conf.domains): | |||||
try: | |||||
requests.get(self.frappe_server_url + "/api/method/frappe.handler.version", timeout=5) | |||||
except: | |||||
frappe.throw(_("Unable to make request to the Frappe Server URL")) |
@@ -8,14 +8,14 @@ $.extend(frappe.model, { | |||||
extract docs, docinfo (attachments, comments, assignments) | extract docs, docinfo (attachments, comments, assignments) | ||||
from incoming request and set in `locals` and `frappe.model.docinfo` | from incoming request and set in `locals` and `frappe.model.docinfo` | ||||
*/ | */ | ||||
var isPlain; | |||||
if(!r.docs && !r.docinfo) r = {docs:r}; | if(!r.docs && !r.docinfo) r = {docs:r}; | ||||
if($.isPlainObject(r.docs)) r.docs = [r.docs]; | |||||
isPlain = $.isPlainObject(r.docs); | |||||
if(isPlain) r.docs = [r.docs]; | |||||
if(r.docs) { | if(r.docs) { | ||||
var last_parent_name = null; | var last_parent_name = null; | ||||
var dirty = []; | |||||
for(var i=0, l=r.docs.length; i<l; i++) { | for(var i=0, l=r.docs.length; i<l; i++) { | ||||
var d = r.docs[i]; | var d = r.docs[i]; | ||||
@@ -45,7 +45,7 @@ $.extend(frappe.model, { | |||||
} | } | ||||
} | } | ||||
if(cur_frm && dirty.indexOf(cur_frm.doctype)!==-1) cur_frm.dirty(); | |||||
if(cur_frm && isPlain) cur_frm.dirty(); | |||||
} | } | ||||
@@ -4,7 +4,7 @@ from __future__ import unicode_literals | |||||
import unittest | import unittest | ||||
from frappe.utils import evaluate_filters | |||||
from frappe.utils import evaluate_filters, money_in_words | |||||
class TestFilters(unittest.TestCase): | class TestFilters(unittest.TestCase): | ||||
def test_simple_dict(self): | def test_simple_dict(self): | ||||
@@ -34,3 +34,27 @@ class TestFilters(unittest.TestCase): | |||||
{'status': 'Open', 'age': ('>', 10)})) | {'status': 'Open', 'age': ('>', 10)})) | ||||
self.assertFalse(evaluate_filters({'doctype': 'User', 'status': 'Open', 'age': 20}, | self.assertFalse(evaluate_filters({'doctype': 'User', 'status': 'Open', 'age': 20}, | ||||
{'status': 'Open', 'age': ('>', 30)})) | {'status': 'Open', 'age': ('>', 30)})) | ||||
class TestMoney(unittest.TestCase): | |||||
def test_money_in_words(self): | |||||
nums_bhd = [ | |||||
(5000, "BHD Five Thousand only."), (5000.0, "BHD Five Thousand only."), | |||||
(0.1, "One Hundred Fils only."), (0, "BHD Zero only."), ("Fail", "") | |||||
] | |||||
nums_ngn = [ | |||||
(5000, "NGN Five Thousand only."), (5000.0, "NGN Five Thousand only."), | |||||
(0.1, "Ten Kobo only."), (0, "NGN Zero only."), ("Fail", "") | |||||
] | |||||
for num in nums_bhd: | |||||
self.assertEqual( | |||||
money_in_words(num[0], "BHD"), num[1], "{0} is not the same as {1}". | |||||
format(money_in_words(num[0], "BHD"), num[1]) | |||||
) | |||||
for num in nums_ngn: | |||||
self.assertEqual( | |||||
money_in_words(num[0], "NGN"), num[1], "{0} is not the same as {1}". | |||||
format(money_in_words(num[0], "NGN"), num[1]) | |||||
) |
@@ -420,7 +420,14 @@ def money_in_words(number, main_currency = None, fraction_currency=None): | |||||
from frappe.utils import get_defaults | from frappe.utils import get_defaults | ||||
_ = frappe._ | _ = frappe._ | ||||
if not number or flt(number) < 0: | |||||
try: | |||||
# note: `flt` returns 0 for invalid input and we don't want that | |||||
number = float(number) | |||||
except ValueError: | |||||
return "" | |||||
number = flt(number) | |||||
if number < 0: | |||||
return "" | return "" | ||||
d = get_defaults() | d = get_defaults() | ||||
@@ -429,20 +436,30 @@ def money_in_words(number, main_currency = None, fraction_currency=None): | |||||
if not fraction_currency: | if not fraction_currency: | ||||
fraction_currency = frappe.db.get_value("Currency", main_currency, "fraction") or _("Cent") | fraction_currency = frappe.db.get_value("Currency", main_currency, "fraction") or _("Cent") | ||||
n = "%.2f" % flt(number) | |||||
main, fraction = n.split('.') | |||||
if len(fraction)==1: fraction += '0' | |||||
number_format = frappe.db.get_value("Currency", main_currency, "number_format", cache=True) or \ | number_format = frappe.db.get_value("Currency", main_currency, "number_format", cache=True) or \ | ||||
frappe.db.get_default("number_format") or "#,###.##" | frappe.db.get_default("number_format") or "#,###.##" | ||||
fraction_length = len(number_format.rsplit('.', 1)[-1]) | |||||
n = "%.{0}f".format(fraction_length) % number | |||||
main, fraction = n.split('.') | |||||
if len(fraction) < fraction_length: | |||||
zeros = '0' * (fraction_length - len(fraction)) | |||||
fraction += zeros | |||||
in_million = True | in_million = True | ||||
if number_format == "#,##,###.##": in_million = False | if number_format == "#,##,###.##": in_million = False | ||||
out = main_currency + ' ' + in_words(main, in_million).title() | |||||
if cint(fraction): | |||||
out = out + ' ' + _('and') + ' ' + in_words(fraction, in_million).title() + ' ' + fraction_currency | |||||
# 0.00 | |||||
if main == '0' and fraction in ['00', '000']: | |||||
out = "{0} {1}".format(main_currency, _('Zero')) | |||||
# 0.XX | |||||
elif main == '0': | |||||
out = _(in_words(fraction, in_million).title()) + ' ' + fraction_currency | |||||
else: | |||||
out = main_currency + ' ' + _(in_words(main, in_million).title()) | |||||
if cint(fraction): | |||||
out = out + ' ' + _('and') + ' ' + _(in_words(fraction, in_million).title()) + ' ' + fraction_currency | |||||
return out + ' ' + _('only.') | return out + ' ' + _('only.') | ||||