From d8464d3b3d7762ba3c667485588811a6d7a5cd15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A0n=20Todorovich?= Date: Mon, 12 Dec 2022 17:12:36 -0300 Subject: [PATCH] [ADD] mrp_production_availability_search --- .../README.rst | 1 + .../__init__.py | 1 + .../__manifest__.py | 16 +++ .../models/__init__.py | 1 + .../models/mrp_production.py | 45 ++++++++ .../readme/CONTRIBUTORS.rst | 3 + .../readme/DESCRIPTION.rst | 7 ++ .../tests/__init__.py | 1 + .../tests/test_search.py | 102 ++++++++++++++++++ .../views/mrp_production.xml | 23 ++++ ...p_production_component_availability_search | 1 + .../setup.py | 6 ++ 12 files changed, 207 insertions(+) create mode 100644 mrp_production_component_availability_search/README.rst create mode 100644 mrp_production_component_availability_search/__init__.py create mode 100644 mrp_production_component_availability_search/__manifest__.py create mode 100644 mrp_production_component_availability_search/models/__init__.py create mode 100644 mrp_production_component_availability_search/models/mrp_production.py create mode 100644 mrp_production_component_availability_search/readme/CONTRIBUTORS.rst create mode 100644 mrp_production_component_availability_search/readme/DESCRIPTION.rst create mode 100644 mrp_production_component_availability_search/tests/__init__.py create mode 100644 mrp_production_component_availability_search/tests/test_search.py create mode 100644 mrp_production_component_availability_search/views/mrp_production.xml create mode 120000 setup/mrp_production_component_availability_search/odoo/addons/mrp_production_component_availability_search create mode 100644 setup/mrp_production_component_availability_search/setup.py diff --git a/mrp_production_component_availability_search/README.rst b/mrp_production_component_availability_search/README.rst new file mode 100644 index 000000000..f86ee85e1 --- /dev/null +++ b/mrp_production_component_availability_search/README.rst @@ -0,0 +1 @@ +TO BE GENERATED diff --git a/mrp_production_component_availability_search/__init__.py b/mrp_production_component_availability_search/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/mrp_production_component_availability_search/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/mrp_production_component_availability_search/__manifest__.py b/mrp_production_component_availability_search/__manifest__.py new file mode 100644 index 000000000..67603695c --- /dev/null +++ b/mrp_production_component_availability_search/__manifest__.py @@ -0,0 +1,16 @@ +# Copyright 2022 Camptocamp SA (https://www.camptocamp.com). +# @author Iván Todorovich +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "MRP Component Availability Search", + "summary": "Filter manufacturing orders by their components availability state", + "version": "15.0.1.0.0", + "author": "Camptocamp, Odoo Community Association (OCA)", + "maintainers": ["ivantodorovich"], + "website": "https://github.com/OCA/manufacture", + "license": "AGPL-3", + "category": "Manufacture", + "depends": ["mrp"], + "data": ["views/mrp_production.xml"], +} diff --git a/mrp_production_component_availability_search/models/__init__.py b/mrp_production_component_availability_search/models/__init__.py new file mode 100644 index 000000000..a9e5f13e4 --- /dev/null +++ b/mrp_production_component_availability_search/models/__init__.py @@ -0,0 +1 @@ +from . import mrp_production diff --git a/mrp_production_component_availability_search/models/mrp_production.py b/mrp_production_component_availability_search/models/mrp_production.py new file mode 100644 index 000000000..0f73b8fbb --- /dev/null +++ b/mrp_production_component_availability_search/models/mrp_production.py @@ -0,0 +1,45 @@ +# Copyright 2022 Camptocamp SA (https://www.camptocamp.com). +# @author Iván Todorovich +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import _, api, fields, models +from odoo.exceptions import UserError +from odoo.osv.expression import OR, distribute_not, normalize_domain + + +class MrpProduction(models.Model): + _inherit = "mrp.production" + + components_availability_state = fields.Selection( + search="_search_components_availability_state" + ) + + @api.model + def _search_components_availability_state(self, operator, value): + if operator not in ("=", "!="): + raise UserError(_("Invalid domain operator %s", operator)) + if not isinstance(value, str) and value is not False: + raise UserError(_("Invalid domain right operand %s", value)) + # Search all orders for which we need to compute the products availability. + # The value would be ``False`` if the mo doesn't fall under this list. + domain = [("state", "in", ["confirmed", "progress"])] + # Special case for "is set" / "is not set" domains + if operator == "=" and value is False: + return distribute_not(["!"] + normalize_domain(domain)) + if operator == "!=" and value is False: + return domain + # Perform a search and compute values to filter on-the-fly + res_ids = [] + orders = self.with_context(prefetch_fields=False).search(domain) + for order in orders: + if order.components_availability_state == value: + res_ids.append(order.id) + if operator == "=": + return [("id", "in", res_ids)] + else: + return OR( + [ + distribute_not(["!"] + normalize_domain(domain)), + [("id", "not in", res_ids)], + ] + ) diff --git a/mrp_production_component_availability_search/readme/CONTRIBUTORS.rst b/mrp_production_component_availability_search/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..a1e0a8395 --- /dev/null +++ b/mrp_production_component_availability_search/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `Camptocamp `_ + + * Iván Todorovich diff --git a/mrp_production_component_availability_search/readme/DESCRIPTION.rst b/mrp_production_component_availability_search/readme/DESCRIPTION.rst new file mode 100644 index 000000000..534ac6bb9 --- /dev/null +++ b/mrp_production_component_availability_search/readme/DESCRIPTION.rst @@ -0,0 +1,7 @@ +Allows to search or filter manufacturing orders by their Components Availability State. + +To do so, a search method is implemented on that computed field, to compute and filter +records on the fly. + +For convenience, a filter named **Components Availability Issues** is added to the +search view. diff --git a/mrp_production_component_availability_search/tests/__init__.py b/mrp_production_component_availability_search/tests/__init__.py new file mode 100644 index 000000000..de6927da4 --- /dev/null +++ b/mrp_production_component_availability_search/tests/__init__.py @@ -0,0 +1 @@ +from . import test_search diff --git a/mrp_production_component_availability_search/tests/test_search.py b/mrp_production_component_availability_search/tests/test_search.py new file mode 100644 index 000000000..55512f552 --- /dev/null +++ b/mrp_production_component_availability_search/tests/test_search.py @@ -0,0 +1,102 @@ +# Copyright 2022 Camptocamp SA (https://www.camptocamp.com). +# @author Iván Todorovich +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import Command +from odoo.tests import Form, TransactionCase + + +class TestSearch(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.partner = cls.env.ref("base.res_partner_1") + cls.component = cls.env["product.product"].create( + {"name": "Component", "type": "product"} + ) + cls.product = cls.env["product.product"].create( + {"name": "Product", "type": "product"} + ) + cls.product_bom = cls.env["mrp.bom"].create( + { + "product_tmpl_id": cls.product.product_tmpl_id.id, + "product_qty": 1.0, + "product_uom_id": cls.product.uom_id.id, + "bom_line_ids": [ + Command.create( + { + "product_id": cls.component.id, + "product_qty": 1.0, + "product_uom_id": cls.component.uom_id.id, + } + ) + ], + } + ) + # Create some initial stocks + cls.location_stock = cls.env.ref("stock.stock_location_stock") + cls.env["stock.quant"].create( + { + "product_id": cls.component.id, + "product_uom_id": cls.component.uom_id.id, + "location_id": cls.location_stock.id, + "quantity": 10.00, + } + ) + # Create some manufacturing orders + cls.mo_draft = cls._create_mo(confirm=False) + cls.mo_confirm = cls._create_mo(confirm=True) + cls.mo_unavailable = cls._create_mo(quantity=1000.0, confirm=True) + + @classmethod + def _create_mo(cls, product=None, bom=None, quantity=1.0, confirm=False): + if product is None: + product = cls.product + if bom is None: + bom = cls.product_bom + mo_form = Form(cls.env["mrp.production"]) + mo_form.product_id = product + mo_form.bom_id = bom + mo_form.product_qty = quantity + mo_form.product_uom_id = product.uom_id + mo = mo_form.save() + if confirm: + mo.action_confirm() + return mo + + def test_search_is_set(self): + records = self.env["mrp.production"].search( + [ + ("product_id", "=", self.product.id), + ("components_availability_state", "!=", False), + ] + ) + self.assertEqual(records, self.mo_confirm + self.mo_unavailable) + + def test_search_is_not_set(self): + records = self.env["mrp.production"].search( + [ + ("product_id", "=", self.product.id), + ("components_availability_state", "=", False), + ] + ) + self.assertEqual(records, self.mo_draft) + + def test_search_is_available(self): + records = self.env["mrp.production"].search( + [ + ("product_id", "=", self.product.id), + ("components_availability_state", "=", "available"), + ] + ) + self.assertEqual(records, self.mo_confirm) + + def test_search_is_not_available(self): + records = self.env["mrp.production"].search( + [ + ("product_id", "=", self.product.id), + ("components_availability_state", "!=", "available"), + ] + ) + self.assertEqual(records, self.mo_draft + self.mo_unavailable) diff --git a/mrp_production_component_availability_search/views/mrp_production.xml b/mrp_production_component_availability_search/views/mrp_production.xml new file mode 100644 index 000000000..ae61f1225 --- /dev/null +++ b/mrp_production_component_availability_search/views/mrp_production.xml @@ -0,0 +1,23 @@ + + + + + + mrp.production + + + + + + + + + diff --git a/setup/mrp_production_component_availability_search/odoo/addons/mrp_production_component_availability_search b/setup/mrp_production_component_availability_search/odoo/addons/mrp_production_component_availability_search new file mode 120000 index 000000000..6b3d90c5a --- /dev/null +++ b/setup/mrp_production_component_availability_search/odoo/addons/mrp_production_component_availability_search @@ -0,0 +1 @@ +../../../../mrp_production_component_availability_search \ No newline at end of file diff --git a/setup/mrp_production_component_availability_search/setup.py b/setup/mrp_production_component_availability_search/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/mrp_production_component_availability_search/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)