From ff4c86089c39378c2279b7369aee554c687b0061 Mon Sep 17 00:00:00 2001 From: tundebabzy Date: Tue, 13 Jun 2017 07:39:35 +0100 Subject: [PATCH] Make "Amount in words" spell three decimals (#3358) * fixes frappe/erpnext#8895 * checks for non-numeric type * makes words translatable --- frappe/tests/test_utils.py | 26 +++++++++++++++++++++++++- frappe/utils/data.py | 35 ++++++++++++++++++++++++++--------- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/frappe/tests/test_utils.py b/frappe/tests/test_utils.py index 713c8a636c..af13829e7e 100644 --- a/frappe/tests/test_utils.py +++ b/frappe/tests/test_utils.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import unittest -from frappe.utils import evaluate_filters +from frappe.utils import evaluate_filters, money_in_words class TestFilters(unittest.TestCase): def test_simple_dict(self): @@ -34,3 +34,27 @@ class TestFilters(unittest.TestCase): {'status': 'Open', 'age': ('>', 10)})) self.assertFalse(evaluate_filters({'doctype': 'User', 'status': 'Open', 'age': 20}, {'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]) + ) \ No newline at end of file diff --git a/frappe/utils/data.py b/frappe/utils/data.py index 067d5a4650..f28c321f0b 100644 --- a/frappe/utils/data.py +++ b/frappe/utils/data.py @@ -419,7 +419,14 @@ def money_in_words(number, main_currency = None, fraction_currency=None): from frappe.utils import get_defaults _ = 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 "" d = get_defaults() @@ -428,20 +435,30 @@ def money_in_words(number, main_currency = None, fraction_currency=None): if not fraction_currency: 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 \ 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 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.')