Add ceil and floor as uom rounding methods
issue6222 review27041002
This commit is contained in:
@@ -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
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user