diff --git a/sale_stock_mto_as_mts_orderpoint/models/product_product.py b/sale_stock_mto_as_mts_orderpoint/models/product_product.py index 77025fa524a2..fb3ade8240eb 100644 --- a/sale_stock_mto_as_mts_orderpoint/models/product_product.py +++ b/sale_stock_mto_as_mts_orderpoint/models/product_product.py @@ -3,6 +3,7 @@ from odoo import models + class ProductProduct(models.Model): _inherit = "product.product" diff --git a/sale_stock_mto_as_mts_orderpoint/models/stock_warehouse.py b/sale_stock_mto_as_mts_orderpoint/models/stock_warehouse.py index 936e883af639..b16056d6d3a2 100644 --- a/sale_stock_mto_as_mts_orderpoint/models/stock_warehouse.py +++ b/sale_stock_mto_as_mts_orderpoint/models/stock_warehouse.py @@ -7,24 +7,29 @@ class StockWarehouse(models.Model): _inherit = "stock.warehouse" - mto_as_mts = fields.Boolean(inverse="_inverse_mto_as_mts") + mto_as_mts = fields.Boolean() def _get_locations_for_mto_orderpoints(self): return self.mapped("lot_stock_id") - def _inverse_mto_as_mts(self): - mto_route = self.env.ref("stock.route_warehouse0_mto", raise_if_not_found=False) - if not mto_route: - return + def write(self, vals): + res = super().write(vals) + if "mto_as_mts" in vals and vals["mto_as_mts"] == False: + self._archive_orderpoints_on_mts_mto_removal() + return res + + def _archive_orderpoints_on_mts_mto_removal(self): for warehouse in self: - if warehouse.mto_as_mts: - wh_mto_rules = self.env["stock.rule"].search( - [ - ("route_id", "=", mto_route.id), - "|", - ("warehouse_id", "=", warehouse.id), - ("picking_type_id.warehouse_id", "=", warehouse.id), - ] - ) - if wh_mto_rules: - wh_mto_rules.active = False + domain = warehouse._get_orderpoints_to_archive_domain() + orderpoints = self.env["stock.warehouse.orderpoint"].search(domain) + if orderpoints: + orderpoints.write({"active": False}) + + def _get_orderpoints_to_archive_domain(self): + self.ensure_one() + locations = self._get_locations_for_mto_orderpoints() + return [ + ("product_min_qty", "=", 0.0), + ("product_max_qty", "=", 0.0), + ("location_id", "in", locations.ids), + ] diff --git a/sale_stock_mto_as_mts_orderpoint_product_variant/models/product_product.py b/sale_stock_mto_as_mts_orderpoint_product_variant/models/product_product.py index 6b6393c1bada..cf5f11f4ac96 100644 --- a/sale_stock_mto_as_mts_orderpoint_product_variant/models/product_product.py +++ b/sale_stock_mto_as_mts_orderpoint_product_variant/models/product_product.py @@ -1,7 +1,9 @@ # Copyright 2023 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -from odoo import api, models +from odoo import models +from odoo.osv.expression import AND + class ProductProduct(models.Model): _inherit = "product.product" @@ -10,32 +12,28 @@ def _variant_is_mto(self): self.ensure_one() return self.is_mto - def _inverse_is_mto(self): - res = super()._inverse_is_mto() - self._archive_orderpoints_on_mto_removal() - return res - - @api.depends("product_tmpl_id.route_ids") - def _compute_is_mto(self): - # Archive orderpoints when variant becomes not mto - res = super()._compute_is_mto() - self._archive_orderpoints_on_mto_removal() + def write(self, vals): + res = super().write(vals) + if "is_mto" in vals and vals["is_mto"] == False: + self._archive_orderpoints_on_mto_removal() return res - def _get_orderpoints_to_archive_domain(self): + def _get_orderpoints_to_archive_domain(self, warehouse): # Orderpoints to archive are those where - warehouses = self.env["stock.warehouse"].search([]) - locations = warehouses._get_locations_for_mto_orderpoints() - return [ - ("product_id", "in", self.ids), - ("product_min_qty", "=", 0.0), - ("product_max_qty", "=", 0.0), - ("location_id", "in", locations.ids), - ("product_id.is_mto", "=", False), - ] + domain = warehouse._get_orderpoints_to_archive_domain() + if self: + domain = AND( + [ + domain, + [("product_id", "in", self.ids)], + ] + ) + return domain def _archive_orderpoints_on_mto_removal(self): - domain = self._get_orderpoints_to_archive_domain() - ops = self.env["stock.warehouse.orderpoint"].search(domain) - if ops: - ops.write({"active": False}) + warehouses = self.env["stock.warehouse"].search([]) + for wh in warehouses: + domain = self._get_orderpoints_to_archive_domain(wh) + ops = self.env["stock.warehouse.orderpoint"].search(domain) + if ops: + ops.write({"active": False}) diff --git a/sale_stock_mto_as_mts_orderpoint_product_variant/tests/test_mto_as_mts_variant.py b/sale_stock_mto_as_mts_orderpoint_product_variant/tests/test_mto_as_mts_variant.py index 1dc1eed9d9be..a70c8087e0be 100644 --- a/sale_stock_mto_as_mts_orderpoint_product_variant/tests/test_mto_as_mts_variant.py +++ b/sale_stock_mto_as_mts_orderpoint_product_variant/tests/test_mto_as_mts_variant.py @@ -2,11 +2,11 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) from odoo.tests import Form + from odoo.addons.stock_product_variant_mto.tests.common import TestMTOVariantCommon class TestMtoAsMtsVariant(TestMTOVariantCommon): - @classmethod def setUpClass(cls): super().setUpClass() @@ -25,6 +25,7 @@ def setUpClass(cls): ] ) cls.warehouse = cls.env.ref("stock.warehouse0") + cls.warehouse.mto_as_mts = True @classmethod def setUpClassProduct(cls): @@ -49,12 +50,29 @@ def _get_orderpoint_for_products(self, products, archived=False): orderpoint = self.env["stock.warehouse.orderpoint"] if archived: orderpoint = orderpoint.with_context(active_test=False) - return orderpoint.search( - [("product_id", "in", products.ids)] - ) + return orderpoint.search([("product_id", "in", products.ids)]) + + def test_mto_as_mts_orderpoint_warehouse_archive(self): + black_pen = self.black_pen + order = self._create_sale_order(black_pen) + order.action_confirm() + orderpoint = self._get_orderpoint_for_products(black_pen) + self.assertTrue(orderpoint) + self.warehouse.mto_as_mts = False + orderpoint = self._get_orderpoint_for_products(black_pen) + self.assertFalse(orderpoint) + + def test_mto_as_mts_orderpoint_product_archive(self): + black_pen = self.black_pen + order = self._create_sale_order(black_pen) + order.action_confirm() + orderpoint = self._get_orderpoint_for_products(black_pen) + self.assertTrue(orderpoint) + self.toggle_is_mto(black_pen) + orderpoint = self._get_orderpoint_for_products(black_pen) + self.assertFalse(orderpoint) def test_mto_as_mts_orderpoint(self): - template_pen = self.template_pen black_pen = self.black_pen blue_pen = self.blue_pen red_pen = self.red_pen @@ -62,8 +80,11 @@ def test_mto_as_mts_orderpoint(self): order = self._create_sale_order(black_pen) orderpoint = self._get_orderpoint_for_products(black_pen) self.assertFalse(orderpoint) + self.assertIn(self.mto_route, black_pen.route_ids) + self.assertTrue(black_pen.is_mto) order.action_confirm() orderpoint = self._get_orderpoint_for_products(black_pen) + self.assertTrue(orderpoint) self.assertEqual( orderpoint.location_id, self.warehouse._get_locations_for_mto_orderpoints(), @@ -82,17 +103,24 @@ def test_mto_as_mts_orderpoint(self): self.assertFalse(self._get_orderpoint_for_products(black_pen)) self.assertTrue(self._get_orderpoint_for_products(black_pen, archived=True)) other_pens = red_pen | green_pen | blue_pen - self.assertEqual( - len(self._get_orderpoint_for_products(other_pens)), 3 - ) + self.assertEqual(len(self._get_orderpoint_for_products(other_pens)), 3) def test_mtp_as_mts_orderpoint_product_no_mto(self): + self.assertIn(self.buy_route, self.template_pen.route_ids) template_pen = self.template_pen black_pen = self.black_pen variants_pen = self.variants_pen + self.assertIn(self.buy_route, black_pen.route_ids) # set everything to not mto - template_pen.route_ids = False + template_pen.write({"route_ids": [(3, self.mto_route.id, 0)]}) + self.assertIn(self.buy_route, black_pen.route_ids) + self.assertNotIn(self.mto_route, black_pen.route_ids) + self.toggle_is_mto(variants_pen) + self.assertIn(self.mto_route, black_pen.route_ids) + self.assertIn(self.buy_route, black_pen.route_ids) self.toggle_is_mto(variants_pen) + self.assertNotIn(self.mto_route, black_pen.route_ids) + self.assertIn(self.buy_route, black_pen.route_ids) # then check that no orderpoint is created order = self._create_sale_order(black_pen) orderpoint = self.env["stock.warehouse.orderpoint"].search( diff --git a/stock_product_variant_mto/__manifest__.py b/stock_product_variant_mto/__manifest__.py index d647fa79f93b..6bb7ae957038 100644 --- a/stock_product_variant_mto/__manifest__.py +++ b/stock_product_variant_mto/__manifest__.py @@ -7,7 +7,7 @@ "version": "14.0.1.0.0", "development_status": "Alpha", "category": "Inventory", - "website": "https://github.com/OCA/stock-workflow", + "website": "https://github.com/OCA/stock-logistics-workflow", "author": "Camptocamp SA, Odoo Community Association (OCA)", "maintainers": ["mmequignon"], "license": "AGPL-3", diff --git a/stock_product_variant_mto/models/product_product.py b/stock_product_variant_mto/models/product_product.py index a2d3037934e4..4cf5b36427df 100644 --- a/stock_product_variant_mto/models/product_product.py +++ b/stock_product_variant_mto/models/product_product.py @@ -1,7 +1,7 @@ # Copyright 2023 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -from odoo import api, models, fields +from odoo import api, fields, models IS_MTO_HELP = """ Check or Uncheck this field to enable the Make To Order on the variant, @@ -10,6 +10,7 @@ will reset this setting on its variants. """ + class ProductProduct(models.Model): _inherit = "product.product" @@ -25,7 +26,7 @@ class ProductProduct(models.Model): "stock.location.route", compute="_compute_route_ids", domain="[('product_selectable', '=', True)]", - store=False + store=False, ) def _compute_is_mto(self): diff --git a/stock_product_variant_mto/models/product_template.py b/stock_product_variant_mto/models/product_template.py index e1680cc66629..faf0670d98dc 100644 --- a/stock_product_variant_mto/models/product_template.py +++ b/stock_product_variant_mto/models/product_template.py @@ -1,7 +1,8 @@ # Copyright 2023 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -from odoo import _, api, models, fields +from odoo import _, api, models + class ProductTemplate(models.Model): _inherit = "product.template" diff --git a/stock_product_variant_mto/views/product_product.xml b/stock_product_variant_mto/views/product_product.xml index 1e5720bae1e8..b83e92d7bfdd 100644 --- a/stock_product_variant_mto/views/product_product.xml +++ b/stock_product_variant_mto/views/product_product.xml @@ -8,6 +8,8 @@ product.product +