From ededbdb3c966ca77487a93aed9cd556e76e9ec43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 15 Jan 2026 17:20:32 +0100 Subject: [PATCH] Do not convert input when converting between the same unit Applying the factor/rate twice may loose precision if one of the number is a rational with recurring decimal. Closes #14501 --- modules/product/tests/test_module.py | 2 + modules/product/uom.py | 72 +++++++++++++++------------- 2 files changed, 41 insertions(+), 33 deletions(-) diff --git a/modules/product/tests/test_module.py b/modules/product/tests/test_module.py index 3dc44c33fb..0bc5c6205b 100644 --- a/modules/product/tests/test_module.py +++ b/modules/product/tests/test_module.py @@ -130,6 +130,7 @@ class ProductTestCase(CompanyTestMixin, ModuleTestCase): tests = [ ('Kilogram', 100, 'Gram', 100000, 100000), ('Gram', 1, 'Pound', 0.0022046226218487759, 0.0), + ('Ounce', 1 / 7, 'Ounce', 1 / 7, 0.14), ('Second', 5, 'Minute', 0.083333333333333343, 0.08), ('Second', 25, 'Hour', 0.0069444444444444441, 0.01), ('Millimeter', 3, 'Inch', 0.11811023622047245, 0.12), @@ -201,6 +202,7 @@ class ProductTestCase(CompanyTestMixin, ModuleTestCase): tests = [ ('Kilogram', Decimal('100'), 'Gram', Decimal('0.1')), ('Gram', Decimal('1'), 'Pound', Decimal('453.59237')), + ('Ounce', Decimal(1 / 7), 'Ounce', Decimal(1 / 7)), ('Second', Decimal('5'), 'Minute', Decimal('300')), ('Second', Decimal('25'), 'Hour', Decimal('90000')), ('Millimeter', Decimal('3'), 'Inch', Decimal('76.2')), diff --git a/modules/product/uom.py b/modules/product/uom.py index ea78bb4bb8..29df97e57e 100644 --- a/modules/product/uom.py +++ b/modules/product/uom.py @@ -206,25 +206,28 @@ class Uom(SymbolMixin, DigitsMixin, DeactivableMixin, ModelSQL, ModelView): elif factor or rate: raise ValueError("factor and rate not allowed for same category") - if from_uom.accurate_field == 'factor': - amount = qty * from_uom.factor - else: - amount = qty / from_uom.rate - - if factor and rate: - if _accurate_operator(factor, rate) == 'rate': - factor = None + if from_uom != to_uom: + if from_uom.accurate_field == 'factor': + amount = qty * from_uom.factor else: - rate = None - if factor: - amount *= factor - elif rate: - amount /= rate + amount = qty / from_uom.rate - if to_uom.accurate_field == 'factor': - amount = amount / to_uom.factor + if factor and rate: + if _accurate_operator(factor, rate) == 'rate': + factor = None + else: + rate = None + if factor: + amount *= factor + elif rate: + amount /= rate + + if to_uom.accurate_field == 'factor': + amount = amount / to_uom.factor + else: + amount = amount * to_uom.rate else: - amount = amount * to_uom.rate + amount = qty if round: amount = to_uom.round(amount) @@ -254,27 +257,30 @@ class Uom(SymbolMixin, DigitsMixin, DeactivableMixin, ModelSQL, ModelView): elif factor or rate: raise ValueError("factor and rate not allow for same category") - format_ = '%%.%df' % uom_conversion_digits[1] + if from_uom != to_uom: + format_ = '%%.%df' % uom_conversion_digits[1] - if from_uom.accurate_field == 'factor': - new_price = price / Decimal(format_ % from_uom.factor) - else: - new_price = price * Decimal(format_ % from_uom.rate) - - if factor and rate: - if _accurate_operator(factor, rate) == 'rate': - factor = None + if from_uom.accurate_field == 'factor': + new_price = price / Decimal(format_ % from_uom.factor) else: - rate = None - if factor: - new_price /= Decimal(factor) - elif rate: - new_price *= Decimal(rate) + new_price = price * Decimal(format_ % from_uom.rate) - if to_uom.accurate_field == 'factor': - new_price = new_price * Decimal(format_ % to_uom.factor) + if factor and rate: + if _accurate_operator(factor, rate) == 'rate': + factor = None + else: + rate = None + if factor: + new_price /= Decimal(factor) + elif rate: + new_price *= Decimal(rate) + + if to_uom.accurate_field == 'factor': + new_price = new_price * Decimal(format_ % to_uom.factor) + else: + new_price = new_price / Decimal(format_ % to_uom.rate) else: - new_price = new_price / Decimal(format_ % to_uom.rate) + new_price = price return new_price