From 6f856e7ea7145f4358e8fbdff48e08c10e7b3f0f Mon Sep 17 00:00:00 2001 From: david Date: Mon, 30 Dec 2019 13:49:13 +0100 Subject: [PATCH] [FIX] stock_quantity_history_location: qty by location When a qty at date is asked the location context could be overriden if the company_owned one was inserted after we removed it. We ensure to remove it to always obtain the right values. As a side effect, stock_account valuation is now correctly computed when AVCO or Standard methods are implied. So should be with FIFO manual (as it computes the quantities the same way) but doubtful in FIFO real time. --- .../tests/__init__.py | 1 + .../tests/test_valuation_location.py | 55 +++++++++++++++++++ .../wizards/stock_quantity_history.py | 21 ++++++- .../wizards/stock_quantity_history.xml | 6 -- stock_quantity_history_location/__init__.py | 1 + .../models/__init__.py | 1 + .../models/product.py | 16 ++++++ .../test_stock_quantity_history_location.py | 3 + .../wizards/stock_quantity_history.py | 2 + 9 files changed, 98 insertions(+), 8 deletions(-) create mode 100644 stock_account_quantity_history_location/tests/__init__.py create mode 100644 stock_account_quantity_history_location/tests/test_valuation_location.py create mode 100644 stock_quantity_history_location/models/__init__.py create mode 100644 stock_quantity_history_location/models/product.py diff --git a/stock_account_quantity_history_location/tests/__init__.py b/stock_account_quantity_history_location/tests/__init__.py new file mode 100644 index 0000000..0118859 --- /dev/null +++ b/stock_account_quantity_history_location/tests/__init__.py @@ -0,0 +1 @@ +from . import test_valuation_location diff --git a/stock_account_quantity_history_location/tests/test_valuation_location.py b/stock_account_quantity_history_location/tests/test_valuation_location.py new file mode 100644 index 0000000..7b8704d --- /dev/null +++ b/stock_account_quantity_history_location/tests/test_valuation_location.py @@ -0,0 +1,55 @@ +# Copyright 2020 Tecnativa - David Vidal +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo.addons.stock_quantity_history_location.tests import ( + test_stock_quantity_history_location +) + + +class TestValuationLocation(test_stock_quantity_history_location + .TestStockQuantityHistoryLocation): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env['product.price.history'].create({ + 'datetime': '2019-08-12', + 'product_id': cls.product.id, + 'cost': 35.01, + }) + cls.env['product.price.history'].create({ + 'datetime': '2020-01-01', + 'product_id': cls.product.id, + 'cost': 33.33, + }) + cls.product.categ_id.property_cost_method = 'standard' + cls.test_stock_loc2 = cls.env['stock.location'].create({ + 'usage': 'internal', + 'name': 'Test Stock Location 2', + 'company_id': cls.main_company.id + }) + # Create a move for the past + move = cls.env['stock.move'].create({ + 'name': 'Stock move in', + 'location_id': cls.supplier_location.id, + 'location_dest_id': cls.test_stock_loc2.id, + 'product_id': cls.product.id, + 'product_uom': cls.product.uom_id.id, + 'product_uom_qty': 50.0, + }) + move._action_confirm() + move._action_assign() + move_line = move.move_line_ids[0] + move_line.qty_done = 50.0 + move._action_done() + move.date = "2020-01-01" + + def test_stock_location_valuation(self): + wizard = self.env['stock.quantity.history'].create({ + "location_id": self.test_stock_loc2.id, + "include_child_locations": True, + "compute_at_date": 1, + "date": "2020-01-01", + }) + action = wizard.with_context( + company_owned=True, valuation=True).open_table() + self.assertAlmostEqual( + self.product.with_context(action['context']).stock_value, 1666.5) diff --git a/stock_account_quantity_history_location/wizards/stock_quantity_history.py b/stock_account_quantity_history_location/wizards/stock_quantity_history.py index aa1ac6f..ec3ba8f 100644 --- a/stock_account_quantity_history_location/wizards/stock_quantity_history.py +++ b/stock_account_quantity_history_location/wizards/stock_quantity_history.py @@ -9,11 +9,28 @@ class StockQuantityHistory(models.TransientModel): _inherit = 'stock.quantity.history' def open_table(self): - action = super(StockQuantityHistory, self).open_table() + """Stock valuation goes by a different path than stock report, so + we ensure the correct context as well""" + action = super().open_table() + if not self.env.context.get('valuation'): + return action # Show 0 quantities on Inventory Valuation to display Account Valuation # anomalies, such as, non 0 stock_value on cost_method FIFO - if self.env.context.get('valuation') and not self.location_id: + if not self.location_id: domain = ast.literal_eval(action['domain']) domain.pop(domain.index(('qty_available', '!=', 0))) action['domain'] = domain + ctx = action['context'] + if isinstance(ctx, str): + ctx = ast.literal_eval(ctx) + if self.location_id: + ctx['location'] = self.location_id.id + ctx['compute_child'] = self.include_child_locations + if ctx.get('company_owned', False): + ctx.pop('company_owned') + # Ensure the context isn't added later and catch it + ctx['drop_company_owned'] = True + action['name'] = '%s (%s)' % (action['name'], + self.location_id.complete_name) + action['context'] = ctx return action diff --git a/stock_account_quantity_history_location/wizards/stock_quantity_history.xml b/stock_account_quantity_history_location/wizards/stock_quantity_history.xml index 4ce6489..3844ef1 100644 --- a/stock_account_quantity_history_location/wizards/stock_quantity_history.xml +++ b/stock_account_quantity_history_location/wizards/stock_quantity_history.xml @@ -9,12 +9,6 @@ - diff --git a/stock_quantity_history_location/__init__.py b/stock_quantity_history_location/__init__.py index 5cb1c49..aee8895 100644 --- a/stock_quantity_history_location/__init__.py +++ b/stock_quantity_history_location/__init__.py @@ -1 +1,2 @@ +from . import models from . import wizards diff --git a/stock_quantity_history_location/models/__init__.py b/stock_quantity_history_location/models/__init__.py new file mode 100644 index 0000000..9649db7 --- /dev/null +++ b/stock_quantity_history_location/models/__init__.py @@ -0,0 +1 @@ +from . import product diff --git a/stock_quantity_history_location/models/product.py b/stock_quantity_history_location/models/product.py new file mode 100644 index 0000000..46445e8 --- /dev/null +++ b/stock_quantity_history_location/models/product.py @@ -0,0 +1,16 @@ +# Copyright 2019 Tecnativa - David Vidal +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import models + + +class ProductProduct(models.Model): + _inherit = 'product.product' + + def _get_domain_locations(self): + """Ensures the location context isn't overriden""" + ctx = dict(self.env.context) + if not ctx.get('drop_company_owned') or not ctx.get('company_owned'): + return super()._get_domain_locations() + ctx.pop('company_owned', None) + self_ctx = self.with_context(ctx) + return super(ProductProduct, self_ctx)._get_domain_locations() diff --git a/stock_quantity_history_location/tests/test_stock_quantity_history_location.py b/stock_quantity_history_location/tests/test_stock_quantity_history_location.py index 3f1b762..56d6a67 100644 --- a/stock_quantity_history_location/tests/test_stock_quantity_history_location.py +++ b/stock_quantity_history_location/tests/test_stock_quantity_history_location.py @@ -39,6 +39,9 @@ class TestStockQuantityHistoryLocation(SavepointCase): move._action_done() move.date = "2019-08-11" + +class StockQuantityHistoryLocationTests(TestStockQuantityHistoryLocation): + def test_wizard_past_date(self): wizard = self.env['stock.quantity.history'].create({ "location_id": self.test_stock_loc.id, diff --git a/stock_quantity_history_location/wizards/stock_quantity_history.py b/stock_quantity_history_location/wizards/stock_quantity_history.py index 5849e68..30818fa 100644 --- a/stock_quantity_history_location/wizards/stock_quantity_history.py +++ b/stock_quantity_history_location/wizards/stock_quantity_history.py @@ -33,6 +33,8 @@ class StockQuantityHistory(models.TransientModel): ctx['compute_child'] = self.include_child_locations if ctx.get('company_owned', False): ctx.pop('company_owned') + # Ensure the context isn't added later and catch it + ctx['drop_company_owned'] = True action['name'] = '%s (%s)' % (action['name'], self.location_id.complete_name) action['context'] = ctx