[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:
@@ -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
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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__":
|
||||
|
||||
|
||||
Reference in New Issue
Block a user