[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) comment3 = fields.Html(sanitize=True, strip_classes=True)
comment4 = fields.Html(sanitize=True, strip_style=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 @api.one
def _compute_now(self): def _compute_now(self):
# this is a non-stored computed field without dependencies # this is a non-stored computed field without dependencies
@@ -235,6 +235,22 @@ class TestNewFields(common.TransactionCase):
record.number = 2.4999999999999996 record.number = 2.4999999999999996
self.assertEqual(record.number, 2.50) 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): def test_21_date(self):
""" test date fields """ """ test date fields """
record = self.env['test_new_api.mixed'].create({}) record = self.env['test_new_api.mixed'].create({})
+3 -2
View File
@@ -13,7 +13,7 @@ import pytz
import xmlrpclib import xmlrpclib
from openerp.sql_db import LazyCursor 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_DATE_FORMAT as DATE_FORMAT
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT as DATETIME_FORMAT from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT as DATETIME_FORMAT
from openerp.tools.translate import html_translate 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 # FIXME @rco-odoo: currency may not be already initialized if it is
# a function or related field! # a function or related field!
if currency: 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) return float(value or 0.0)
+7 -1
View File
@@ -404,9 +404,15 @@ class float(_column):
def digits_change(self, cr): def digits_change(self, cr):
pass 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): class monetary(_column):
_type = 'monetary' _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 _symbol_get = lambda self,x: x or 0.0
def to_field_args(self): 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)!! # precision. e.g. str(123456789.1234) == str(123456789.123)!!
return ("%%.%sf" % precision_digits) % value 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__": if __name__ == "__main__":