-
-
Notifications
You must be signed in to change notification settings - Fork 715
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by sebastienbeau
- Loading branch information
Showing
16 changed files
with
1,194 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
=============================== | ||
Product Attribute Variant Rules | ||
=============================== | ||
|
||
.. | ||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
!! This file is generated by oca-gen-addon-readme !! | ||
!! changes will be overwritten. !! | ||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
!! source digest: sha256:3557e19d06bc602378a8840ca208031e48ff3106d82706f0ffe66904d612c18b | ||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
.. |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/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/14.0/product_attribute_variant_rules | ||
: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-14-0/product-attribute-14-0-product_attribute_variant_rules | ||
: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=14.0 | ||
:alt: Try me on Runboat | ||
|
||
|badge1| |badge2| |badge3| |badge4| |badge5| | ||
|
||
This module adds a more powerful way to describe your product attributes combinations | ||
than the default exclusions. | ||
|
||
It allows to write rules like: | ||
|
||
* All products with blue or green color in XL size will appear only with a V neck collar | ||
and a short sleeve. | ||
* All L size products will never appear with a sailor collar. | ||
|
||
The rules are split between a precondition a type and a postcondition. | ||
|
||
Different attributes are ANDed and same attributes are ORed. | ||
|
||
For instance the rule:: | ||
All products with blue or green color in XL size will appear only with a V neck collar and a short sleeve. | ||
|
||
|
||
Will be written as:: | ||
Precondition: (color: blue), (color: green), (size: XL) | ||
Type: Only With | ||
Postcondition: (collar: V neck), (sleeve: short) | ||
|
||
|
||
**Table of contents** | ||
|
||
.. contents:: | ||
:local: | ||
|
||
Usage | ||
===== | ||
|
||
After saving your product click on the Use Attribute Rules checkbox in the product | ||
variants tab. | ||
|
||
Then add your different rules, specifying optionally some rule preconditions, a type of | ||
inclusion/exclusion and the rule postconditions. | ||
|
||
When you save your product the variant will be recomputed. | ||
|
||
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_variant_rules%0Aversion:%2014.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 | ||
~~~~~~~~~~~~ | ||
|
||
* Florian Mounier <[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. | ||
|
||
This module is part of the `OCA/product-attribute <https://github.com/OCA/product-attribute/tree/14.0/product_attribute_variant_rules>`_ project on GitHub. | ||
|
||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import models |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# Copyright 2023 Akretion | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). | ||
|
||
{ | ||
"name": "Product Attribute Variant Rules", | ||
"summary": "Add better rules for product variants generation from attributes", | ||
"version": "14.0.1.0.0", | ||
"license": "AGPL-3", | ||
"author": "Akretion,Odoo Community Association (OCA)", | ||
"depends": ["product"], | ||
"data": [ | ||
"security/ir.model.access.csv", | ||
"views/product_template_views.xml", | ||
], | ||
"website": "https://github.com/OCA/product-attribute", | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from . import product_attribute_rule | ||
from . import product_template |
142 changes: 142 additions & 0 deletions
142
product_attribute_variant_rules/models/product_attribute_rule.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
# Copyright 2023 Akretion (https://www.akretion.com). | ||
# @author Florian Mounier <[email protected]> | ||
|
||
from odoo import api, fields, models | ||
from odoo.tools import groupby | ||
|
||
|
||
class ProductAttributeRule(models.Model): | ||
_name = "product.attribute.rule" | ||
_description = "Product Attribute Rule" | ||
|
||
product_tmpl_id = fields.Many2one( | ||
comodel_name="product.template", | ||
string="Product Template", | ||
required=True, | ||
ondelete="cascade", | ||
) | ||
|
||
product_attribute_value_precondition_ids = fields.Many2many( | ||
comodel_name="product.attribute.value", | ||
relation="product_attribute_rule_precondition_rel", | ||
string="Rule preconditions", | ||
help="This rule will only be applied if all the preconditions are met.\n" | ||
"The attribute values are ANDed together except if they are from the " | ||
"same attribute in which case they are ORed.\n" | ||
"If empty, the rule will always be applied.", | ||
) | ||
|
||
rule_type = fields.Selection( | ||
[ | ||
("only", "Only With"), | ||
("never", "Never With"), | ||
], | ||
string="Type", | ||
default="only", | ||
required=True, | ||
) | ||
|
||
product_attribute_value_postcondition_ids = fields.Many2many( | ||
comodel_name="product.attribute.value", | ||
relation="product_attribute_rule_postcondition_rel", | ||
string="Rule postconditions", | ||
help="The product variant will exist only if these conditions are met " | ||
"if the precondition is met.\n" | ||
"The attribute values are ANDed together except if they are from the " | ||
"same attribute in which case they are ORed.", | ||
required=True, | ||
) | ||
|
||
available_precondition_attribute_ids = fields.Many2many( | ||
comodel_name="product.attribute", | ||
compute="_compute_available_precondition_attribute_ids", | ||
string="Available preconditions", | ||
) | ||
|
||
available_postcondition_attribute_ids = fields.Many2many( | ||
comodel_name="product.attribute", | ||
compute="_compute_available_postcondition_attribute_ids", | ||
string="Available postconditions", | ||
) | ||
|
||
@api.depends( | ||
"product_tmpl_id.attribute_line_ids", | ||
"product_attribute_value_postcondition_ids", | ||
) | ||
def _compute_available_precondition_attribute_ids(self): | ||
""" | ||
Compute the available preconditions. | ||
""" | ||
for rule in self: | ||
rule.available_precondition_attribute_ids = ( | ||
rule.product_tmpl_id.attribute_line_ids.mapped("attribute_id") | ||
- rule.product_attribute_value_postcondition_ids.mapped("attribute_id") | ||
) | ||
|
||
@api.depends( | ||
"product_tmpl_id.attribute_line_ids", "product_attribute_value_precondition_ids" | ||
) | ||
def _compute_available_postcondition_attribute_ids(self): | ||
""" | ||
Compute the available postconditions. | ||
""" | ||
for rule in self: | ||
rule.available_postcondition_attribute_ids = ( | ||
rule.product_tmpl_id.attribute_line_ids.mapped("attribute_id") | ||
- rule.product_attribute_value_precondition_ids.mapped("attribute_id") | ||
) | ||
|
||
def _is_combination_possible(self, combination): | ||
""" | ||
Check if the combination is possible with the rules defined on the | ||
product template. | ||
""" | ||
# Check if the combination matches the preconditions | ||
if not self._combination_matches_conditions( | ||
combination, self.product_attribute_value_precondition_ids | ||
): | ||
# If the combination does not match the preconditions, | ||
# the rule is not applied | ||
return True | ||
|
||
# Check if the combination matches the postconditions | ||
match = self._combination_matches_conditions( | ||
combination, self.product_attribute_value_postcondition_ids | ||
) | ||
|
||
if self.rule_type == "only" and match: | ||
# Both conditions are met in only, the combination is possible | ||
return True | ||
elif self.rule_type == "never" and not match: | ||
# Precondition is met but postcondition is not met in never, | ||
# the combination is possible | ||
return True | ||
|
||
# The combination is not possible | ||
return False | ||
|
||
def _combination_matches_conditions(self, combination, conditions): | ||
""" | ||
Check if the combination matches the given conditions. | ||
""" | ||
# If there is no condition, the combination matches | ||
# (only possible for the preconditions) | ||
if not conditions: | ||
return True | ||
|
||
# Check if the combination matches the preconditions ANDed between | ||
# different attributes | ||
for attribute, attribute_values in groupby( | ||
conditions, lambda condition: condition.attribute_id | ||
): | ||
if ( | ||
combination.filtered( | ||
lambda value: value.attribute_id == attribute | ||
).product_attribute_value_id | ||
not in attribute_values # The OR between the same attribute values | ||
): | ||
# The combination does not match a precondition | ||
return False | ||
|
||
# The combination matches all preconditions | ||
return True |
72 changes: 72 additions & 0 deletions
72
product_attribute_variant_rules/models/product_template.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# Copyright 2023 Akretion (https://www.akretion.com). | ||
# @author Florian Mounier <[email protected]> | ||
|
||
from odoo import api, fields, models | ||
|
||
|
||
class ProductTemplate(models.Model): | ||
_inherit = "product.template" | ||
|
||
product_attribute_rule_ids = fields.One2many( | ||
comodel_name="product.attribute.rule", | ||
inverse_name="product_tmpl_id", | ||
string="Product Attribute Rules", | ||
) | ||
|
||
use_attribute_rules = fields.Boolean( | ||
string="Use Attribute Rules", | ||
help="If checked, the product variants will be generated based on the rules " | ||
"defined below.", | ||
) | ||
|
||
product_attribute_value_ids = fields.Many2many( | ||
string="Technical Attributes", | ||
comodel_name="product.attribute.value", | ||
compute="_compute_product_attribute_value_ids", | ||
) | ||
|
||
@api.depends("attribute_line_ids.value_ids") | ||
def _compute_product_attribute_value_ids(self): | ||
for template in self: | ||
template.product_attribute_value_ids = template.mapped( | ||
"attribute_line_ids.value_ids" | ||
) | ||
|
||
def _is_combination_possible_by_config(self, combination, ignore_no_variant=False): | ||
rv = super()._is_combination_possible_by_config( | ||
combination, ignore_no_variant=ignore_no_variant | ||
) | ||
if ( | ||
not rv # Combination is not possible | ||
or not self.use_attribute_rules # Rules are not enabled | ||
or not self.product_attribute_rule_ids # No rules defined | ||
): | ||
return rv | ||
|
||
# Check if the combination matches the rules | ||
return self._is_combination_possible_with_rules(combination) | ||
|
||
def _is_combination_possible_with_rules(self, combination): | ||
""" | ||
Check if the combination is possible with the rules defined on the product template. | ||
""" | ||
# Rules are ANDed together | ||
for rule in self.product_attribute_rule_ids: | ||
if not rule._is_combination_possible(combination): | ||
return False | ||
return True | ||
|
||
def write(self, vals): | ||
res = super().write(vals) | ||
|
||
# Recreate variants if the rules have changed | ||
empty = vals.get("active") and len(self.product_variant_ids) == 0 | ||
if "attribute_line_ids" in vals or empty: | ||
# Already done in super | ||
return res | ||
|
||
if "use_attribute_rules" in vals or "product_attribute_rule_ids" in vals: | ||
# Recreate variants | ||
self._create_variant_ids() | ||
|
||
return res |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
* Florian Mounier <[email protected]> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
This module adds a more powerful way to describe your product attributes combinations | ||
than the default exclusions. | ||
|
||
It allows to write rules like: | ||
|
||
* All products with blue or green color in XL size will appear only with a V neck collar | ||
and a short sleeve. | ||
* All L size products will never appear with a sailor collar. | ||
|
||
The rules are split between a precondition a type and a postcondition. | ||
|
||
Different attributes are ANDed and same attributes are ORed. | ||
|
||
For instance the rule:: | ||
|
||
All products with blue or green color in XL size will appear only with a V neck collar and a short sleeve. | ||
|
||
|
||
Will be written as:: | ||
|
||
Precondition: (color: blue), (color: green), (size: XL) | ||
Type: Only With | ||
Postcondition: (collar: V neck), (sleeve: short) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
After saving your product click on the Use Attribute Rules checkbox in the product | ||
variants tab. | ||
|
||
Then add your different rules, specifying optionally some rule preconditions, a type of | ||
inclusion/exclusion and the rule postconditions. | ||
|
||
When you save your product the variant will be recomputed. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink | ||
access_product_product_attribute_rule,product.template.attribute rule,model_product_attribute_rule,base.group_user,1,0,0,0 | ||
access_product_product_attribute_rule_manager,product.template.attribute rule manager,model_product_attribute_rule,base.group_system,1,1,1,1 |
Oops, something went wrong.