summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorRémy Voet (ryv) <ryv@odoo.com>2021-02-04 16:15:41 +0000
committerRémy Voet (ryv) <ryv@odoo.com>2021-02-23 09:59:32 +0000
commit90694e09d4e7ebd888c0e0386e739e487a65b9b4 (patch)
tree26588b813fe1d8978c971ee3b19efcc1d866e11d /addons
parent9c5f2c8b18770c3810a9d657970d44f3cf9e6d15 (diff)
[FIX] stock_account: fix rounding adjustment
This fix (odoo/odoo#46850) was too permissive and fix too much stock valuation issues (other than rounding error) under the hood. The problem of the "too permissive" is will hide some errors without get any feedback about them. Also, it changes the SVL value without any explanation. To avoid the first issue, apply a threshold on the rounding adjustment. This threshold is '(quantity_out * smallest_value_of_the_currency) / 2' (`smallest_value_of_the_currency` = `rounding` field of the related currency). Also to improve debugging, add a line in the description about the rounding adjustment done in a SVL task-2452786 closes odoo/odoo#66662 X-original-commit: 74ba8039c7e300bb2adeda8cd071df4272b3635a Signed-off-by: William Henrotin <Whenrow@users.noreply.github.com>
Diffstat (limited to 'addons')
-rw-r--r--addons/stock_account/models/product.py24
-rw-r--r--addons/stock_account/models/stock_move.py1
-rw-r--r--addons/stock_account/tests/test_stockvaluationlayer.py21
3 files changed, 37 insertions, 9 deletions
diff --git a/addons/stock_account/models/product.py b/addons/stock_account/models/product.py
index 07aadea626e..901d393984c 100644
--- a/addons/stock_account/models/product.py
+++ b/addons/stock_account/models/product.py
@@ -3,7 +3,7 @@
from odoo import api, fields, models, _
from odoo.exceptions import UserError
-from odoo.tools import float_is_zero
+from odoo.tools import float_is_zero, float_repr
from odoo.exceptions import ValidationError
@@ -185,12 +185,22 @@ class ProductProduct(models.Model):
if self.cost_method in ('average', 'fifo'):
fifo_vals = self._run_fifo(abs(quantity), company)
vals['remaining_qty'] = fifo_vals.get('remaining_qty')
- # in case of AVCO, fix rounding issue of standard price when needed.
+ # In case of AVCO, fix rounding issue of standard price when needed.
if self.cost_method == 'average':
- rounding_error = self.standard_price * self.quantity_svl - self.value_svl
- vals['value'] += self.env.company.currency_id.round(rounding_error)
- if self.quantity_svl:
- vals['unit_cost'] = self.value_svl / self.quantity_svl
+ currency = self.env.company.currency_id
+ rounding_error = currency.round(self.standard_price * self.quantity_svl - self.value_svl)
+ if rounding_error:
+ # If it is bigger than the (smallest number of the currency * quantity) / 2,
+ # then it isn't a rounding error but a stock valuation error, we shouldn't fix it under the hood ...
+ if abs(rounding_error) <= (abs(quantity) * currency.rounding) / 2:
+ vals['value'] += rounding_error
+ vals['rounding_adjustment'] = '\nRounding Adjustment: %s%s %s' % (
+ '+' if rounding_error > 0 else '',
+ float_repr(rounding_error, precision_digits=currency.decimal_places),
+ currency.symbol
+ )
+ if self.quantity_svl:
+ vals['unit_cost'] = self.value_svl / self.quantity_svl
if self.cost_method == 'fifo':
vals.update(fifo_vals)
return vals
@@ -528,7 +538,7 @@ class ProductProduct(models.Model):
# FIXME: create an empty layer to track the change?
continue
svsl_vals = product._prepare_out_svl_vals(product.quantity_svl, self.env.company)
- svsl_vals['description'] = description
+ svsl_vals['description'] = description + svsl_vals.pop('rounding_adjustment', '')
svsl_vals['company_id'] = self.env.company.id
empty_stock_svl_list.append(svsl_vals)
return empty_stock_svl_list, products_orig_quantity_svl, impacted_products
diff --git a/addons/stock_account/models/stock_move.py b/addons/stock_account/models/stock_move.py
index 46cfc895e4a..5b30e804a0e 100644
--- a/addons/stock_account/models/stock_move.py
+++ b/addons/stock_account/models/stock_move.py
@@ -179,6 +179,7 @@ class StockMove(models.Model):
svl_vals.update(move._prepare_common_svl_vals())
if forced_quantity:
svl_vals['description'] = 'Correction of %s (modification of past move)' % move.picking_id.name or move.name
+ svl_vals['description'] += svl_vals.pop('rounding_adjustment', '')
svl_vals_list.append(svl_vals)
return self.env['stock.valuation.layer'].sudo().create(svl_vals_list)
diff --git a/addons/stock_account/tests/test_stockvaluationlayer.py b/addons/stock_account/tests/test_stockvaluationlayer.py
index df804b3c57f..136bc967737 100644
--- a/addons/stock_account/tests/test_stockvaluationlayer.py
+++ b/addons/stock_account/tests/test_stockvaluationlayer.py
@@ -485,19 +485,36 @@ class TestStockValuationAVCO(TestStockValuationCommon):
self.assertEqual(self.product1.quantity_svl, 2)
self.assertEqual(self.product1.standard_price, 15)
- def test_rounding_1(self):
+ def test_rounding_slv_1(self):
move1 = self._make_in_move(self.product1, 1, unit_cost=1.00)
move2 = self._make_in_move(self.product1, 1, unit_cost=1.00)
move3 = self._make_in_move(self.product1, 1, unit_cost=1.01)
self.assertAlmostEqual(self.product1.value_svl, 3.01)
- move4 = self._make_out_move(self.product1, 3, create_picking=True)
+ move_out = self._make_out_move(self.product1, 3, create_picking=True)
+
+ self.assertIn('Rounding Adjustment: -0.01', move_out.stock_valuation_layer_ids.description)
self.assertEqual(self.product1.value_svl, 0)
self.assertEqual(self.product1.quantity_svl, 0)
self.assertEqual(self.product1.standard_price, 1.00)
+ def test_rounding_slv_2(self):
+ self._make_in_move(self.product1, 1, unit_cost=1.02)
+ self._make_in_move(self.product1, 1, unit_cost=1.00)
+ self._make_in_move(self.product1, 1, unit_cost=1.00)
+
+ self.assertAlmostEqual(self.product1.value_svl, 3.02)
+
+ move_out = self._make_out_move(self.product1, 3, create_picking=True)
+
+ self.assertIn('Rounding Adjustment: +0.01', move_out.stock_valuation_layer_ids.description)
+
+ self.assertEqual(self.product1.value_svl, 0)
+ self.assertEqual(self.product1.quantity_svl, 0)
+ self.assertEqual(self.product1.standard_price, 1.01)
+
class TestStockValuationFIFO(TestStockValuationCommon):
def setUp(self):