Skip to content

Commit

Permalink
[MIG] stock_product_variant_mto: Migration to 18.0
Browse files Browse the repository at this point in the history
  • Loading branch information
chaule97 committed Jan 10, 2025
1 parent bee6c75 commit d557475
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 65 deletions.
9 changes: 9 additions & 0 deletions stock_product_variant_mto/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ Contributors

- Matthieu Méquignon <matthieu.mequignon@camptocamp.com>
- Akim Juillerat <akim.juillerat@camptocamp.com>
- Chau Le <chaulb@trobz.com>

Other credits
-------------

The development and migration of this module has been financially
supported by:

- Camptocamp

Maintainers
-----------
Expand Down
2 changes: 1 addition & 1 deletion stock_product_variant_mto/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{
"name": "Stock Product Variant MTO",
"summary": "Allow to individually set variants as MTO",
"version": "14.0.1.0.0",
"version": "18.0.1.0.0",
"development_status": "Alpha",
"category": "Inventory",
"website": "https://github.com/OCA/stock-logistics-workflow",
Expand Down
29 changes: 15 additions & 14 deletions stock_product_variant_mto/models/product_product.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,30 +23,31 @@ class ProductProduct(models.Model):
)

route_ids = fields.Many2many(
"stock.location.route",
"stock.route",
compute="_compute_route_ids",
domain="[('product_selectable', '=', True)]",
store=False,
store=True,
)

def _compute_is_mto(self):
mto_route = self.env.ref("stock.route_warehouse0_mto", raise_if_not_found=False)
if not mto_route:
self.update({"is_mto": False})
return

Check warning on line 36 in stock_product_variant_mto/models/product_product.py

View check run for this annotation

Codecov / codecov/patch

stock_product_variant_mto/models/product_product.py#L35-L36

Added lines #L35 - L36 were not covered by tests

for product in self:
if not mto_route:
product.is_mto = False
continue
product.is_mto = mto_route in product.product_tmpl_id.route_ids

@api.depends("is_mto", "product_tmpl_id.route_ids")
def _compute_route_ids(self):
mto_route = self.env.ref("stock.route_warehouse0_mto", raise_if_not_found=False)
for product in self:
if mto_route and mto_route in product.product_tmpl_id.route_ids:
if not product.is_mto:
product.route_ids = product.product_tmpl_id.route_ids - mto_route
continue
else:
if mto_route and product.is_mto:
product.route_ids = product.product_tmpl_id.route_ids + mto_route
continue
product.route_ids = product.product_tmpl_id.route_ids
new_route_ids = product.product_tmpl_id.route_ids

if mto_route:
if product.is_mto and mto_route not in new_route_ids:
new_route_ids += mto_route
elif not product.is_mto and mto_route in new_route_ids:
new_route_ids -= mto_route

product.route_ids = new_route_ids
62 changes: 38 additions & 24 deletions stock_product_variant_mto/models/product_template.py
Original file line number Diff line number Diff line change
@@ -1,58 +1,72 @@
# Copyright 2023 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

from odoo import _, api, models
from odoo import api, models


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

def write(self, values):
if "route_ids" not in values:
mto_route = self.env.ref("stock.route_warehouse0_mto", raise_if_not_found=False)

if "route_ids" not in values or not mto_route:
return super().write(values)

# As _compute_is_mto cannot use api.depends (or it would reset MTO
# route on variants as soon as there is a change on the template routes),
# we need to check which template in self had MTO route activated
# or deactivated to force the recomputation of is_mto on variants
mto_route = self.env.ref("stock.route_warehouse0_mto")
template_not_mto_before = self.filtered(lambda t: mto_route not in t.route_ids)

templates_not_mto_before = self.filtered(lambda t: mto_route not in t.route_ids)

res = super().write(values)

templates_mto_after = self.filtered(lambda t: mto_route in t.route_ids)
templates_mto_added = template_not_mto_before & templates_mto_after
templates_mto_removed = (self - template_not_mto_before) & (
self - templates_mto_after
)
(
templates_mto_added = templates_not_mto_before & templates_mto_after
templates_mto_removed = self - templates_mto_after - templates_not_mto_before

affected_product_variants = (
templates_mto_added | templates_mto_removed
).product_variant_ids._compute_is_mto()
).product_variant_ids
if affected_product_variants:
affected_product_variants._compute_is_mto()

return res

@api.onchange("route_ids")
def onchange_route_ids(self):
mto_route = self.env.ref("stock.route_warehouse0_mto", raise_if_not_found=False)
if (
mto_route not in self._origin.route_ids
and mto_route in self.route_ids._origin
):
if not mto_route:
return

Check warning on line 41 in stock_product_variant_mto/models/product_template.py

View check run for this annotation

Codecov / codecov/patch

stock_product_variant_mto/models/product_template.py#L41

Added line #L41 was not covered by tests

origin_routes = (
self._origin.route_ids if self._origin else self.env["stock.route"]
)
current_routes = (
self.route_ids._origin if self.route_ids else self.env["stock.route"]
)

if mto_route not in origin_routes and mto_route in current_routes:
# Return warning activating MTO route
return {
"warning": {
"title": _("Warning"),
"message": _(
"Activating MTO route will reset `Variant is MTO` setting on the variants."
"title": self.env._("Warning"),
"message": self.env._(
"Activating MTO route will reset `Variant is MTO` "
"setting on the variants."
),
}
}
if (
mto_route in self._origin.route_ids
and mto_route not in self.route_ids._origin
):

if mto_route in origin_routes and mto_route not in current_routes:
# Return warning deactivating MTO route
return {
"warning": {
"title": _("Warning"),
"message": _(
"Deactivating MTO route will reset `Variant is MTO` setting on the variants."
"title": self.env._("Warning"),
"message": self.env._(
"Deactivating MTO route will reset `Variant is MTO` "
"setting on the variants."
),
}
}
1 change: 1 addition & 0 deletions stock_product_variant_mto/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
- Matthieu Méquignon \<<matthieu.mequignon@camptocamp.com>\>
- Akim Juillerat \<<akim.juillerat@camptocamp.com>\>
- Chau Le \<<chaulb@trobz.com>\>
3 changes: 3 additions & 0 deletions stock_product_variant_mto/readme/CREDITS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
The development and migration of this module has been financially supported by:

- Camptocamp
14 changes: 12 additions & 2 deletions stock_product_variant_mto/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,8 @@ <h1 class="title">Stock Product Variant MTO</h1>
<li><a class="reference internal" href="#credits" id="toc-entry-2">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-3">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-4">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-5">Maintainers</a></li>
<li><a class="reference internal" href="#other-credits" id="toc-entry-5">Other credits</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-6">Maintainers</a></li>
</ul>
</li>
</ul>
Expand All @@ -411,10 +412,19 @@ <h2><a class="toc-backref" href="#toc-entry-4">Contributors</a></h2>
<ul class="simple">
<li>Matthieu Méquignon &lt;<a class="reference external" href="mailto:matthieu.mequignon&#64;camptocamp.com">matthieu.mequignon&#64;camptocamp.com</a>&gt;</li>
<li>Akim Juillerat &lt;<a class="reference external" href="mailto:akim.juillerat&#64;camptocamp.com">akim.juillerat&#64;camptocamp.com</a>&gt;</li>
<li>Chau Le &lt;<a class="reference external" href="mailto:chaulb&#64;trobz.com">chaulb&#64;trobz.com</a>&gt;</li>
</ul>
</div>
<div class="section" id="other-credits">
<h2><a class="toc-backref" href="#toc-entry-5">Other credits</a></h2>
<p>The development and migration of this module has been financially
supported by:</p>
<ul class="simple">
<li>Camptocamp</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-5">Maintainers</a></h2>
<h2><a class="toc-backref" href="#toc-entry-6">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
Expand Down
13 changes: 6 additions & 7 deletions stock_product_variant_mto/tests/common.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
# Copyright 2023 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

from odoo.tests.common import Form, SavepointCase
from odoo.tests import Form, tagged

from odoo.addons.base.tests.common import BaseCommon

class TestMTOVariantCommon(SavepointCase):
at_install = False
post_install = True

@tagged("post_install", "-at_install")
class TestMTOVariantCommon(BaseCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
cls.setUpClassProduct()

@classmethod
Expand Down Expand Up @@ -79,13 +78,13 @@ def toggle_is_mto(self, records):
record.is_mto = not record.is_mto

def assertVariantsMTO(self, records):
records.invalidate_cache(["is_mto"])
records.invalidate_recordset(["is_mto"])
self.assertTrue(all([record.is_mto for record in records]))
for rec in records:
self.assertIn(self.mto_route, rec.route_ids)

def assertVariantsNotMTO(self, records):
records.invalidate_cache(["is_mto"])
records.invalidate_recordset(["is_mto"])
self.assertFalse(any([record.is_mto for record in records]))
for rec in records:
self.assertNotIn(self.mto_route, rec.route_ids)
47 changes: 30 additions & 17 deletions stock_product_variant_mto/tests/test_mto_variant.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

from .common import TestMTOVariantCommon

onchange_logger = "odoo.tests.form.onchange"

_logger = logging.getLogger(onchange_logger)


class TestMTOVariant(TestMTOVariantCommon):
def test_variants_mto(self):
Expand All @@ -24,14 +28,16 @@ def test_variants_mto(self):
self.assertVariantsMTO(black_pen | blue_pen)
self.assertVariantsNotMTO(red_pen | green_pen)
# Now enable the mto route for the template, all variants get is_mto = True
self.add_route(pen_template, self.mto_route)
with self.assertLogs(onchange_logger, level="WARNING"):
self.add_route(pen_template, self.mto_route)
self.assertVariantsMTO(pens)
# Disable mto route for black_pen
self.toggle_is_mto(black_pen)
self.assertVariantsNotMTO(black_pen)
self.assertVariantsMTO(blue_pen | green_pen | red_pen)
# Disable mto route on the template, reset is_mto on variants
self.remove_route(pen_template, self.mto_route)
with self.assertLogs(onchange_logger, level="WARNING"):
self.remove_route(pen_template, self.mto_route)
self.assertVariantsNotMTO(pens)

def test_template_routes_updated(self):
Expand All @@ -44,7 +50,8 @@ def test_template_routes_updated(self):
black_pen = self.black_pen
self.assertVariantsNotMTO(pens)
# If template is set to MTO, all variants are updated
self.add_route(pen_template, self.mto_route)
with self.assertLogs(onchange_logger, level="WARNING"):
self.add_route(pen_template, self.mto_route)
self.assertVariantsMTO(pens)
# Now toggle a few variants to is_mto == False
self.toggle_is_mto(black_pen | blue_pen)
Expand All @@ -65,43 +72,49 @@ def test_template_warnings(self):
red_pen = self.red_pen
green_pen = self.green_pen
black_pen = self.black_pen
onchange_logger = logging.getLogger("odoo.tests.common.onchange")
self.assertVariantsNotMTO(pens)

# enable mto route for black pen
self.toggle_is_mto(black_pen)
self.assertVariantsMTO(black_pen)

# Enable mto route on the template, raise warning as is_mto is reset on variants
with self.assertLogs(onchange_logger) as log:
with self.assertLogs(onchange_logger, level="WARNING") as log_catcher:
self.add_route(pen_template, self.mto_route)
self.assertIn("WARNING", log.output[0])
self.assertIn("Activating MTO route will reset", log.output[0])
self.assertIn("WARNING", log_catcher.output[0])
self.assertIn("Activating MTO route will reset", log_catcher.output[0])
self.assertVariantsMTO(pens)

# Disable mto route for black pen
self.toggle_is_mto(black_pen)
self.assertVariantsNotMTO(black_pen)
self.assertVariantsMTO(blue_pen | green_pen | red_pen)

# Enable unrelated route does not raise warning nor reset
random_route = self.mto_route.create({"name": "loutourout de la vit"})
with self.assertLogs(onchange_logger) as log:
with self.assertLogs(onchange_logger) as log_catcher:
self.add_route(pen_template, random_route)
onchange_logger.info("No warning raised")
self.assertNotIn("WARNING", log.output[0])
_logger.info("No warning raised")
self.assertNotIn("WARNING", log_catcher.output[0])
self.assertVariantsNotMTO(black_pen)
self.assertVariantsMTO(blue_pen | green_pen | red_pen)
# Disable mto route on the template, raise warning as is_mto is reset on variants
with self.assertLogs(onchange_logger) as log:

# Disable mto route on the template,
# raise warning as is_mto is reset on variants
with self.assertLogs(onchange_logger) as log_catcher:
self.remove_route(pen_template, self.mto_route)
self.assertIn("WARNING", log.output[0])
self.assertIn("Deactivating MTO route will reset", log.output[0])
self.assertIn("WARNING", log_catcher.output[0])
self.assertIn("Deactivating MTO route will reset", log_catcher.output[0])
self.assertVariantsNotMTO(pens)

# Enable mto route for black pen
self.toggle_is_mto(black_pen)
self.assertVariantsMTO(black_pen)
self.assertVariantsNotMTO(blue_pen | green_pen | red_pen)

# Disable unrelated route does not raise warning nor reset
with self.assertLogs(onchange_logger) as log:
with self.assertLogs(onchange_logger) as log_catcher:
self.remove_route(pen_template, random_route)
onchange_logger.info("No warning raised")
self.assertNotIn("WARNING", log.output[0])
_logger.info("No warning raised")
self.assertVariantsMTO(black_pen)
self.assertVariantsNotMTO(blue_pen | green_pen | red_pen)

0 comments on commit d557475

Please sign in to comment.