From c49ac84fb8ce3a24495267c98a7a2aef79dc0832 Mon Sep 17 00:00:00 2001 From: Mathieu Walravens Date: Thu, 3 Aug 2023 13:14:59 +0200 Subject: [PATCH] [FIX] stock_account: valuation change has always positive value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Steps to reproduce: 1. Create a new product with a cost of 10 (std, manual) 2. Create an inventory adjustment to set the quantity to -3 3. Change the product's category to real-time Now the valuation for this product is -30€ but the accounting part has 30€, creating a difference of 60€. Before this commit: Changing the valuation to real-time while having a negative quantity/value gives a positive value in accounting. After this commit: Changing the valuation to real-time while having a negative quantity/value gives a negative value in accounting. opw-3390692 closes odoo/odoo#143000 X-original-commit: 9a0e620d2fa1d5e55d73bc4920f26ef2ba82ce9d Signed-off-by: Arnold Moyaux (arm) Signed-off-by: Walravens Mathieu (wama) --- addons/stock_account/models/product.py | 5 +++ .../tests/test_stockvaluationlayer.py | 36 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/addons/stock_account/models/product.py b/addons/stock_account/models/product.py index fc17c917d88..c373dce26d2 100644 --- a/addons/stock_account/models/product.py +++ b/addons/stock_account/models/product.py @@ -648,6 +648,11 @@ class ProductProduct(models.Model): debit_account_id = product_accounts[product.id]['stock_valuation'].id credit_account_id = product_accounts[product.id]['stock_input'].id value = out_stock_valuation_layer.value + if out_stock_valuation_layer.currency_id.compare_amounts(value, 0) < 0: + # Swap accounts makes a negative value in accounting + debit_account_id = product_accounts[product.id]['stock_output'].id + credit_account_id = product_accounts[product.id]['stock_valuation'].id + move_vals = { 'journal_id': product_accounts[product.id]['stock_journal'].id, 'company_id': self.env.company.id, diff --git a/addons/stock_account/tests/test_stockvaluationlayer.py b/addons/stock_account/tests/test_stockvaluationlayer.py index 77b3c62ae9f..2c68e564f88 100644 --- a/addons/stock_account/tests/test_stockvaluationlayer.py +++ b/addons/stock_account/tests/test_stockvaluationlayer.py @@ -939,6 +939,12 @@ class TestStockValuationChangeValuation(TestStockValuationCommon): 'property_stock_journal': cls.stock_journal.id, }) + def _get_stock_account_balance(self, stock_valuation_layer): + """Return the accounting balance from the stock valuation account for the given SVL""" + stock_valuation_account = stock_valuation_layer.product_id.categ_id.property_stock_valuation_account_id + return stock_valuation_layer.account_move_id.line_ids.filtered(lambda line: line.account_id == stock_valuation_account).balance + + def test_standard_manual_to_auto_1(self): self.product1.product_tmpl_id.categ_id.property_cost_method = 'standard' self.product1.product_tmpl_id.categ_id.property_valuation = 'manual_periodic' @@ -962,6 +968,9 @@ class TestStockValuationChangeValuation(TestStockValuationCommon): # An accounting entry should only be created for the replenish now that the category is perpetual. self.assertEqual(len(self.product1.stock_valuation_layer_ids.mapped('account_move_id')), 1) self.assertEqual(len(self.product1.stock_valuation_layer_ids), 3) + # Check the entry's value + last_svl = self.product1.stock_valuation_layer_ids.sorted()[-1] + self.assertEqual(self._get_stock_account_balance(last_svl), self.product1.value_svl) for svl in self.product1.stock_valuation_layer_ids.sorted()[-2:]: self.assertEqual(svl.description, 'Valuation method change for product category All: from manual_periodic to real_time.') @@ -996,6 +1005,33 @@ class TestStockValuationChangeValuation(TestStockValuationCommon): # An accounting entry should only be created for the replenish now that the category is perpetual. self.assertEqual(len(self.product1.stock_valuation_layer_ids.mapped('account_move_id')), 1) self.assertEqual(len(self.product1.stock_valuation_layer_ids), 3) + # Check the entry's value + last_svl = self.product1.stock_valuation_layer_ids.sorted()[-1] + self.assertEqual(self._get_stock_account_balance(last_svl), self.product1.value_svl) + + def test_standard_manual_to_auto_3(self): + self.product1.product_tmpl_id.categ_id.property_cost_method = 'standard' + self.product1.product_tmpl_id.categ_id.property_valuation = 'manual_periodic' + self.product1.product_tmpl_id.standard_price = 10 + self._make_out_move(self.product1, 10, force_assign=True) + + self.assertEqual(self.product1.value_svl, -100) + self.assertEqual(self.product1.quantity_svl, -10) + self.assertEqual(len(self.product1.stock_valuation_layer_ids.account_move_id), 0) + self.assertEqual(len(self.product1.stock_valuation_layer_ids), 1) + + self.product1.product_tmpl_id.categ_id.property_valuation = 'real_time' + + self.assertEqual(self.product1.value_svl, -100) + self.assertEqual(self.product1.quantity_svl, -10) + # An accounting entry should only be created for the replenish now that the category is perpetual. + self.assertEqual(len(self.product1.stock_valuation_layer_ids.account_move_id), 1) + self.assertEqual(len(self.product1.stock_valuation_layer_ids), 3) + # Check the entry's value + last_svl = self.product1.stock_valuation_layer_ids.sorted()[-1] + self.assertEqual(self._get_stock_account_balance(last_svl), self.product1.value_svl) + for svl in self.product1.stock_valuation_layer_ids.sorted()[-2:]: + self.assertEqual(svl.description, 'Valuation method change for product category All: from manual_periodic to real_time.') def test_standard_auto_to_manual_1(self): self.product1.product_tmpl_id.categ_id.property_cost_method = 'standard'