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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ addon | version | maintainers | summary
[product_attribute_archive](product_attribute_archive/) | 16.0.1.0.0 | | Add an active field on product attributes
[product_attribute_company_favorite](product_attribute_company_favorite/) | 16.0.1.0.0 | <a href='https://github.com/victor-champonnois'><img src='https://github.com/victor-champonnois.png' width='32' height='32' style='border-radius:50%;' alt='victor-champonnois'/></a> | Possibility to set favorite product attributes per company
[product_attribute_model_link](product_attribute_model_link/) | 16.0.1.0.1 | | Use any model records as product attribute values
[product_attribute_value_dependent_mixin](product_attribute_value_dependent_mixin/) | 16.0.1.2.0 | | Mixin to make product attribute values fields on models
[product_attribute_value_menu](product_attribute_value_menu/) | 16.0.1.0.1 | | Product attributes values tree and form. Import attribute values.
[product_catalog](product_catalog/) | 16.0.1.0.0 | | Backport of Odoos v17 product catalog
[product_catalog_sale](product_catalog_sale/) | 16.0.1.0.0 | | Backport of Odoos v17 product catalog for sale orders
Expand Down
192 changes: 192 additions & 0 deletions product_attribute_value_dependent_mixin/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

=======================================
Product Attribute Value Dependent Mixin
=======================================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:12dfa656bf593464de4ef8bdaf6570d35c2f32de73e6b802a7294629ca13bd4b
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/license-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/16.0/product_attribute_value_dependent_mixin
: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-16-0/product-attribute-16-0-product_attribute_value_dependent_mixin
: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=16.0
:alt: Try me on Runboat

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

This technical module introduces a reusable mixin designed to enable any model to
establish dependencies on specific product attribute values.
By inheriting from this mixin, developers can easily link business rules,
configurations, or records to precise product variants without duplicating complex
filtering logic.

**Table of contents**

.. contents::
:local:

Usage
=====

Fields
~~~~~~

The mixin exposes the following fields:

- ``product_tmpl_id``: the product template to filter on (optional).
- ``product_id``: a specific product variant (optional).
- ``attribute_value_ids``: a set of attribute values to filter on
(optional).
- ``available_product_domain``: a computed domain to restrict the
selection of ``product_id`` in views, based on ``product_tmpl_id``.
- ``available_attribute_value_domain``: a computed domain to restrict
the selection of ``attribute_value_ids`` in views, based on
``product_tmpl_id``.

Inheriting the Mixin
~~~~~~~~~~~~~~~~~~~~

To use this mixin, inherit from ``attribute.value.dependent.mixin`` in
your model alongside your base model:

.. code:: python

from odoo import fields, models


class MyModel(models.Model):
_name = "my.model"
_inherit = ["my.model", "attribute.value.dependent.mixin"]

All fields from the mixin (``product_tmpl_id``, ``product_id``,
``attribute_value_ids``, ``available_product_domain``,
``available_attribute_value_domain``) are automatically available on
your model.

Using the Domain Fields in a Form View
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The computed domain fields must be referenced in the ``domain``
attribute of the corresponding fields in the view. Odoo 18.0
automatically loads fields referenced in ``domain`` attributes, so no
additional declaration is needed.

.. code:: xml

<record id="view_my_model_form" model="ir.ui.view">
<field name="name">my.model.form</field>
<field name="model">my.model</field>
<field name="arch" type="xml">
<form>
<group>
<field name="product_tmpl_id"/>
<field name="product_id"
domain="available_product_domain"
context="{'default_product_tmpl_id': product_tmpl_id}"/>
<field name="attribute_value_ids"
domain="available_attribute_value_domain"
widget="many2many_tags"/>
</group>
</form>
</field>
</record>

Matching Logic
~~~~~~~~~~~~~~

The ``is_matching_product(product)`` method validates whether a given
product variant satisfies the configured constraints. All fields are
optional and act as independent filters combined with AND logic:

- If ``product_id`` is set, it is the most restrictive criterion: the
method returns ``True`` only if the given product is exactly that
variant, regardless of other fields.
- If ``product_tmpl_id`` is set, the given product must belong to that
template.
- If ``attribute_value_ids`` is set, the given product must match the
configured attribute values with the following logic:

- Values belonging to the **same attribute** are combined with
**OR** (e.g. size S *or* M).
- Values belonging to **different attributes** are combined with
**AND** (e.g. size S or M *and* color Red).
- Since an attribute value can exist across multiple templates,
``product_tmpl_id`` and ``attribute_value_ids`` should be used
together to avoid unintended matches across templates.

- If no field is set, the method returns ``True`` for any product (no
constraint).

Using ``is_matching_product``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The ``is_matching_product(product)`` method can be called from Python
code to check whether a given ``product.product`` record satisfies the
constraints defined on a mixin record:

.. code:: python

for rule in self.env["my.model"].search([]):
if rule.is_matching_product(product):
# apply rule

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_attribute_value_dependent_mixin%0Aversion:%2016.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
~~~~~~~

* Akretion

Contributors
~~~~~~~~~~~~

* `Akretion <https://www.akretion.com>`_

* Chafique Delli <chafique.delli@akretion.com>
* Guillaume Masson <guillaume.masson@akretion.com>

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.

This module is part of the `OCA/product-attribute <https://github.com/OCA/product-attribute/tree/16.0/product_attribute_value_dependent_mixin>`_ 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_attribute_value_dependent_mixin/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
15 changes: 15 additions & 0 deletions product_attribute_value_dependent_mixin/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright 2023 Akretion
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

{
"name": "Product Attribute Value Dependent Mixin",
"summary": "Mixin to make product attribute values fields on models",
"author": "Akretion,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/product-attribute",
"license": "AGPL-3",
"application": False,
"installable": True,
"category": "Product",
"version": "16.0.1.2.0",
"depends": ["product"],
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * product_attribute_value_dependent_mixin
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"

#. module: product_attribute_value_dependent_mixin
#: model:ir.model,name:product_attribute_value_dependent_mixin.model_attribute_value_dependent_mixin
msgid "Attribute Value Dependent Mixin"
msgstr ""

#. module: product_attribute_value_dependent_mixin
#: model:ir.model.fields,field_description:product_attribute_value_dependent_mixin.field_attribute_value_dependent_mixin__attribute_value_ids
msgid "Attribute Values"
msgstr ""

#. module: product_attribute_value_dependent_mixin
#: model:ir.model.fields,field_description:product_attribute_value_dependent_mixin.field_attribute_value_dependent_mixin__available_attribute_value_domain
msgid "Available Attribute Value Domain"
msgstr ""

#. module: product_attribute_value_dependent_mixin
#: model:ir.model.fields,field_description:product_attribute_value_dependent_mixin.field_attribute_value_dependent_mixin__available_product_domain
msgid "Available Product Domain"
msgstr ""

#. module: product_attribute_value_dependent_mixin
#: model:ir.model.fields,field_description:product_attribute_value_dependent_mixin.field_attribute_value_dependent_mixin__product_id
msgid "Product"
msgstr ""

#. module: product_attribute_value_dependent_mixin
#: model:ir.model.fields,field_description:product_attribute_value_dependent_mixin.field_attribute_value_dependent_mixin__product_tmpl_id
msgid "Product Template"
msgstr ""
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2026 Akretion
# @author Guillaume MASSON <guillaume.masson@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from openupgradelib import openupgrade

_model_renames = [
("attribute.value.dependant.mixin", "attribute.value.dependent.mixin"),
]


@openupgrade.migrate()
def migrate(env, version):
openupgrade.rename_models(env.cr, _model_renames)
1 change: 1 addition & 0 deletions product_attribute_value_dependent_mixin/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import product_attribute_value_dependent_mixin
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Copyright 2023 Akretion
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

import itertools

from odoo import api, fields, models


class AttributeValueDependentMixin(models.AbstractModel):
_name = "attribute.value.dependent.mixin"
_description = "Attribute Value Dependent Mixin"

product_tmpl_id = fields.Many2one(
comodel_name="product.template",
string="Product Template",
)
product_id = fields.Many2one(
comodel_name="product.product",
)
available_product_domain = fields.Binary(
compute="_compute_available_product_domain",
)
attribute_value_ids = fields.Many2many(
comodel_name="product.attribute.value",
string="Attribute Values",
)
available_attribute_value_domain = fields.Binary(
compute="_compute_available_attribute_value_domain",
)

@api.depends("product_tmpl_id.product_variant_ids")
def _compute_available_product_domain(self):
for rec in self:
if rec.product_tmpl_id:
rec.available_product_domain = [
("id", "in", rec.product_tmpl_id.product_variant_ids.ids)
]
else:
rec.available_product_domain = []

@api.depends("product_tmpl_id.attribute_line_ids.value_ids")
def _compute_available_attribute_value_domain(self):
for rec in self:
if rec.product_tmpl_id:
rec.available_attribute_value_domain = [
("id", "in", rec.product_tmpl_id.attribute_line_ids.value_ids.ids)
]
else:
rec.available_attribute_value_domain = []

def is_matching_product(self, product):
self.ensure_one()
if self.product_id:
return self.product_id == product
if self.product_tmpl_id and self.product_tmpl_id != product.product_tmpl_id:
return False
if self.attribute_value_ids:
ptav = product.product_template_attribute_value_ids
attr2vals = {
attribute: set(values)
for attribute, values in itertools.groupby(
self.attribute_value_ids, lambda pav: pav.attribute_id
)
}
for attribute in attr2vals:
if attribute not in ptav.attribute_id:
return False
if not attr2vals[attribute] & set(ptav.product_attribute_value_id):
return False
return True
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
* `Akretion <https://www.akretion.com>`_

* Chafique Delli <chafique.delli@akretion.com>
* Guillaume Masson <guillaume.masson@akretion.com>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
This technical module introduces a reusable mixin designed to enable any model to
establish dependencies on specific product attribute values.
By inheriting from this mixin, developers can easily link business rules,
configurations, or records to precise product variants without duplicating complex
filtering logic.
Loading