diff --git a/addons/stock_account/models/product.py b/addons/stock_account/models/product.py index 86f563b00abe85631dfd81a990c91a0358aba38c..cc1dd966ec501d0495514830b372641207f719b6 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, tools, _ 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 @@ -161,12 +161,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 @@ -438,7 +448,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 558769556f5d9eb33334ec4b8304c049bbd4922a..d83e5d5709045e671f59fc30103804ff35868ff9 100644 --- a/addons/stock_account/models/stock_move.py +++ b/addons/stock_account/models/stock_move.py @@ -182,6 +182,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 652be87fc2b8fd4bd84e1df6d1fa54fe141e6543..6a5c5260713094231d73d20f19feb2fd69c6ce68 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):