diff --git a/product_configurator/demo/product_attribute.xml b/product_configurator/demo/product_attribute.xml
index 5e833da0..4b69d8da 100644
--- a/product_configurator/demo/product_attribute.xml
+++ b/product_configurator/demo/product_attribute.xml
@@ -335,7 +335,7 @@
ref('product_configurator.product_attribute_value_tow_hook'),
]
)]"/>
-
+
diff --git a/product_configurator/demo/product_config_lines.xml b/product_configurator/demo/product_config_lines.xml
index 84d54ff1..0e4f7b24 100644
--- a/product_configurator/demo/product_config_lines.xml
+++ b/product_configurator/demo/product_config_lines.xml
@@ -11,6 +11,7 @@
ref('product_attribute_value_m235i'),
ref('product_attribute_value_m235i_xdrive')])]"/>
+ Gasoline Fuel requires a Gasoline Engine to be selected
@@ -22,6 +23,7 @@
ref('product_attribute_value_220d_xdrive'),
ref('product_attribute_value_225d')])]"/>
+ Diesel Fuel requires a Diesel Engine to be selected
@@ -31,6 +33,7 @@
ref('product_attribute_value_sport_line'),
ref('product_attribute_value_luxury_line')])]"/>
+ 218i Engine has a limited selection of Lines
diff --git a/product_configurator/models/product.py b/product_configurator/models/product.py
index 9a6bfe56..56cdd063 100644
--- a/product_configurator/models/product.py
+++ b/product_configurator/models/product.py
@@ -357,7 +357,8 @@ def create_get_variant(self, value_ids, custom_values=None):
"""
if custom_values is None:
custom_values = {}
- valid = self.validate_configuration(value_ids, custom_values)
+ valid = self.validate_configuration(value_ids, custom_values,
+ do_raise=True)
if not valid:
raise ValidationError(_('Invalid Configuration'))
@@ -413,7 +414,7 @@ def validate_domains_against_sels(self, domains, sel_val_ids):
return avail
@api.multi
- def values_available(self, attr_val_ids, sel_val_ids):
+ def values_available(self, attr_val_ids, sel_val_ids, do_raise=False):
"""Determines whether the attr_values from the product_template
are available for selection given the configuration ids and the
dependencies set on the product template
@@ -421,6 +422,7 @@ def values_available(self, attr_val_ids, sel_val_ids):
:param attr_val_ids: list of attribute value ids to check for
availability
:param sel_val_ids: list of attribute value ids already selected
+ :param do_raise: boolean on whether to raise a warning or just fail
:returns: list of available attribute values
"""
@@ -436,11 +438,23 @@ def values_available(self, attr_val_ids, sel_val_ids):
avail = self.validate_domains_against_sels(domains, sel_val_ids)
if avail:
avail_val_ids.append(attr_val_id)
-
+ elif do_raise:
+ if len(config_lines) == 1 and config_lines[0].rule_description:
+ raise ValidationError(config_lines[0].rule_description)
+ else:
+ attribute_value = \
+ self.env['product.attribute.value'].browse(attr_val_id)
+ raise ValidationError(
+ _('%s for %s is not valid in this configuration') %
+ (attribute_value.name,
+ attribute_value.attribute_id.name
+ )
+ )
return avail_val_ids
@api.multi
- def validate_configuration(self, value_ids, custom_vals=None, final=True):
+ def validate_configuration(self, value_ids,
+ custom_vals=None, final=True, do_raise=False):
""" Verifies if the configuration values passed via value_ids and custom_vals
are valid
@@ -448,12 +462,12 @@ def validate_configuration(self, value_ids, custom_vals=None, final=True):
:param custom_vals: custom values dict {attr_id: custom_val}
:param final: boolean marker to check required attributes.
pass false to check non-final configurations
+ :param do_raise: boolean on whether to raise a warning or just fail
:returns: Error dict with reason of validation failure
or True
"""
- # TODO: Raise ConfigurationError with reason
- # Check if required values are missing for final configuration
+ # TODO: Check if required values are missing for final configuration
if custom_vals is None:
custom_vals = {}
@@ -466,11 +480,15 @@ def validate_configuration(self, value_ids, custom_vals=None, final=True):
common_vals = set(value_ids) & set(line.value_ids.ids)
custom_val = custom_vals.get(attr.id)
if line.required and not common_vals and not custom_val:
+ if do_raise:
+ raise ValidationError(_("No value provided for %s") %
+ line.attribute.name)
# TODO: Verify custom value type to be correct
return False
# Check if all all the values passed are not restricted
- avail_val_ids = self.values_available(value_ids, value_ids)
+ avail_val_ids = self.values_available(value_ids, value_ids,
+ do_raise=do_raise)
if set(value_ids) - set(avail_val_ids):
return False
@@ -478,7 +496,17 @@ def validate_configuration(self, value_ids, custom_vals=None, final=True):
custom_attr_ids = self.attribute_line_ids.filtered(
'custom').mapped('attribute_id').ids
- if not set(custom_vals.keys()) <= set(custom_attr_ids):
+ invalid_custom_vals = set(custom_vals.keys()) - set(custom_attr_ids)
+ if invalid_custom_vals:
+ if do_raise:
+ ProductAttribute = self.env['product.attribute']
+ attributes_names = ', '.join(attr.name
+ for attr in
+ ProductAttribute.browse(
+ list(invalid_custom_vals)))
+ raise ValidationError(
+ _('Attributes (%s) cannot hold custom values') %
+ attributes_names)
return False
# Check if there are multiple values passed for non-multi attributes
diff --git a/product_configurator/models/product_config.py b/product_configurator/models/product_config.py
index 64ae1a31..0b27f395 100644
--- a/product_configurator/models/product_config.py
+++ b/product_configurator/models/product_config.py
@@ -180,6 +180,11 @@ def onchange_attribute(self):
string='Restrictions'
)
+ rule_description = fields.Char(
+ string='Rule Description',
+ help='User Displayed error if rule is broken'
+ )
+
sequence = fields.Integer(string='Sequence', default=10)
_order = 'product_tmpl_id, sequence, id'
@@ -229,7 +234,7 @@ def _check_value_ids(self):
if not valid:
raise ValidationError(
_("Values entered for line '%s' generate "
- "a incompatible configuration" % cfg_img.name)
+ "an incompatible configuration" % cfg_img.name)
)
@@ -451,7 +456,7 @@ def write(self, vals):
for x in self.custom_value_ids
}
valid = self.product_tmpl_id.validate_configuration(
- self.value_ids.ids, custom_val_dict, final=False)
+ self.value_ids.ids, custom_val_dict, final=False, do_raise=True)
if not valid:
raise ValidationError(_('Invalid Configuration'))
return res
diff --git a/product_configurator/tests/test_configuration_rules.py b/product_configurator/tests/test_configuration_rules.py
index 984a3010..649478e6 100644
--- a/product_configurator/tests/test_configuration_rules.py
+++ b/product_configurator/tests/test_configuration_rules.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from odoo.tests.common import TransactionCase
+from odoo.exceptions import ValidationError
class ConfigurationRules(TransactionCase):
@@ -56,6 +57,10 @@ def test_invalid_configuration(self):
validation = self.cfg_tmpl.validate_configuration(attr_val_ids)
self.assertFalse(validation, "Incompatible values (Diesel Fuel -> "
"Gasoline Engine) configuration passed validation")
+ self.assertRaises(ValidationError,
+ self.cfg_tmpl.validate_configuration,
+ attr_val_ids, do_raise=True,
+ )
def test_missing_val_configuration(self):
conf = [
diff --git a/product_configurator/views/product_view.xml b/product_configurator/views/product_view.xml
index 1519dece..69db3c7a 100644
--- a/product_configurator/views/product_view.xml
+++ b/product_configurator/views/product_view.xml
@@ -63,6 +63,7 @@
options="{'no_create': True, 'no_create_edit': True}"
context="{'show_attribute': False}"/>
+