From ff376d7d015250656fb75c6a8f69ac00917dd08d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sat, 21 Oct 2023 14:12:12 +0200 Subject: [PATCH] Prevent storing Decimal with too much trailing zero The trailing zero are kept in the Decimal and rendered in the string representation which is unexpected for a field with limited digits. Closes #12587 --- tryton/tryton/common/number_entry.py | 6 ++++-- trytond/trytond/model/modelstorage.py | 11 ++++------- trytond/trytond/tests/test_field_numeric.py | 11 +++++++++++ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/tryton/tryton/common/number_entry.py b/tryton/tryton/common/number_entry.py index 08026649cc..5df9cd89ad 100644 --- a/tryton/tryton/common/number_entry.py +++ b/tryton/tryton/common/number_entry.py @@ -2,6 +2,7 @@ # this repository contains the full copyright notices and license terms. import gettext import locale +from decimal import Decimal from gi.repository import Gdk, GObject, Gtk @@ -60,11 +61,12 @@ class NumberEntry(Gtk.Entry, Gtk.Editable): value = None if text not in ['-', self.__decimal_point, self.__thousands_sep]: try: - value = self.convert(locale.delocalize(text, self.monetary)) + value = Decimal(locale.delocalize(text, self.monetary)) except ValueError: return position if (value and self.__digits is not None - and round(value, self.__digits) != value): + and (round(value, self.__digits) != value + or value.as_tuple().exponent < -self.__digits)): return position buffer_.insert_text(position, new_text, len(new_text)) return position + len(new_text) diff --git a/trytond/trytond/model/modelstorage.py b/trytond/trytond/model/modelstorage.py index 8006a1a5c8..048828fe67 100644 --- a/trytond/trytond/model/modelstorage.py +++ b/trytond/trytond/model/modelstorage.py @@ -1436,13 +1436,10 @@ class ModelStorage(Model): or not digits or any(d is None for d in digits)): return - if isinstance(value, Decimal): - exp = Decimal('.'.join(['0', '0' * digits[1]])) - if value.quantize(exp) != value: - raise_error(value) - else: - if not (round(value, digits[1]) == float(value)): - raise_error(value) + if (round(value, digits[1]) != value + or (isinstance(value, Decimal) + and value.as_tuple().exponent < -digits[1])): + raise_error(value) # validate digits if getattr(field, 'digits', None): if is_pyson(field.digits): diff --git a/trytond/trytond/tests/test_field_numeric.py b/trytond/trytond/tests/test_field_numeric.py index 7af9a2bcbc..bc47290bf6 100644 --- a/trytond/trytond/tests/test_field_numeric.py +++ b/trytond/trytond/tests/test_field_numeric.py @@ -167,6 +167,17 @@ class FieldNumericTestCase(unittest.TestCase): 'numeric': Decimal('1.11111111111'), }]) + @with_transaction() + def test_create_trailing_zero_digits_invalid(self): + "Test create numeric with invalid trailing zero digits" + Numeric = Pool().get('test.numeric_digits') + + with self.assertRaises(DigitsValidationError): + Numeric.create([{ + 'digits': 1, + 'numeric': Decimal('1.10'), + }]) + @with_transaction() def test_search_equals(self): "Test search numeric equals"