Add ceil and floor as uom rounding methods

issue6222
review27041002
This commit is contained in:
Cédric Krier
2017-02-02 12:11:10 +01:00
parent 7cced70d33
commit b2ee1756ef
3 changed files with 48 additions and 20 deletions
+2
View File
@@ -1,3 +1,5 @@
* Add ceil and floor as uom rounding
Version 4.2.0 - 2016-11-28
* Bug fixes (see mercurial logs for details)
* Add relate from Template to Products
+17 -14
View File
@@ -313,24 +313,27 @@ class ProductTestCase(ModuleTestCase):
], count=True), 2)
@with_transaction()
def test_uom_round(self):
'Test uom round function'
def test_uom_rounding(self):
'Test uom rounding functions'
pool = Pool()
Uom = pool.get('product.uom')
tests = [
(2.53, .1, 2.5),
(3.8, .1, 3.8),
(3.7, .1, 3.7),
(1.3, .5, 1.5),
(1.1, .3, 1.2),
(17, 10, 20),
(7, 10, 10),
(4, 10, 0),
(17, 15, 15),
(2.5, 1.4, 2.8),
(2.53, .1, 2.5, 2.6, 2.5),
(3.8, .1, 3.8, 3.8, 3.8),
(3.7, .1, 3.7, 3.7, 3.7),
(1.3, .5, 1.5, 1.5, 1.0),
(1.1, .3, 1.2, 1.2, 0.9),
(17, 10, 20, 20, 10),
(7, 10, 10, 10, 0),
(4, 10, 0, 10, 0),
(17, 15, 15, 30, 15),
(2.5, 1.4, 2.8, 2.8, 1.4),
]
for number, precision, result in tests:
self.assertEqual(Uom(rounding=precision).round(number), result)
for number, precision, round, ceil, floor in tests:
uom = Uom(rounding=precision)
self.assertEqual(uom.round(number), round)
self.assertEqual(uom.ceil(number), ceil)
self.assertEqual(uom.floor(number), floor)
@with_transaction()
def test_product_order(self):
+29 -6
View File
@@ -3,6 +3,7 @@
from __future__ import division
from decimal import Decimal
from sql import Table
from math import ceil, floor, log10
from trytond.model import ModelView, ModelSQL, fields, Check
from trytond.pyson import Eval
@@ -135,12 +136,13 @@ class Uom(ModelSQL, ModelView):
]
def round(self, number):
precision = self.rounding
i, d = divmod(precision, 1)
base = round(number / precision)
# Instead of multiply by the decimal part, we must divide by the
# integer to avoid precision lost due to float
return (base * i) + ((base / (1 / d)) if d != 0 else 0)
return _round(self, number, func=round)
def ceil(self, number):
return _round(self, number, func=ceil)
def floor(self, number):
return _round(self, number, func=floor)
@classmethod
def validate(cls, uoms):
@@ -265,3 +267,24 @@ class Uom(ModelSQL, ModelView):
new_price = new_price / Decimal(rate_format % to_uom.rate)
return new_price
def _round(uom, number, func=round):
precision = uom.rounding
# Convert precision into an integer greater than 1 to avoid precision lost.
# This works for most cases because rounding is often: n * 10**i
if precision < 1:
exp = -floor(log10(precision))
factor = 10 ** exp
number *= factor
precision *= factor
else:
factor = 1
# Divide by factor which is an integer to avoid precision lost due to
# multiplication by float < 1.
# example:
# >>> 3 * 0.1
# 0.30000000000000004
# >>> 3 / 10.
# 0.3
return func(number / precision) * precision / factor