diff --git a/stock_location_last_inventory_date/__init__.py b/stock_location_last_inventory_date/__init__.py index 67e28b5c3a45..bee1c916c01b 100644 --- a/stock_location_last_inventory_date/__init__.py +++ b/stock_location_last_inventory_date/__init__.py @@ -1,3 +1,5 @@ # Copyright 2021 Camptocamp SA +# Copyright 2024 Michael Tietz (MT Software) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +from .hooks import post_init_hook from . import models diff --git a/stock_location_last_inventory_date/__manifest__.py b/stock_location_last_inventory_date/__manifest__.py index 77a148e4d68e..8800861bd548 100644 --- a/stock_location_last_inventory_date/__manifest__.py +++ b/stock_location_last_inventory_date/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Stock Location Last Inventory Date", "summary": "Show the last inventory date for a leaf location", - "version": "14.0.1.0.0", + "version": "14.0.2.0.0", "development_status": "Alpha", "category": "Warehouse", "website": "https://github.com/OCA/stock-logistics-warehouse", @@ -13,4 +13,5 @@ "installable": True, "depends": ["product", "stock"], "data": ["views/stock_location_views.xml"], + "post_init_hook": "post_init_hook", } diff --git a/stock_location_last_inventory_date/hooks.py b/stock_location_last_inventory_date/hooks.py new file mode 100644 index 000000000000..6bcfbd0a921b --- /dev/null +++ b/stock_location_last_inventory_date/hooks.py @@ -0,0 +1,36 @@ +# Copyright 2024 Michael Tietz (MT Software) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +import logging + +logger = logging.getLogger(__name__) + + +def set_initial_last_inventory_date(cr): + cr.execute( + """ + Update + stock_location + set + last_inventory_date = sub.date + from ( + Select + line.location_id, + max(inventory.date) as date + from + stock_inventory_line as line + join + stock_inventory as inventory + on + inventory.id = line.inventory_id + group by + line.location_id + ) as sub + where + stock_location.id = sub.location_id; + """ + ) + + +def post_init_hook(cr, registry): + logger.info("Calculate last inventory date for locations") + set_initial_last_inventory_date(cr) diff --git a/stock_location_last_inventory_date/migrations/14.0.2.0.0/post-migration.py b/stock_location_last_inventory_date/migrations/14.0.2.0.0/post-migration.py new file mode 100644 index 000000000000..28eb5110eeb4 --- /dev/null +++ b/stock_location_last_inventory_date/migrations/14.0.2.0.0/post-migration.py @@ -0,0 +1,9 @@ +# Copyright 2024 Michael Tietz (MT Software) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +from odoo.addons.stock_location_last_inventory_date.hooks import ( + set_initial_last_inventory_date, +) + + +def migrate(cr, version): + set_initial_last_inventory_date(cr) diff --git a/stock_location_last_inventory_date/models/__init__.py b/stock_location_last_inventory_date/models/__init__.py index e06d283bf5f1..366aca9defaf 100644 --- a/stock_location_last_inventory_date/models/__init__.py +++ b/stock_location_last_inventory_date/models/__init__.py @@ -1,3 +1,5 @@ # Copyright 2021 Camptocamp SA +# Copyright 2024 Michael Tietz (MT Software) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) from . import stock_location +from . import stock_inventory diff --git a/stock_location_last_inventory_date/models/stock_inventory.py b/stock_location_last_inventory_date/models/stock_inventory.py new file mode 100644 index 000000000000..dd038811b405 --- /dev/null +++ b/stock_location_last_inventory_date/models/stock_inventory.py @@ -0,0 +1,14 @@ +# Copyright 2024 Michael Tietz (MT Software) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +from odoo import models + + +class StockInventory(models.Model): + _inherit = "stock.inventory" + + def _action_done(self): + super()._action_done() + for inventory in self: + last_inventory_date = inventory.date + done_locations = inventory.line_ids.location_id + done_locations.write({"last_inventory_date": last_inventory_date}) diff --git a/stock_location_last_inventory_date/models/stock_location.py b/stock_location_last_inventory_date/models/stock_location.py index 4d377b841839..de46103349ae 100644 --- a/stock_location_last_inventory_date/models/stock_location.py +++ b/stock_location_last_inventory_date/models/stock_location.py @@ -1,4 +1,5 @@ # Copyright 2021 Camptocamp SA +# Copyright 2024 Michael Tietz (MT Software) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) from odoo import fields, models @@ -8,20 +9,6 @@ class StockLocation(models.Model): last_inventory_date = fields.Datetime( "Last Inventory Date", - compute="_compute_last_inventory_date", help="Indicates the last inventory date for the location, " "including inventory done on parents location.", ) - - def _compute_last_inventory_date(self): - for location in self: - location_ids = [ - int(location_id) - for location_id in location.parent_path.rstrip("/").split("/") - ] - last_inventory = self.env["stock.inventory"].search( - [("location_ids", "in", location_ids), ("state", "=", "done")], - order="date desc", - limit=1, - ) - location.last_inventory_date = last_inventory.date diff --git a/stock_location_last_inventory_date/readme/CONTRIBUTORS.rst b/stock_location_last_inventory_date/readme/CONTRIBUTORS.rst index d5dd63c1714e..13defa0dd9a4 100644 --- a/stock_location_last_inventory_date/readme/CONTRIBUTORS.rst +++ b/stock_location_last_inventory_date/readme/CONTRIBUTORS.rst @@ -1,2 +1,3 @@ * Carlos Serra-Toro * `Trobz `_: +* Michael Tietz (MT Software) diff --git a/stock_location_last_inventory_date/tests/test_stock_location.py b/stock_location_last_inventory_date/tests/test_stock_location.py index 8453738aec22..46d79411d526 100644 --- a/stock_location_last_inventory_date/tests/test_stock_location.py +++ b/stock_location_last_inventory_date/tests/test_stock_location.py @@ -1,5 +1,8 @@ # Copyright 2021 Camptocamp SA +# Copyright 2024 Michael Tietz (MT Software) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +from freezegun import freeze_time + from odoo import fields from odoo.exceptions import AccessError from odoo.tests import SavepointCase @@ -10,8 +13,26 @@ class TestStockLocation(SavepointCase): def setUpClass(cls): super().setUpClass() cls.product = cls.env.ref("product.product_product_7") - cls.leaf_location = cls.env.ref("stock.location_refrigerator_small") - cls.top_location = cls.leaf_location.location_id + cls.top_location = cls.env["stock.location"].create( + { + "name": "Top", + "location_id": cls.env.ref("stock.stock_location_locations").id, + } + ) + cls.leaf_location = cls.env["stock.location"].create( + {"name": "Leaf", "location_id": cls.top_location.id} + ) + cls.test_locations = cls.top_location | cls.leaf_location + cls.env["stock.quant"]._update_available_quantity( + cls.product, + cls.top_location, + 10, + ) + cls.env["stock.quant"]._update_available_quantity( + cls.product, + cls.leaf_location, + 10, + ) def _create_user(self, name, groups): return ( @@ -67,6 +88,13 @@ def test_leaf_location(self): inventory.action_validate() self.assertEqual(self.leaf_location.last_inventory_date, inventory.date) self.assertFalse(self.top_location.last_inventory_date) + locations = self.env["stock.location"].search( + [ + ("last_inventory_date", "<=", inventory.date), + ("id", "in", self.test_locations.ids), + ] + ) + self.assertEqual(locations, self.leaf_location) def test_top_location(self): inventory = self.env["stock.inventory"].create( @@ -80,3 +108,40 @@ def test_top_location(self): inventory.action_validate() self.assertEqual(self.leaf_location.last_inventory_date, inventory.date) self.assertEqual(self.top_location.last_inventory_date, inventory.date) + locations = self.env["stock.location"].search( + [ + ("last_inventory_date", "<=", inventory.date), + ("id", "in", self.test_locations.ids), + ] + ) + self.assertEqual(locations, self.test_locations) + + def test_top_and_leaf_location(self): + self.test_top_location() + top_inventory_date = self.top_location.last_inventory_date + with freeze_time(fields.Date.add(top_inventory_date, days=5)): + inventory = self.env["stock.inventory"].create( + { + "name": "Inventory Adjustment", + "product_ids": [(4, self.product.id)], + "location_ids": [(4, self.leaf_location.id)], + } + ) + inventory.action_start() + inventory.action_validate() + leaf_inventory_date = self.leaf_location.last_inventory_date + self.assertTrue(leaf_inventory_date > top_inventory_date) + locations = self.env["stock.location"].search( + [ + ("last_inventory_date", "<=", leaf_inventory_date), + ("id", "in", self.test_locations.ids), + ] + ) + self.assertEqual(locations, (self.leaf_location | self.top_location)) + locations = self.env["stock.location"].search( + [ + ("last_inventory_date", "<=", top_inventory_date), + ("id", "in", self.test_locations.ids), + ] + ) + self.assertEqual(locations, self.top_location)