diff --git a/setup/stock_location_children/odoo/addons/stock_location_children b/setup/stock_location_children/odoo/addons/stock_location_children new file mode 120000 index 000000000..094bfef70 --- /dev/null +++ b/setup/stock_location_children/odoo/addons/stock_location_children @@ -0,0 +1 @@ +../../../../stock_location_children \ No newline at end of file diff --git a/setup/stock_location_children/setup.py b/setup/stock_location_children/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/stock_location_children/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/stock_location_children/__init__.py b/stock_location_children/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/stock_location_children/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/stock_location_children/__manifest__.py b/stock_location_children/__manifest__.py new file mode 100644 index 000000000..614b259b3 --- /dev/null +++ b/stock_location_children/__manifest__.py @@ -0,0 +1,15 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +{ + "name": "Stock location children", + "summary": "Add relation between stock location and all its children", + "version": "13.0.1.0.0", + "development_status": "Alpha", + "category": "Warehouse Management", + "website": "https://github.com/OCA/stock-logistics-warehouse", + "author": "Camptocamp, Odoo Community Association (OCA)", + "license": "AGPL-3", + "application": False, + "installable": True, + "depends": ["stock"], +} diff --git a/stock_location_children/models/__init__.py b/stock_location_children/models/__init__.py new file mode 100644 index 000000000..88493e35d --- /dev/null +++ b/stock_location_children/models/__init__.py @@ -0,0 +1 @@ +from . import stock_location diff --git a/stock_location_children/models/stock_location.py b/stock_location_children/models/stock_location.py new file mode 100644 index 000000000..efe3d14b1 --- /dev/null +++ b/stock_location_children/models/stock_location.py @@ -0,0 +1,45 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +from odoo import api, fields, models + + +class StockLocation(models.Model): + + _inherit = "stock.location" + + children_ids = fields.Many2many( + "stock.location", + "stock_location_children_ids", + "parent_id", + "children_id", + compute="_compute_children_ids", + store=True, + help="All the children (multi-level) stock location of this location", + ) + + @api.depends("child_ids", "children_ids.child_ids") + def _compute_children_ids(self): + query = """SELECT sub.id, ARRAY_AGG(sl2.id) AS children + FROM stock_location sl2, + ( + SELECT id, parent_path + FROM stock_location sl + ) sub + WHERE sl2.parent_path LIKE sub.parent_path || '%%' + AND sl2.id != sub.id + AND sub.id IN %s + GROUP BY sub.id; + """ + self.flush(["location_id", "child_ids"]) + self.env.cr.execute(query, (tuple(self.ids),)) + rows = self.env.cr.dictfetchall() + for loc in self: + all_ids = [] + for row in rows: + if row.get("id") == loc.id: + all_ids = row.get("children") + break + if all_ids: + loc.children_ids = [(6, 0, all_ids)] + else: + loc.children_ids = [(5, 0, 0)] diff --git a/stock_location_children/readme/CONTRIBUTORS.rst b/stock_location_children/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..e31e2f0c4 --- /dev/null +++ b/stock_location_children/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Akim Juillerat diff --git a/stock_location_children/readme/DESCRIPTION.rst b/stock_location_children/readme/DESCRIPTION.rst new file mode 100644 index 000000000..6dd2b549b --- /dev/null +++ b/stock_location_children/readme/DESCRIPTION.rst @@ -0,0 +1,3 @@ +This module adds a `children_ids` field on `stock.location` in order to compute +and store all the children for a `stock.location` and not only its first level +children as is the case for `child_ids`. diff --git a/stock_location_children/tests/__init__.py b/stock_location_children/tests/__init__.py new file mode 100644 index 000000000..0bd71126f --- /dev/null +++ b/stock_location_children/tests/__init__.py @@ -0,0 +1 @@ +from . import test_stock_location_children diff --git a/stock_location_children/tests/test_stock_location_children.py b/stock_location_children/tests/test_stock_location_children.py new file mode 100644 index 000000000..773f64b8b --- /dev/null +++ b/stock_location_children/tests/test_stock_location_children.py @@ -0,0 +1,70 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +from odoo.tests import SavepointCase + + +class TestStockLocationChildren(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + ref = cls.env.ref + cls.stock_input = ref("stock.stock_location_company") + cls.stock_location = ref("stock.stock_location_stock") + cls.test_location = cls.env["stock.location"].create( + {"name": "Test Location", "location_id": cls.stock_location.id} + ) + cls.stock_shelf_1 = cls.env["stock.location"].create( + {"name": "Test Shelf 1", "location_id": cls.test_location.id} + ) + cls.stock_shelf_2 = cls.env["stock.location"].create( + {"name": "Test Shelf 2", "location_id": cls.test_location.id} + ) + cls.stock_shelf_2_refrigerator = cls.env["stock.location"].create( + {"name": "Test Shelf Refrigerator", "location_id": cls.stock_shelf_2.id} + ) + + def test_location_children(self): + self.assertFalse(self.stock_shelf_2_refrigerator.child_ids) + self.assertEqual(self.stock_shelf_2.child_ids, self.stock_shelf_2_refrigerator) + self.assertEqual(self.stock_shelf_2.child_ids, self.stock_shelf_2.children_ids) + self.assertFalse(self.stock_shelf_1.child_ids) + self.assertFalse(self.stock_shelf_1.children_ids) + self.assertEqual( + self.test_location.child_ids, self.stock_shelf_1 | self.stock_shelf_2 + ) + self.assertEqual( + self.test_location.children_ids, + self.stock_shelf_1 | self.stock_shelf_2 | self.stock_shelf_2_refrigerator, + ) + + def test_create_write_location(self): + refrigerator_drawer = self.env["stock.location"].create( + { + "name": "Refrigerator drawer", + "location_id": self.stock_shelf_2_refrigerator.id, + } + ) + self.assertEqual(self.stock_shelf_2_refrigerator.child_ids, refrigerator_drawer) + self.assertEqual( + self.stock_shelf_2_refrigerator.children_ids, refrigerator_drawer + ) + self.assertEqual( + self.stock_shelf_2.children_ids, + self.stock_shelf_2_refrigerator | refrigerator_drawer, + ) + self.assertEqual( + self.test_location.children_ids, + self.stock_shelf_1 + | self.stock_shelf_2 + | self.stock_shelf_2_refrigerator + | refrigerator_drawer, + ) + refrigerator_drawer.location_id = self.stock_input + self.assertFalse(self.stock_shelf_2_refrigerator.child_ids) + self.assertEqual(self.stock_shelf_2.child_ids, self.stock_shelf_2_refrigerator) + self.assertEqual(self.stock_shelf_2.child_ids, self.stock_shelf_2.children_ids) + self.assertEqual( + self.test_location.children_ids, + self.stock_shelf_1 | self.stock_shelf_2 | self.stock_shelf_2_refrigerator, + )