diff options
| author | Rémy Voet (ryv) <ryv@odoo.com> | 2021-02-04 16:15:41 +0000 |
|---|---|---|
| committer | Rémy Voet (ryv) <ryv@odoo.com> | 2021-02-23 09:59:32 +0000 |
| commit | 90694e09d4e7ebd888c0e0386e739e487a65b9b4 (patch) | |
| tree | 26588b813fe1d8978c971ee3b19efcc1d866e11d /addons/stock_account | |
| parent | 9c5f2c8b18770c3810a9d657970d44f3cf9e6d15 (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/stock_account')
| -rw-r--r-- | addons/stock_account/models/product.py | 24 | ||||
| -rw-r--r-- | addons/stock_account/models/stock_move.py | 1 | ||||
| -rw-r--r-- | addons/stock_account/tests/test_stockvaluationlayer.py | 21 |
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): |
