Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions product_variant_route_mto/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
=========================
Product Variant Route MTO
=========================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:19127c85672968523ba9dac247eafabed27aaff202e5e8cc20d87b1f6ee204c6
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png
:target: https://odoo-community.org/page/development-status
:alt: Alpha
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fproduct--attribute-lightgray.png?logo=github
:target: https://github.com/OCA/product-attribute/tree/18.0/product_variant_route_mto
:alt: OCA/product-attribute
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/product-attribute-18-0/product-attribute-18-0-product_variant_route_mto
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/product-attribute&target_branch=18.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module allows to define if a product variant can use the Make To
Order route without any dependency on its template routes settings.

.. IMPORTANT::
This is an alpha version, the data model and design can change at any time without warning.
Only for development or testing purpose, do not use in production.
`More details on development status <https://odoo-community.org/page/development-status>`_

**Table of contents**

.. contents::
:local:

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/product-attribute/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/product-attribute/issues/new?body=module:%20product_variant_route_mto%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* Camptocamp SA

Contributors
------------

- Matthieu Méquignon <[email protected]>
- Akim Juillerat <[email protected]>

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

.. |maintainer-mmequignon| image:: https://github.com/mmequignon.png?size=40px
:target: https://github.com/mmequignon
:alt: mmequignon

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-mmequignon|

This module is part of the `OCA/product-attribute <https://github.com/OCA/product-attribute/tree/18.0/product_variant_route_mto>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
1 change: 1 addition & 0 deletions product_variant_route_mto/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
19 changes: 19 additions & 0 deletions product_variant_route_mto/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2023 Camptocamp SA
# Copyright 2025 Jacques-Etienne Baudoux (BCIM) <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

{
"name": "Product Variant Route MTO",
"summary": "Allow to individually set variants as MTO",
"version": "18.0.1.0.0",
"development_status": "Alpha",
"category": "Inventory",
"website": "https://github.com/OCA/product-attribute",
"author": "Camptocamp SA, Odoo Community Association (OCA)",
"maintainers": ["mmequignon", "jbaudoux"],
"license": "AGPL-3",
"installable": True,
"auto_install": False,
"depends": ["product_route_mto"],
"data": ["views/product_product.xml"],
}
2 changes: 2 additions & 0 deletions product_variant_route_mto/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import product_product
from . import product_template
70 changes: 70 additions & 0 deletions product_variant_route_mto/models/product_product.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Copyright 2023 Camptocamp SA
# Copyright 2025 Jacques-Etienne Baudoux (BCIM) <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

from odoo import api, fields, models
from odoo.exceptions import ValidationError

IS_MTO_HELP = """
Check or Uncheck this field to enable the Make To Order on the variant,
independantly from its template configuration.\n
Please note that activating or deactivating Make To Order on the template,
will reset this setting on its variants.
"""


class ProductProduct(models.Model):
_inherit = "product.product"

is_mto = fields.Boolean(
string="Variant is MTO",
compute="_compute_is_mto",
store=True,
readonly=False,
help=IS_MTO_HELP,
)

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

def _compute_is_mto(self):
for product in self:
product.is_mto = product.product_tmpl_id.is_mto

@api.depends("is_mto", "product_tmpl_id.route_ids")
def _compute_route_ids(self):
mto_routes = self.env["stock.route"].search([("is_mto", "=", True)])
for product in self:
routes = product.product_tmpl_id.route_ids
if product.is_mto:
routes += mto_routes
product.route_ids = routes

def _search_route_ids(self, operator, value):
mto_routes = self.env["stock.route"].search([("is_mto", "=", True)])

Check warning on line 49 in product_variant_route_mto/models/product_product.py

View check run for this annotation

Codecov / codecov/patch

product_variant_route_mto/models/product_product.py#L49

Added line #L49 was not covered by tests
if operator in ("=", "!=") and value in mto_routes:
return [("is_mto", operator, True)]
domain = []
route_ids = value.copy()

Check warning on line 53 in product_variant_route_mto/models/product_product.py

View check run for this annotation

Codecov / codecov/patch

product_variant_route_mto/models/product_product.py#L51-L53

Added lines #L51 - L53 were not covered by tests
for idx, route_id in enumerate(route_ids):
if route_id in mto_routes.ids:
route_ids.pop(idx)
domain = [("is_mto", "=" if operator == "in" else "!=", True)]

Check warning on line 57 in product_variant_route_mto/models/product_product.py

View check run for this annotation

Codecov / codecov/patch

product_variant_route_mto/models/product_product.py#L56-L57

Added lines #L56 - L57 were not covered by tests
if route_ids:
domain += [("product_tmpl_id.route_ids", operator, route_ids)]
return domain

Check warning on line 60 in product_variant_route_mto/models/product_product.py

View check run for this annotation

Codecov / codecov/patch

product_variant_route_mto/models/product_product.py#L59-L60

Added lines #L59 - L60 were not covered by tests

@api.constrains("is_mto")
def _check_template_is_mto(self):
for product in self:
if not product.is_mto and product.product_tmpl_id.is_mto:
raise ValidationError(
self.env._(
"You cannot mark a variant as non MTO when the product is MTO"
)
)
73 changes: 73 additions & 0 deletions product_variant_route_mto/models/product_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Copyright 2023 Camptocamp SA
# Copyright 2025 Jacques-Etienne Baudoux (BCIM) <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

from odoo import api, models


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

def write(self, values):
if "route_ids" not in values:
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
templates_mto_before = self.filtered("is_mto")
templates_not_mto_before = self - templates_mto_before

res = super().write(values)

templates_mto_after = self.filtered("is_mto")
templates_not_mto_after = self - templates_mto_after

templates_mto_added = templates_not_mto_before & templates_mto_after
templates_mto_removed = templates_not_mto_after & templates_mto_before

(
templates_mto_added | templates_mto_removed
).product_variant_ids._compute_is_mto()

return res

@api.onchange("route_ids")
def onchange_route_ids(self):
mto_routes = self.env["stock.route"].search([("is_mto", "=", True)])
if not mto_routes:
return

Check warning on line 40 in product_variant_route_mto/models/product_template.py

View check run for this annotation

Codecov / codecov/patch

product_variant_route_mto/models/product_template.py#L40

Added line #L40 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"]
)

added_routes = current_routes - origin_routes
if set(mto_routes.ids) & set(added_routes.ids):
# Return warning activating MTO route
return {
"warning": {
"title": self.env._("Warning"),
"message": self.env._(
"Activating MTO route will reset `Variant is MTO` "
"setting on the variants."
),
}
}

removed_routes = origin_routes - current_routes
if set(mto_routes.ids) & set(removed_routes.ids):
# Return warning deactivating MTO route
return {
"warning": {
"title": self.env._("Warning"),
"message": self.env._(
"Deactivating MTO route will reset `Variant is MTO` "
"setting on the variants."
),
}
}
3 changes: 3 additions & 0 deletions product_variant_route_mto/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
2 changes: 2 additions & 0 deletions product_variant_route_mto/readme/CONFIGURATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
The checkbox `Variant is MTO` on the product variant allows
to force usage or non-usage of the MTO route for the variant.
4 changes: 4 additions & 0 deletions product_variant_route_mto/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- Matthieu Méquignon \<<[email protected]>\>
- Akim Juillerat \<<[email protected]>\>
- Chau Le \<<[email protected]>\>
- Jacques-Etienne Baudoux (BCIM) \<<[email protected]>\>
3 changes: 3 additions & 0 deletions product_variant_route_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
3 changes: 3 additions & 0 deletions product_variant_route_mto/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This module allows to set a product variant as MTO (enabling the Make To
Order route on that variant only) while the related product is not MTO.
However, you cannot mark a variant has non MTO when the template is MTO.
Loading