diff --git a/setup/stock_inventory_preparation_filter_pos/odoo/addons/stock_inventory_preparation_filter_pos b/setup/stock_inventory_preparation_filter_pos/odoo/addons/stock_inventory_preparation_filter_pos
new file mode 120000
index 000000000..060f10a66
--- /dev/null
+++ b/setup/stock_inventory_preparation_filter_pos/odoo/addons/stock_inventory_preparation_filter_pos
@@ -0,0 +1 @@
+../../../../stock_inventory_preparation_filter_pos
\ No newline at end of file
diff --git a/setup/stock_inventory_preparation_filter_pos/setup.py b/setup/stock_inventory_preparation_filter_pos/setup.py
new file mode 100644
index 000000000..28c57bb64
--- /dev/null
+++ b/setup/stock_inventory_preparation_filter_pos/setup.py
@@ -0,0 +1,6 @@
+import setuptools
+
+setuptools.setup(
+ setup_requires=['setuptools-odoo'],
+ odoo_addon=True,
+)
diff --git a/stock_inventory_preparation_filter/models/stock_inventory.py b/stock_inventory_preparation_filter/models/stock_inventory.py
index 9197f5796..2c67721ab 100644
--- a/stock_inventory_preparation_filter/models/stock_inventory.py
+++ b/stock_inventory_preparation_filter/models/stock_inventory.py
@@ -52,18 +52,26 @@ class StockInventory(models.Model):
product_domain = fields.Char("Domain", default=[("name", "ilike", "")])
def _action_start(self):
- Product = self.env["product.product"]
for inventory in self:
- products = Product.browse()
if inventory.state != "draft":
continue
- if inventory.filter == "categories":
- products = Product.search([("categ_id", "in", inventory.categ_ids.ids)])
- if inventory.filter == "lots":
- products = inventory.lot_ids.mapped("product_id")
- if inventory.filter == "domain":
- domain = safe_eval(inventory.product_domain)
- products = Product.search(domain)
- if products:
- inventory.product_ids = [(6, 0, products.ids)]
+ if inventory.filter:
+ products = inventory._prepare_inventory_filter()
+ if products:
+ inventory.product_ids = [(6, 0, products.ids)]
return super()._action_start()
+
+ def _prepare_inventory_filter(self):
+ # This method is designed to be inherited by other modules
+ # such as the OCA module stock_inventory_preparation_filter_pos
+ self.ensure_one()
+ Product = self.env["product.product"]
+ products = Product
+ if self.filter == "categories":
+ products = Product.search([("categ_id", "in", self.categ_ids.ids)])
+ elif self.filter == "lots":
+ products = self.lot_ids.product_id
+ elif self.filter == "domain":
+ domain = safe_eval(self.product_domain)
+ products = Product.search(domain)
+ return products
diff --git a/stock_inventory_preparation_filter/views/stock_inventory_view.xml b/stock_inventory_preparation_filter/views/stock_inventory_view.xml
index e71cd0168..a6ada2253 100644
--- a/stock_inventory_preparation_filter/views/stock_inventory_view.xml
+++ b/stock_inventory_preparation_filter/views/stock_inventory_view.xml
@@ -34,12 +34,12 @@
diff --git a/stock_inventory_preparation_filter_pos/README.rst b/stock_inventory_preparation_filter_pos/README.rst
new file mode 100644
index 000000000..afb488df6
--- /dev/null
+++ b/stock_inventory_preparation_filter_pos/README.rst
@@ -0,0 +1 @@
+Will be generated from readme subdir
diff --git a/stock_inventory_preparation_filter_pos/__init__.py b/stock_inventory_preparation_filter_pos/__init__.py
new file mode 100644
index 000000000..0650744f6
--- /dev/null
+++ b/stock_inventory_preparation_filter_pos/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/stock_inventory_preparation_filter_pos/__manifest__.py b/stock_inventory_preparation_filter_pos/__manifest__.py
new file mode 100644
index 000000000..b54ddb987
--- /dev/null
+++ b/stock_inventory_preparation_filter_pos/__manifest__.py
@@ -0,0 +1,17 @@
+# Copyright 2021 Akretion France (http://www.akretion.com/)
+# @author: Alexis de Lattre
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+{
+ "name": "Inventory Preparation Filters POS",
+ "version": "14.0.1.0.0",
+ "license": "AGPL-3",
+ "summary": "Add POS category filter on inventory adjustments",
+ "depends": ["stock_inventory_preparation_filter", "point_of_sale"],
+ "author": "Akretion, Odoo Community Association (OCA)",
+ "maintainers": ["alexis-via"],
+ "category": "Inventory, Logistic, Storage",
+ "website": "https://github.com/OCA/stock-logistics-warehouse",
+ "data": ["views/stock_inventory.xml"],
+ "installable": True,
+}
diff --git a/stock_inventory_preparation_filter_pos/models/__init__.py b/stock_inventory_preparation_filter_pos/models/__init__.py
new file mode 100644
index 000000000..35536816e
--- /dev/null
+++ b/stock_inventory_preparation_filter_pos/models/__init__.py
@@ -0,0 +1 @@
+from . import stock_inventory
diff --git a/stock_inventory_preparation_filter_pos/models/stock_inventory.py b/stock_inventory_preparation_filter_pos/models/stock_inventory.py
new file mode 100644
index 000000000..739d822a4
--- /dev/null
+++ b/stock_inventory_preparation_filter_pos/models/stock_inventory.py
@@ -0,0 +1,32 @@
+# Copyright 2021 Akretion France (http://www.akretion.com/)
+# @author: Alexis de Lattre
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo import _, api, fields, models
+
+
+class StockInventory(models.Model):
+ _inherit = "stock.inventory"
+
+ pos_categ_ids = fields.Many2many(
+ "pos.category",
+ string="Point of Sale Categories",
+ readonly=True,
+ states={"draft": [("readonly", False)]},
+ )
+
+ @api.model
+ def _selection_filter(self):
+ res_filter = super()._selection_filter()
+ res_filter.insert(
+ -1, ("pos_categories", _("Selected Point of Sale Categories"))
+ )
+ return res_filter
+
+ def _prepare_inventory_filter(self):
+ products = super()._prepare_inventory_filter()
+ if self.filter == "pos_categories":
+ products = self.env["product.product"].search(
+ [("pos_categ_id", "child_of", self.pos_categ_ids.ids)]
+ )
+ return products
diff --git a/stock_inventory_preparation_filter_pos/readme/CONTRIBUTORS.rst b/stock_inventory_preparation_filter_pos/readme/CONTRIBUTORS.rst
new file mode 100644
index 000000000..ff65d68ce
--- /dev/null
+++ b/stock_inventory_preparation_filter_pos/readme/CONTRIBUTORS.rst
@@ -0,0 +1 @@
+* Alexis de Lattre
diff --git a/stock_inventory_preparation_filter_pos/readme/DESCRIPTION.rst b/stock_inventory_preparation_filter_pos/readme/DESCRIPTION.rst
new file mode 100644
index 000000000..cfe7ec6ca
--- /dev/null
+++ b/stock_inventory_preparation_filter_pos/readme/DESCRIPTION.rst
@@ -0,0 +1 @@
+with this module, when you create an inventory, you can filter the inventory on one or several point of sale categories.
diff --git a/stock_inventory_preparation_filter_pos/readme/USAGE.rst b/stock_inventory_preparation_filter_pos/readme/USAGE.rst
new file mode 100644
index 000000000..f64aa0877
--- /dev/null
+++ b/stock_inventory_preparation_filter_pos/readme/USAGE.rst
@@ -0,0 +1,3 @@
+Go to the menu *Inventory > Operations > Inventory Adjustments*, create a new inventory, choose **Selected Point of Sale Categories** and then select the point of sale categories that you want to filter on.
+
+Note that, if you select a parent point of sale category, Odoo will also select the products attached to the children point of sale categories.
diff --git a/stock_inventory_preparation_filter_pos/tests/__init__.py b/stock_inventory_preparation_filter_pos/tests/__init__.py
new file mode 100644
index 000000000..b64a88dad
--- /dev/null
+++ b/stock_inventory_preparation_filter_pos/tests/__init__.py
@@ -0,0 +1 @@
+from . import test_preparation_filter_pos
diff --git a/stock_inventory_preparation_filter_pos/tests/test_preparation_filter_pos.py b/stock_inventory_preparation_filter_pos/tests/test_preparation_filter_pos.py
new file mode 100644
index 000000000..11191d9e6
--- /dev/null
+++ b/stock_inventory_preparation_filter_pos/tests/test_preparation_filter_pos.py
@@ -0,0 +1,80 @@
+# Copyright 2021 Akretion France (http://www.akretion.com/)
+# @author: Alexis de Lattre
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo.tests import common
+
+
+class TestStockInventoryPreparationFilterPos(common.TransactionCase):
+ def setUp(self):
+ super().setUp()
+ self.location = self.env.ref("stock.stock_location_stock")
+ ppo = self.env["product.product"]
+ pcateg_id = self.env["product.category"].create({"name": "Test1"}).id
+ self.pos_categ1 = self.env["pos.category"].create({"name": "Test POS Categ"})
+ self.pos_categ2 = self.env["pos.category"].create(
+ {"name": "Test POS Categ2", "parent_id": self.pos_categ1.id}
+ )
+ # Create products
+ self.product1 = ppo.create(
+ {
+ "name": "Product POS test1",
+ "type": "product",
+ "categ_id": pcateg_id,
+ "pos_categ_id": self.pos_categ1.id,
+ }
+ )
+ self.product2 = ppo.create(
+ {
+ "name": "Product POS test2",
+ "type": "product",
+ "categ_id": pcateg_id,
+ "pos_categ_id": self.pos_categ1.id,
+ }
+ )
+ self.product3 = ppo.create(
+ {
+ "name": "Product POS test3",
+ "type": "product",
+ "categ_id": pcateg_id,
+ "pos_categ_id": self.pos_categ2.id,
+ }
+ )
+
+ # Add stock levels for these products
+ self.env["stock.quant"].create(
+ [
+ {
+ "product_id": self.product1.id,
+ "product_uom_id": self.product1.uom_id.id,
+ "location_id": self.location.id,
+ "quantity": 42.0,
+ },
+ {
+ "product_id": self.product2.id,
+ "product_uom_id": self.product2.uom_id.id,
+ "location_id": self.location.id,
+ "quantity": 43.0,
+ },
+ {
+ "product_id": self.product3.id,
+ "product_uom_id": self.product3.uom_id.id,
+ "location_id": self.location.id,
+ "quantity": 44.0,
+ },
+ ]
+ )
+
+ def test_inventory_filter_pos(self):
+ inventory = self.env["stock.inventory"].create(
+ {
+ "name": "Test POS filter",
+ "filter": "pos_categories",
+ "pos_categ_ids": [(6, 0, self.pos_categ1.ids)],
+ }
+ )
+ inventory.action_start()
+ # We make sure that the products of children categs are also included
+ self.assertEqual(len(inventory.product_ids), 3)
+ self.assertEqual(len(inventory.line_ids), 3)
+ inventory.action_cancel_draft()
diff --git a/stock_inventory_preparation_filter_pos/views/stock_inventory.xml b/stock_inventory_preparation_filter_pos/views/stock_inventory.xml
new file mode 100644
index 000000000..0ecbc1888
--- /dev/null
+++ b/stock_inventory_preparation_filter_pos/views/stock_inventory.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+ stock.inventory
+
+
+
+
+
+
+
+
+