Skip to content
This repository has been archived by the owner on Apr 28, 2022. It is now read-only.

[ENH] Custom Float Value Enhancements #86

Open
wants to merge 3 commits into
base: 10.0
Choose a base branch
from
Open
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
38 changes: 31 additions & 7 deletions product_configurator/models/product_attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,24 @@ def onchange_custom_type(self):
'disable a attribute without deleting it'
)

min_val = fields.Integer(string="Min Value", help="Minimum value allowed")
max_val = fields.Integer(string="Max Value", help="Minimum value allowed")
min_val = fields.Integer(
string="Min Value",
help="Minimum value allowed"
)
max_val = fields.Integer(
string="Max Value",
help="Maximum value allowed, or 0 if not checked"
)
min_fval = fields.Float(
string="Min Float Value",
digits=(16, 4),
help="Minimum value allowed"
)
max_fval = fields.Float(
string="Max Float Value",
digits=(16, 4),
help="Maximum value allowed, or 0 if not checked"
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we keep one float field for min/max of int and float since there can only be one custom field type per attribute?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PCatinean True, but it needs to be a float min or max, not an integer. Our requirement was that the minimum was 0.573, and the maximum was 20.127 (a length).

I think this is a little cleaner, though, because it implies the validation steps are dependent on the type, which could be extended to other types at another time...

Also, if a float min is used, then we need to ensure the error converts the min to an integer before displaying so it does not say "Value cannot be greater than 20.0"


# TODO: Exclude self from result-set of dependency
val_custom = fields.Boolean(
Expand All @@ -59,6 +75,10 @@ def onchange_custom_type(self):
size=64,
help='The type of the custom field generated in the frontend'
)
custom_digits = fields.Integer(
string='Decimal Digits',
help='Number of digits accuracy on float entry'
)

description = fields.Text(string='Description', translate=True)

Expand Down Expand Up @@ -109,24 +129,28 @@ def validate_custom_val(self, val):
Probaly should check type, etc, but let's assume fine for the moment.
"""
self.ensure_one()
if self.custom_type in ('int', 'float'):
if self.custom_type == 'float':
minv = self.min_fval
maxv = self.max_fval
elif self.custom_type == 'int':
minv = self.min_val
maxv = self.max_val
if self.custom_type in ('int', 'float'):
val = literal_eval(val)
if minv and maxv and (val < minv or val > maxv):
raise ValidationError(
_("Selected custom value '%s' must be between %s and %s"
% (self.name, self.min_val, self.max_val))
% (self.name, minv, maxv))
)
elif minv and val < minv:
raise ValidationError(
_("Selected custom value '%s' must be at least %s" %
(self.name, self.min_val))
(self.name, minv))
)
elif maxv and val > maxv:
raise ValidationError(
_("Selected custom value '%s' must be lower than %s" %
(self.name, self.max_val + 1))
_("Selected custom value '%s' cannot be greater than %s" %
(self.name, maxv))
)


Expand Down
1 change: 1 addition & 0 deletions product_configurator/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

from . import test_create
from . import test_configuration_rules
from . import test_numeric_customvals
116 changes: 116 additions & 0 deletions product_configurator/tests/test_numeric_customvals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# -*- coding: utf-8 -*-

from odoo.tests.common import TransactionCase
from odoo.exceptions import ValidationError


class ConfigurationRules(TransactionCase):

def setUp(self):
super(ConfigurationRules, self).setUp()

self.attr_repeater = self.env['product.attribute'].create(
{'name': 'Repeater',
'value_ids': [
(0, 0, {'name': '1'}),
(0, 0, {'name': '2'}),
],
'custom_type': 'int',
'min_val': 3,
'max_val': 10,
}
)
self.attr_flux = self.env['product.attribute'].create(
{'name': 'Flux Adjustment',
'value_ids': [
(0, 0, {'name': 'Standard'}),
],
'custom_type': 'float',
'custom_digits': 3,
'min_fval': 0.750,
'max_fval': 1.823,
}
)
self.flux_capacitor = self.env['product.template'].create(
{'name': 'Flux Capacitor',
'config_ok': True,
'type': 'product',
'categ_id': self.env['ir.model.data'] .xmlid_to_res_id(
'product.product_category_5'
),
'attribute_line_ids': [
(0, 0, {'attribute_id': self.attr_repeater.id,
'value_ids': [
(6, 0, self.attr_repeater.value_ids.ids),
],
'required': True,
'custom': True,
}),
(0, 0, {'attribute_id': self.attr_flux.id,
'value_ids': [
(6, 0, self.attr_flux.value_ids.ids),
],
'required': True,
'custom': True,
})
]
}
)

def test_without_custom(self):
attr_val_ids = [self.attr_repeater.value_ids[0].id,
self.attr_flux.value_ids[0].id,
]
custom_vals = {}
validation = self.flux_capacitor.validate_configuration(
attr_val_ids, custom_vals)
self.assertTrue(
validation,
"Configuration with custom values rejected with all "
"standard selections"
)

def test_valid_custom_numerics(self):
attr_val_ids = []
custom_vals = {
self.attr_repeater.id: '5',
self.attr_flux.id: '1.234',
}
validation = self.flux_capacitor.validate_configuration(
attr_val_ids, custom_vals)
self.assertTrue(
validation,
"Configuration with valid custom numeric values rejected"
)

def test_invalid_custom_numerics(self):
attr_val_ids = []
validation_method = self.flux_capacitor.validate_configuration

custom_vals = {
self.attr_repeater.id: '2',
self.attr_flux.id: '1.234',
}
self.assertRaises(ValidationError,
validation_method, attr_val_ids, custom_vals)

custom_vals = {
self.attr_repeater.id: '11',
self.attr_flux.id: '1.234',
}
self.assertRaises(ValidationError,
validation_method, attr_val_ids, custom_vals)

custom_vals = {
self.attr_repeater.id: '5',
self.attr_flux.id: '0.749',
}
self.assertRaises(ValidationError,
validation_method, attr_val_ids, custom_vals)

custom_vals = {
self.attr_repeater.id: '5',
self.attr_flux.id: '1.824',
}
self.assertRaises(ValidationError,
validation_method, attr_val_ids, custom_vals)
7 changes: 5 additions & 2 deletions product_configurator/views/product_attribute_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,14 @@
<group name="config_custom_vals">
<group>
<field name="custom_type"/>
<field name="custom_digits" attrs="{'invisible': [('custom_type','not in',['float'])]}"/>
<field name="search_ok"/>
</group>
<group>
<field name="min_val" attrs="{'invisible': [('custom_type','not in',['int','float'])]}"/>
<field name="max_val" attrs="{'invisible': [('custom_type','not in',['int','float'])]}"/>
<field name="min_val" attrs="{'invisible': [('custom_type','not in',['int'])]}"/>
<field name="max_val" attrs="{'invisible': [('custom_type','not in',['int'])]}"/>
<field name="min_fval" attrs="{'invisible': [('custom_type','not in',['float'])]}"/>
<field name="max_fval" attrs="{'invisible': [('custom_type','not in',['float'])]}"/>
</group>
</group>
</page>
Expand Down
6 changes: 5 additions & 1 deletion product_configurator_wizard/wizard/product_configurator.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,13 +317,17 @@ def fields_get(self, allfields=None, attributes=None):
elif custom_type in [f[0] for f in field_types]:
field_type = custom_type

custom_field = self.custom_field_prefix + str(attribute.id)
# TODO: Implement custom string on custom attribute
res[self.custom_field_prefix + str(attribute.id)] = dict(
res[custom_field] = dict(
default_attrs,
string="Custom",
type=field_type,
sequence=line.sequence,
)
if field_type == 'float' and line.attribute_id.custom_digits:
res[custom_field]['digits'] = \
(16, line.attribute_id.custom_digits)

# Add the dynamic field to the resultset using the convention
# "__attribute-DBID" to later identify and extract it
Expand Down
6 changes: 5 additions & 1 deletion website_product_configurator/controllers/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,9 +371,13 @@ def config_parse(self, product_tmpl, post, config_step=None,
custom_val = post.get(custom_field_name)
if custom_val:
custom_type = line.attribute_id.custom_type
if custom_type in ['float', 'integer']:
if custom_type == 'float':
max_val = line.attribute_id.max_fval
min_val = line.attribute_id.min_fval
elif custom_type == 'integer':
max_val = line.attribute_id.max_val
min_val = line.attribute_id.min_val
if custom_type in ['float', 'integer']:
if max_val and custom_val > max_val:
continue
elif min_val and custom_val < min_val:
Expand Down
1 change: 1 addition & 0 deletions website_product_configurator/data/config_layout.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
<i class="fa fa-minus"></i>
</a>
</span>
<!-- TODO: Need to allow for min_fval and max_fval -->
<input
type="text"
t-att-id="cfg_vars['custom_attr_field_prefix'] + str(line.attribute_id.id)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ odoo.define('website_product_configurator.website_form', function (require) {
});
}

// TODO: Need to allow for min_fval and max_fval
config_form.on('click', 'a.js_add_qty', function (ev) {
ev.preventDefault();
var $link = $(ev.currentTarget);
Expand Down