Skip to content

Commit 3d81b69

Browse files
committed
[FIX] mto_as_mts_orderpoint_product_variant: Refactor for archival
There were multiple issues: - Inverse function on storable field is not reliable and we should prefer overriding write to add any processing when the field value is changed. Moreover, the field is_mto on product.product did not have its inverse function set on the field, so the function was not called. - When mto_as_mts is changed on the warehouse, we do not want to archive MTO rule for this warehouse (because we could still want to do MTO but not "as MTS").
1 parent d634638 commit 3d81b69

File tree

8 files changed

+90
-54
lines changed

8 files changed

+90
-54
lines changed

sale_stock_mto_as_mts_orderpoint/models/product_product.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from odoo import models
55

6+
67
class ProductProduct(models.Model):
78
_inherit = "product.product"
89

sale_stock_mto_as_mts_orderpoint/models/stock_warehouse.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,29 @@ class StockWarehouse(models.Model):
77

88
_inherit = "stock.warehouse"
99

10-
mto_as_mts = fields.Boolean(inverse="_inverse_mto_as_mts")
10+
mto_as_mts = fields.Boolean()
1111

1212
def _get_locations_for_mto_orderpoints(self):
1313
return self.mapped("lot_stock_id")
1414

15-
def _inverse_mto_as_mts(self):
16-
mto_route = self.env.ref("stock.route_warehouse0_mto", raise_if_not_found=False)
17-
if not mto_route:
18-
return
15+
def write(self, vals):
16+
res = super().write(vals)
17+
if "mto_as_mts" in vals and vals["mto_as_mts"] == False:
18+
self._archive_orderpoints_on_mts_mto_removal()
19+
return res
20+
21+
def _archive_orderpoints_on_mts_mto_removal(self):
1922
for warehouse in self:
20-
if warehouse.mto_as_mts:
21-
wh_mto_rules = self.env["stock.rule"].search(
22-
[
23-
("route_id", "=", mto_route.id),
24-
"|",
25-
("warehouse_id", "=", warehouse.id),
26-
("picking_type_id.warehouse_id", "=", warehouse.id),
27-
]
28-
)
29-
if wh_mto_rules:
30-
wh_mto_rules.active = False
23+
domain = warehouse._get_orderpoints_to_archive_domain()
24+
orderpoints = self.env["stock.warehouse.orderpoint"].search(domain)
25+
if orderpoints:
26+
orderpoints.write({"active": False})
27+
28+
def _get_orderpoints_to_archive_domain(self):
29+
self.ensure_one()
30+
locations = self._get_locations_for_mto_orderpoints()
31+
return [
32+
("product_min_qty", "=", 0.0),
33+
("product_max_qty", "=", 0.0),
34+
("location_id", "in", locations.ids),
35+
]
Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
# Copyright 2023 Camptocamp SA
22
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
33

4-
from odoo import api, models
4+
from odoo import models
5+
from odoo.osv.expression import AND
6+
57

68
class ProductProduct(models.Model):
79
_inherit = "product.product"
@@ -10,32 +12,28 @@ def _variant_is_mto(self):
1012
self.ensure_one()
1113
return self.is_mto
1214

13-
def _inverse_is_mto(self):
14-
res = super()._inverse_is_mto()
15-
self._archive_orderpoints_on_mto_removal()
16-
return res
17-
18-
@api.depends("product_tmpl_id.route_ids")
19-
def _compute_is_mto(self):
20-
# Archive orderpoints when variant becomes not mto
21-
res = super()._compute_is_mto()
22-
self._archive_orderpoints_on_mto_removal()
15+
def write(self, vals):
16+
res = super().write(vals)
17+
if "is_mto" in vals and vals["is_mto"] == False:
18+
self._archive_orderpoints_on_mto_removal()
2319
return res
2420

25-
def _get_orderpoints_to_archive_domain(self):
21+
def _get_orderpoints_to_archive_domain(self, warehouse):
2622
# Orderpoints to archive are those where
27-
warehouses = self.env["stock.warehouse"].search([])
28-
locations = warehouses._get_locations_for_mto_orderpoints()
29-
return [
30-
("product_id", "in", self.ids),
31-
("product_min_qty", "=", 0.0),
32-
("product_max_qty", "=", 0.0),
33-
("location_id", "in", locations.ids),
34-
("product_id.is_mto", "=", False),
35-
]
23+
domain = warehouse._get_orderpoints_to_archive_domain()
24+
if self:
25+
domain = AND(
26+
[
27+
domain,
28+
[("product_id", "in", self.ids)],
29+
]
30+
)
31+
return domain
3632

3733
def _archive_orderpoints_on_mto_removal(self):
38-
domain = self._get_orderpoints_to_archive_domain()
39-
ops = self.env["stock.warehouse.orderpoint"].search(domain)
40-
if ops:
41-
ops.write({"active": False})
34+
warehouses = self.env["stock.warehouse"].search([])
35+
for wh in warehouses:
36+
domain = self._get_orderpoints_to_archive_domain(wh)
37+
ops = self.env["stock.warehouse.orderpoint"].search(domain)
38+
if ops:
39+
ops.write({"active": False})

sale_stock_mto_as_mts_orderpoint_product_variant/tests/test_mto_as_mts_variant.py

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
33

44
from odoo.tests import Form
5+
56
from odoo.addons.stock_product_variant_mto.tests.common import TestMTOVariantCommon
67

78

89
class TestMtoAsMtsVariant(TestMTOVariantCommon):
9-
1010
@classmethod
1111
def setUpClass(cls):
1212
super().setUpClass()
@@ -25,6 +25,7 @@ def setUpClass(cls):
2525
]
2626
)
2727
cls.warehouse = cls.env.ref("stock.warehouse0")
28+
cls.warehouse.mto_as_mts = True
2829

2930
@classmethod
3031
def setUpClassProduct(cls):
@@ -49,21 +50,41 @@ def _get_orderpoint_for_products(self, products, archived=False):
4950
orderpoint = self.env["stock.warehouse.orderpoint"]
5051
if archived:
5152
orderpoint = orderpoint.with_context(active_test=False)
52-
return orderpoint.search(
53-
[("product_id", "in", products.ids)]
54-
)
53+
return orderpoint.search([("product_id", "in", products.ids)])
54+
55+
def test_mto_as_mts_orderpoint_warehouse_archive(self):
56+
black_pen = self.black_pen
57+
order = self._create_sale_order(black_pen)
58+
order.action_confirm()
59+
orderpoint = self._get_orderpoint_for_products(black_pen)
60+
self.assertTrue(orderpoint)
61+
self.warehouse.mto_as_mts = False
62+
orderpoint = self._get_orderpoint_for_products(black_pen)
63+
self.assertFalse(orderpoint)
64+
65+
def test_mto_as_mts_orderpoint_product_archive(self):
66+
black_pen = self.black_pen
67+
order = self._create_sale_order(black_pen)
68+
order.action_confirm()
69+
orderpoint = self._get_orderpoint_for_products(black_pen)
70+
self.assertTrue(orderpoint)
71+
self.toggle_is_mto(black_pen)
72+
orderpoint = self._get_orderpoint_for_products(black_pen)
73+
self.assertFalse(orderpoint)
5574

5675
def test_mto_as_mts_orderpoint(self):
57-
template_pen = self.template_pen
5876
black_pen = self.black_pen
5977
blue_pen = self.blue_pen
6078
red_pen = self.red_pen
6179
green_pen = self.green_pen
6280
order = self._create_sale_order(black_pen)
6381
orderpoint = self._get_orderpoint_for_products(black_pen)
6482
self.assertFalse(orderpoint)
83+
self.assertIn(self.mto_route, black_pen.route_ids)
84+
self.assertTrue(black_pen.is_mto)
6585
order.action_confirm()
6686
orderpoint = self._get_orderpoint_for_products(black_pen)
87+
self.assertTrue(orderpoint)
6788
self.assertEqual(
6889
orderpoint.location_id,
6990
self.warehouse._get_locations_for_mto_orderpoints(),
@@ -82,17 +103,24 @@ def test_mto_as_mts_orderpoint(self):
82103
self.assertFalse(self._get_orderpoint_for_products(black_pen))
83104
self.assertTrue(self._get_orderpoint_for_products(black_pen, archived=True))
84105
other_pens = red_pen | green_pen | blue_pen
85-
self.assertEqual(
86-
len(self._get_orderpoint_for_products(other_pens)), 3
87-
)
106+
self.assertEqual(len(self._get_orderpoint_for_products(other_pens)), 3)
88107

89108
def test_mtp_as_mts_orderpoint_product_no_mto(self):
109+
self.assertIn(self.buy_route, self.template_pen.route_ids)
90110
template_pen = self.template_pen
91111
black_pen = self.black_pen
92112
variants_pen = self.variants_pen
113+
self.assertIn(self.buy_route, black_pen.route_ids)
93114
# set everything to not mto
94-
template_pen.route_ids = False
115+
template_pen.write({"route_ids": [(3, self.mto_route.id, 0)]})
116+
self.assertIn(self.buy_route, black_pen.route_ids)
117+
self.assertNotIn(self.mto_route, black_pen.route_ids)
118+
self.toggle_is_mto(variants_pen)
119+
self.assertIn(self.mto_route, black_pen.route_ids)
120+
self.assertIn(self.buy_route, black_pen.route_ids)
95121
self.toggle_is_mto(variants_pen)
122+
self.assertNotIn(self.mto_route, black_pen.route_ids)
123+
self.assertIn(self.buy_route, black_pen.route_ids)
96124
# then check that no orderpoint is created
97125
order = self._create_sale_order(black_pen)
98126
orderpoint = self.env["stock.warehouse.orderpoint"].search(

stock_product_variant_mto/__manifest__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"version": "14.0.1.0.0",
88
"development_status": "Alpha",
99
"category": "Inventory",
10-
"website": "https://github.com/OCA/stock-workflow",
10+
"website": "https://github.com/OCA/stock-logistics-workflow",
1111
"author": "Camptocamp SA, Odoo Community Association (OCA)",
1212
"maintainers": ["mmequignon"],
1313
"license": "AGPL-3",

stock_product_variant_mto/models/product_product.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Copyright 2023 Camptocamp SA
22
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
33

4-
from odoo import api, models, fields
4+
from odoo import api, fields, models
55

66
IS_MTO_HELP = """
77
Check or Uncheck this field to enable the Make To Order on the variant,
@@ -10,6 +10,7 @@
1010
will reset this setting on its variants.
1111
"""
1212

13+
1314
class ProductProduct(models.Model):
1415
_inherit = "product.product"
1516

@@ -25,7 +26,7 @@ class ProductProduct(models.Model):
2526
"stock.location.route",
2627
compute="_compute_route_ids",
2728
domain="[('product_selectable', '=', True)]",
28-
store=False
29+
store=False,
2930
)
3031

3132
def _compute_is_mto(self):

stock_product_variant_mto/models/product_template.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# Copyright 2023 Camptocamp SA
22
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
33

4-
from odoo import _, api, models, fields
4+
from odoo import _, api, models
5+
56

67
class ProductTemplate(models.Model):
78
_inherit = "product.template"

stock_product_variant_mto/views/product_product.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
<field name="model">product.product</field>
99
<field name="inherit_id" ref="product.product_normal_form_view"/>
1010
<field name="arch" type="xml">
11+
<!-- TODO: Set route_ids as readonly here to avoid any mess?
12+
And add tooltip to mention it is to be configured on product template -->
1113
<group name="operations" position="inside">
1214
<field name="is_mto"/>
1315
</group>

0 commit comments

Comments
 (0)