[FIX] fields: do not serialize more digits than necessary in monetary._symbol_set()

Add a class for float objects that carry their precision digits, use it for the
value of monetary fields in record cache, and serialize them as expected for
the database.
This commit is contained in:
Raphael Collet
2016-06-23 10:08:57 +02:00
parent 11f88c8cd4
commit 1ba91042d6
5 changed files with 48 additions and 3 deletions
+3
View File
@@ -264,6 +264,9 @@ class MixedModel(models.Model):
comment3 = fields.Html(sanitize=True, strip_classes=True)
comment4 = fields.Html(sanitize=True, strip_style=True)
currency_id = fields.Many2one('res.currency', default=lambda self: self.env.ref('base.EUR'))
amount = fields.Monetary()
@api.one
def _compute_now(self):
# this is a non-stored computed field without dependencies
@@ -235,6 +235,22 @@ class TestNewFields(common.TransactionCase):
record.number = 2.4999999999999996
self.assertEqual(record.number, 2.50)
def test_20_monetary(self):
""" test monetary fields """
record = self.env['test_new_api.mixed'].create({})
self.assertTrue(record.currency_id)
self.assertEqual(record.currency_id.rounding, 0.01)
# the conversion to cache should round the value to 14.700000000000001
record.amount = 14.7
self.assertNotEqual(record.amount, 14.7)
self.assertEqual(record.amount, 14.700000000000001)
# however when stored to database, it should be serialized as 14.70
self.cr.execute('SELECT amount FROM test_new_api_mixed WHERE id=%s', (record.id,))
(amount,) = self.cr.fetchone()
self.assertEqual(amount, 14.7)
def test_21_date(self):
""" test date fields """
record = self.env['test_new_api.mixed'].create({})
+3 -2
View File
@@ -13,7 +13,7 @@ import pytz
import xmlrpclib
from openerp.sql_db import LazyCursor
from openerp.tools import float_round, frozendict, html_sanitize, ustr, OrderedSet
from openerp.tools import float_precision, float_round, frozendict, html_sanitize, ustr, OrderedSet
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as DATE_FORMAT
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT as DATETIME_FORMAT
from openerp.tools.translate import html_translate
@@ -1177,7 +1177,8 @@ class Monetary(Field):
# FIXME @rco-odoo: currency may not be already initialized if it is
# a function or related field!
if currency:
return currency.round(float(value or 0.0))
value = currency.round(float(value or 0.0))
return float_precision(value, currency.decimal_places)
return float(value or 0.0)
+7 -1
View File
@@ -404,9 +404,15 @@ class float(_column):
def digits_change(self, cr):
pass
def _symbol_set_monetary(val):
try:
return val.float_repr() # see float_precision.float_repr()
except Exception:
return __builtin__.float(val or 0.0)
class monetary(_column):
_type = 'monetary'
_symbol_set = ('%s', lambda x: __builtin__.float(x or 0.0))
_symbol_set = ('%s', _symbol_set_monetary)
_symbol_get = lambda self,x: x or 0.0
def to_field_args(self):
+19
View File
@@ -148,6 +148,25 @@ def float_repr(value, precision_digits):
# precision. e.g. str(123456789.1234) == str(123456789.123)!!
return ("%%.%sf" % precision_digits) % value
_float_repr = float_repr
class float_precision(float):
""" A class for float values that carry precision digits. This is a thin
layer on top of ``float``, and the precision digits are not propagated
to the result of arithmetic operations. This class is used when
converting monetary values to cache, and for serializing them to the
database.
"""
__slots__ = ['precision_digits']
def __new__(cls, value, precision_digits):
obj = super(float_precision, cls).__new__(cls, value)
obj.precision_digits = precision_digits
return obj
def float_repr(self):
return _float_repr(self, self.precision_digits)
if __name__ == "__main__":