details:   https://code.tryton.org/tryton/commit/8f2189c7f348
branch:    7.6
user:      Cédric Krier <[email protected]>
date:      Thu Jan 15 17:20:32 2026 +0100
description:
        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
        (grafted from 09e3674f3ffe90f74cf2bc8a9884e885b28c93b8)
diffstat:

 modules/product/tests/test_module.py |   2 +
 modules/product/uom.py               |  72 +++++++++++++++++++----------------
 2 files changed, 41 insertions(+), 33 deletions(-)

diffs (115 lines):

diff -r 8d0862f093c7 -r 8f2189c7f348 modules/product/tests/test_module.py
--- a/modules/product/tests/test_module.py      Thu Jan 15 17:28:20 2026 +0100
+++ b/modules/product/tests/test_module.py      Thu Jan 15 17:20:32 2026 +0100
@@ -130,6 +130,7 @@
         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 @@
         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 -r 8d0862f093c7 -r 8f2189c7f348 modules/product/uom.py
--- a/modules/product/uom.py    Thu Jan 15 17:28:20 2026 +0100
+++ b/modules/product/uom.py    Thu Jan 15 17:20:32 2026 +0100
@@ -206,25 +206,28 @@
         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 @@
         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
 

Reply via email to