diff --git a/setup/stock_mts_mto_mrp_rule/odoo/addons/stock_mts_mto_mrp_rule b/setup/stock_mts_mto_mrp_rule/odoo/addons/stock_mts_mto_mrp_rule new file mode 120000 index 000000000..f3532788e --- /dev/null +++ b/setup/stock_mts_mto_mrp_rule/odoo/addons/stock_mts_mto_mrp_rule @@ -0,0 +1 @@ +../../../../stock_mts_mto_mrp_rule \ No newline at end of file diff --git a/setup/stock_mts_mto_mrp_rule/setup.py b/setup/stock_mts_mto_mrp_rule/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/stock_mts_mto_mrp_rule/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/stock_mts_mto_mrp_rule/README.rst b/stock_mts_mto_mrp_rule/README.rst new file mode 100644 index 000000000..e69de29bb diff --git a/stock_mts_mto_mrp_rule/__init__.py b/stock_mts_mto_mrp_rule/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/stock_mts_mto_mrp_rule/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/stock_mts_mto_mrp_rule/__manifest__.py b/stock_mts_mto_mrp_rule/__manifest__.py new file mode 100644 index 000000000..37c520d18 --- /dev/null +++ b/stock_mts_mto_mrp_rule/__manifest__.py @@ -0,0 +1,14 @@ +{ + "name": "Stock MTS+MTO MRP Rule", + "summary": "Stock MTS+MTO MRP Rule", + "version": "14.0.1.0.0", + "category": "Warehouse", + "website": "https://github.com/OCA/stock-logistics-warehouse", + "author": "Cetmix, Odoo Community Association (OCA)", + "maintainers": ["geomer198", "CetmixGitDrone"], + "license": "AGPL-3", + "depends": ["mrp", "stock_mts_mto_rule"], + "data": [], + "application": False, + "installable": True, +} diff --git a/stock_mts_mto_mrp_rule/models/__init__.py b/stock_mts_mto_mrp_rule/models/__init__.py new file mode 100644 index 000000000..6bda2d242 --- /dev/null +++ b/stock_mts_mto_mrp_rule/models/__init__.py @@ -0,0 +1 @@ +from . import stock_move diff --git a/stock_mts_mto_mrp_rule/models/stock_move.py b/stock_mts_mto_mrp_rule/models/stock_move.py new file mode 100644 index 000000000..e209d16c2 --- /dev/null +++ b/stock_mts_mto_mrp_rule/models/stock_move.py @@ -0,0 +1,57 @@ +from odoo import models +from odoo.tools import float_compare, float_is_zero + + +class StockMove(models.Model): + _inherit = "stock.move" + + def _prepare_move_split_vals(self, qty): + vals = super(StockMove, self)._prepare_move_split_vals(qty) + if self._context.get("changed_purchase_method"): + vals.update(procure_method="make_to_order") + return vals + + def _split(self, qty, restrict_partner_id=False): + new_move_vals = super(StockMove, self)._split( + qty, restrict_partner_id=restrict_partner_id + ) + if self._context.get("changed_purchase_method"): + self.write({"procure_method": "make_to_stock"}) + return new_move_vals + + def _adjust_procure_method(self): + precision = self.env["decimal.precision"].precision_get( + "Product Unit of Measure" + ) + for move in self: + product_id = move.product_id + domain = [ + ("location_src_id", "=", move.location_id.id), + ("location_id", "=", move.location_dest_id.id), + ("action", "!=", "push"), + ] + rules = self.env["procurement.group"]._search_rule( + False, product_id, move.warehouse_id, domain + ) + if not rules or rules and rules.action != "split_procurement": + super(StockMove, move)._adjust_procure_method() + else: + needed_qty = rules.get_mto_qty_to_order( + product_id, move.product_qty, move.product_uom, {} + ) + if float_is_zero(needed_qty, precision_digits=precision): + move.procure_method = "make_to_stock" + elif ( + float_compare( + needed_qty, move.product_qty, precision_digits=precision + ) + == 0.0 + ): + move.procure_method = "make_to_order" + else: + self.create( + move.with_context(changed_purchase_method=True)._split( + needed_qty + ) + ) + move._action_assign() diff --git a/stock_mts_mto_mrp_rule/readme/CONTRIBUTORS.rst b/stock_mts_mto_mrp_rule/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..e3a1f2ff2 --- /dev/null +++ b/stock_mts_mto_mrp_rule/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Ooops404 +* Cetmix diff --git a/stock_mts_mto_mrp_rule/readme/DESCRIPTION.rst b/stock_mts_mto_mrp_rule/readme/DESCRIPTION.rst new file mode 100644 index 000000000..74eba99ae --- /dev/null +++ b/stock_mts_mto_mrp_rule/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +This module adds an implementation for using the procure method that is set in rule (MTS/MTO). diff --git a/stock_mts_mto_mrp_rule/static/description/icon.png b/stock_mts_mto_mrp_rule/static/description/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/stock_mts_mto_mrp_rule/static/description/icon.png differ diff --git a/stock_mts_mto_mrp_rule/tests/__init__.py b/stock_mts_mto_mrp_rule/tests/__init__.py new file mode 100644 index 000000000..4b33b6f53 --- /dev/null +++ b/stock_mts_mto_mrp_rule/tests/__init__.py @@ -0,0 +1 @@ +from . import test_stock_move diff --git a/stock_mts_mto_mrp_rule/tests/common.py b/stock_mts_mto_mrp_rule/tests/common.py new file mode 100644 index 000000000..210bb7f5f --- /dev/null +++ b/stock_mts_mto_mrp_rule/tests/common.py @@ -0,0 +1,77 @@ +from odoo.tests import SavepointCase + + +class TestCommon(SavepointCase): + @classmethod + def setUpClass(cls): + super(TestCommon, cls).setUpClass() + + Product = cls.env["product.product"] + ProcurementGroup = cls.env["procurement.group"] + StockLocationRoute = cls.env["stock.location.route"] + StockRule = cls.env["stock.rule"] + StockQuant = cls.env["stock.quant"] + + cls.warehouse = cls.env.ref("stock.warehouse0") + + route_mto_mts = cls.env.ref("stock_mts_mto_rule.route_mto_mts") + + location_stock_id = cls.env.ref("stock.stock_location_stock").id + + dummy_route = StockLocationRoute.create( + { + "name": "dummy route", + "warehouse_selectable": True, + } + ) + + cls.warehouse.write( + {"route_ids": [(4, dummy_route.id)], "mto_mts_management": True} + ) + + cls.product = Product.create( + { + "name": "Test Product", + "type": "product", + "route_ids": [(6, 0, route_mto_mts.ids)], + } + ) + + cls.group = ProcurementGroup.create({"name": "test"}) + cls.dummy_rule = StockRule.create( + { + "name": "dummy rule", + "location_id": location_stock_id, + "location_src_id": cls.env.ref("stock.stock_location_suppliers").id, + "action": "pull", + "warehouse_id": cls.warehouse.id, + "picking_type_id": cls.env.ref("stock.picking_type_out").id, + "route_id": dummy_route.id, + } + ) + + cls.quant = StockQuant.create( + { + "owner_id": cls.env.ref("base.main_partner").id, + "location_id": location_stock_id, + "product_id": cls.product.id, + "quantity": 3, + } + ) + + def run_procurement_group_and_get_stock_move(self, qty): + self.env["procurement.group"].run( + [ + self.group.Procurement( + self.product, + qty, + self.env.ref("uom.product_uom_unit"), + self.env.ref("stock.stock_location_customers"), + self.product.name, + "test", + self.warehouse.company_id, + {"warehouse_id": self.warehouse, "group_id": self.group}, + ) + ] + ) + return self.env["stock.move"].search([("group_id", "=", self.group.id)]) diff --git a/stock_mts_mto_mrp_rule/tests/test_stock_move.py b/stock_mts_mto_mrp_rule/tests/test_stock_move.py new file mode 100644 index 000000000..47f352e69 --- /dev/null +++ b/stock_mts_mto_mrp_rule/tests/test_stock_move.py @@ -0,0 +1,50 @@ +from .common import TestCommon + + +class TestStockMove(TestCommon): + def test_adjust_procure_method_make_to_stock(self): + moves = self.run_procurement_group_and_get_stock_move(1.0) + self.assertEqual(len(moves), 1, msg="Count records must be equal to 1") + moves._adjust_procure_method() + self.assertEqual( + moves.procure_method, + "make_to_stock", + msg="Procure method must be equal to 'make_to_stock'", + ) + + def test_adjust_procure_method_make_to_order(self): + moves = self.run_procurement_group_and_get_stock_move(4.0) + self.assertEqual(len(moves), 3, msg="Count records must be equal to 3") + moves._adjust_procure_method() + expected_list = ["make_to_order", "make_to_order", "make_to_stock"] + self.assertListEqual( + moves.mapped("procure_method"), expected_list, msg="Lists must be the same" + ) + + def test_adjust_procure_method_make_to_stock_and_copy_stock_move(self): + moves = self.run_procurement_group_and_get_stock_move(2.0) + self.assertEqual(len(moves), 1, msg="Count records must be equal to 1") + moves._adjust_procure_method() + self.assertEqual( + moves.product_uom_qty, 1, msg="Product UOM Qty must be equal to 1" + ) + self.assertEqual( + moves.procure_method, + "make_to_stock", + msg="Procure method must be equal to 'make_to_stock'", + ) + move_copy = self.env["stock.move"].search( + [ + ("product_id", "=", self.product.id), + ("id", "!=", moves.id), + ] + ) + self.assertEqual(len(move_copy), 1, msg="Count records must be equal to 1") + self.assertEqual( + move_copy.product_uom_qty, 1, msg="Product UOM Qty must be equal to 1" + ) + self.assertEqual( + move_copy.procure_method, + "make_to_order", + msg="Procure method must be equal to 'make_to_order'", + )