summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorAurélien Warnon <awa@odoo.com>2018-09-26 11:45:00 +0200
committerAurélien Warnon <awa@odoo.com>2018-09-26 15:08:18 +0200
commit326dabce09d634e029e68281694ec695fa665666 (patch)
treef7e1e482c7a6ae31adfe9f00527a12079b68eb16 /addons
parentee766ab93343f7920558be3e99cdd693b54d07ee (diff)
[IMP] product,(website_)sale: Product configurator naming/cleanup
Targets commit d3530eb07e24278117ab396ecb86a59deecc312d Purpose ======= [Follow up AL review] Code cleanup and some improved models naming for the product configurator feature (see targeted commit). Mostly changes the "product.attribute" related models. Updated models: - "product.product.attribute.value" to "product.template.attribute.value" - "product.attribute.filter.line" to "product.template.attribute.exclusion" - "product.attribute.line" to "product.template.attribute.line" Updated fields: - [sale_order_line in sale/sale.py] "product_custom_variant_values" to "product_custom_attribute_value_ids" - [product_template_attribute_value in product/product_attribute.py] excluded_for is now a o2m instead of a m2m - [product.attribute in sale/product_product.py] "create_variant" field is now a Selection with "no_variant" (old false), "always" (old true), "dynamic" (new value) - [product in product/product.py] "product_attribute_value_ids" to "product_template_attribute_value_ids"
Diffstat (limited to 'addons')
-rw-r--r--addons/mrp/models/mrp_bom.py2
-rw-r--r--addons/point_of_sale/tests/test_frontend.py2
-rw-r--r--addons/product/data/product_demo.xml30
-rw-r--r--addons/product/models/product.py14
-rw-r--r--addons/product/models/product_attribute.py159
-rw-r--r--addons/product/models/product_template.py30
-rw-r--r--addons/product/security/ir.model.access.csv12
-rw-r--r--addons/product/tests/test_product_attribute_value_config.py32
-rw-r--r--addons/product/tests/test_variants.py2
-rw-r--r--addons/product/views/product_attribute_views.xml113
-rw-r--r--addons/sale/controllers/product_configurator.py24
-rw-r--r--addons/sale/data/sale_demo.xml38
-rw-r--r--addons/sale/models/product_product.py4
-rw-r--r--addons/sale/models/product_template.py8
-rw-r--r--addons/sale/models/sale.py22
-rw-r--r--addons/sale/security/ir.model.access.csv6
-rw-r--r--addons/sale/static/src/js/product_configurator_controller.js4
-rw-r--r--addons/sale/static/src/js/product_configurator_mixin.js6
-rw-r--r--addons/sale/static/src/js/product_configurator_modal.js8
-rw-r--r--addons/sale/views/product_attribute_views.xml31
-rw-r--r--addons/sale/views/sale_product_configurator_templates.xml6
-rw-r--r--addons/sale/views/sale_views.xml2
-rw-r--r--addons/sale_management/tests/test_sale_ui.py12
-rw-r--r--addons/stock/security/ir.model.access.csv6
-rw-r--r--addons/website_sale/controllers/main.py10
-rw-r--r--addons/website_sale/data/demo.xml12
-rw-r--r--addons/website_sale/models/sale_order.py6
-rw-r--r--addons/website_sale/security/ir.model.access.csv6
-rw-r--r--addons/website_sale/static/src/js/website_sale_options.js8
-rw-r--r--addons/website_sale_comparison/data/website_sale_comparison_demo.xml6
-rw-r--r--addons/website_sale_options/i18n/it.po169
-rw-r--r--addons/website_sale_options/static/src/js/website_sale.js141
32 files changed, 321 insertions, 610 deletions
diff --git a/addons/mrp/models/mrp_bom.py b/addons/mrp/models/mrp_bom.py
index bcfcef5ab4f..a3cb538229d 100644
--- a/addons/mrp/models/mrp_bom.py
+++ b/addons/mrp/models/mrp_bom.py
@@ -299,7 +299,7 @@ class MrpBomLine(models.Model):
def onchange_parent_product(self):
return {'domain': {'attribute_value_ids': [
('id', 'in', self.parent_product_tmpl_id.mapped('attribute_line_ids.value_ids.id')),
- ('attribute_id.create_variant', '!=', 'never')
+ ('attribute_id.create_variant', '!=', 'no_variant')
]}}
@api.model_create_multi
diff --git a/addons/point_of_sale/tests/test_frontend.py b/addons/point_of_sale/tests/test_frontend.py
index 64dba64d84b..cea15d9e5a6 100644
--- a/addons/point_of_sale/tests/test_frontend.py
+++ b/addons/point_of_sale/tests/test_frontend.py
@@ -35,7 +35,7 @@ class TestUi(odoo.tests.HttpCase):
'name': 'add 2',
}).id,
})
- env['product.product.attribute.value'].create({
+ env['product.template.attribute.value'].create({
'product_tmpl_id': pear.product_tmpl_id.id,
'price_extra': 2,
'product_attribute_value_id': attribute_value.id,
diff --git a/addons/product/data/product_demo.xml b/addons/product/data/product_demo.xml
index 0a2ac250e55..1572481e9ef 100644
--- a/addons/product/data/product_demo.xml
+++ b/addons/product/data/product_demo.xml
@@ -197,19 +197,19 @@
<field name="image" type="base64" file="product/static/img/table01.png"/>
</record>
- <record id="product_attribute_line_1" model="product.attribute.line">
+ <record id="product_template_attribute_line_1" model="product.template.attribute.line">
<field name="product_tmpl_id" ref="product_product_4_product_template"/>
<field name="attribute_id" ref="product_attribute_1"/>
<field name="value_ids" eval="[(6,0,[ref('product.product_attribute_value_1'), ref('product.product_attribute_value_2')])]"/>
</record>
- <record id="product_attribute_line_2" model="product.attribute.line">
+ <record id="product_template_attribute_line_2" model="product.template.attribute.line">
<field name="product_tmpl_id" ref="product_product_4_product_template"/>
<field name="attribute_id" ref="product_attribute_2"/>
<field name="value_ids" eval="[(6,0,[ref('product.product_attribute_value_3'), ref('product.product_attribute_value_4')])]"/>
</record>
<record id="product_product_4_product_template" model="product.template">
- <field name="attribute_line_ids" eval="[(6,0,[ref('product.product_attribute_line_1'), ref('product.product_attribute_line_2')])]"/>
+ <field name="attribute_line_ids" eval="[(6,0,[ref('product.product_template_attribute_line_1'), ref('product.product_template_attribute_line_2')])]"/>
</record>
<record id="product_product_5" model="product.product">
@@ -305,39 +305,39 @@
<field name="attribute_value_ids" eval="[(6,0,[ref('product.product_attribute_value_2')])]"/>
</record>
- <record id="product_attribute_line_4" model="product.attribute.line">
+ <record id="product_template_attribute_line_4" model="product.template.attribute.line">
<field name="product_tmpl_id" ref="product_product_11_product_template"/>
<field name="attribute_id" ref="product_attribute_1"/>
<field name="value_ids" eval="[(6,0,[ref('product.product_attribute_value_1'), ref('product.product_attribute_value_2')])]"/>
</record>
-
+
<record id="product_product_11_product_template" model="product.template">
- <field name="attribute_line_ids" eval="[(6,0,[ref('product.product_attribute_line_4')])]"/>
+ <field name="attribute_line_ids" eval="[(6,0,[ref('product.product_template_attribute_line_4')])]"/>
</record>
- <!--
- Handle automatically created product.product.attribute.value.
+ <!--
+ Handle automatically created product.template.attribute.value.
Meaning that the combination between the "customizable desk" and the attribute value "black" will be materialized
- into a "product.product.attribute.value" with the ref "product.product_product_attribute_value_1".
+ into a "product.template.attribute.value" with the ref "product.product_template_attribute_value_1".
This will allow setting fields like "price_extra" and "exclude_for"
-->
<function model="ir.model.data" name="_update_xmlids">
<value model="base" eval="[{
- 'xml_id': 'product.product_product_attribute_value_1',
- 'record': obj().env.ref('product.product_attribute_line_1').product_value_ids[1],
+ 'xml_id': 'product.product_template_attribute_value_1',
+ 'record': obj().env.ref('product.product_template_attribute_line_1').product_template_value_ids[1],
'noupdate': True,
}, {
- 'xml_id': 'product.product_product_attribute_value_2',
- 'record': obj().env.ref('product.product_attribute_line_4').product_value_ids[1],
+ 'xml_id': 'product.product_template_attribute_value_2',
+ 'record': obj().env.ref('product.product_template_attribute_line_4').product_template_value_ids[1],
'noupdate': True,
}]"/>
</function>
- <record id="product_product_attribute_value_1" model="product.product.attribute.value">
+ <record id="product_template_attribute_value_1" model="product.template.attribute.value">
<field name="price_extra">50.40</field>
</record>
- <record id="product_product_attribute_value_2" model="product.product.attribute.value">
+ <record id="product_template_attribute_value_2" model="product.template.attribute.value">
<field name="price_extra">6.40</field>
</record>
diff --git a/addons/product/models/product.py b/addons/product/models/product.py
index b8338c5e0e1..0bad75eec37 100644
--- a/addons/product/models/product.py
+++ b/addons/product/models/product.py
@@ -109,9 +109,9 @@ class ProductProduct(models.Model):
'Barcode', copy=False, oldname='ean13',
help="International Article Number used for product identification.")
attribute_value_ids = fields.Many2many(
- 'product.attribute.value', string='Attributes', ondelete='restrict')
- product_attribute_value_ids = fields.Many2many(
- 'product.product.attribute.value', string='Attribute Values', compute="_compute_product_attribute_value_ids")
+ 'product.attribute.value', string='Attribute Values', ondelete='restrict')
+ product_template_attribute_value_ids = fields.Many2many(
+ 'product.template.attribute.value', string='Template Attribute Values', compute="_compute_product_template_attribute_value_ids")
# image: all image fields are base64 encoded and PIL-supported
image_variant = fields.Binary(
"Variant Image", attachment=True,
@@ -195,10 +195,10 @@ class ProductProduct(models.Model):
value -= product.price_extra
product.write({'list_price': value})
- @api.depends('product_attribute_value_ids.price_extra')
+ @api.depends('product_template_attribute_value_ids.price_extra')
def _compute_product_price_extra(self):
for product in self:
- product.price_extra = sum(product.mapped('product_attribute_value_ids.price_extra'))
+ product.price_extra = sum(product.mapped('product_template_attribute_value_ids.price_extra'))
@api.depends('list_price', 'price_extra')
def _compute_product_lst_price(self):
@@ -273,9 +273,9 @@ class ProductProduct(models.Model):
else:
self.product_tmpl_id.image = image
- def _compute_product_attribute_value_ids(self):
+ def _compute_product_template_attribute_value_ids(self):
for product in self:
- product.product_attribute_value_ids = self.env['product.product.attribute.value']._search([
+ product.product_template_attribute_value_ids = self.env['product.template.attribute.value']._search([
('product_tmpl_id', '=', product.product_tmpl_id.id),
('product_attribute_value_id', 'in', product.attribute_value_ids.ids)])
diff --git a/addons/product/models/product_attribute.py b/addons/product/models/product_attribute.py
index dc7f37d7fd8..fb65cb09e64 100644
--- a/addons/product/models/product_attribute.py
+++ b/addons/product/models/product_attribute.py
@@ -15,9 +15,9 @@ class ProductAttribute(models.Model):
name = fields.Char('Attribute', required=True, translate=True)
value_ids = fields.One2many('product.attribute.value', 'attribute_id', 'Values', copy=True)
sequence = fields.Integer('Sequence', help="Determine the display order")
- attribute_line_ids = fields.One2many('product.attribute.line', 'attribute_id', 'Lines')
+ attribute_line_ids = fields.One2many('product.template.attribute.line', 'attribute_id', 'Lines')
create_variant = fields.Selection([
- ('never', 'Never'),
+ ('no_variant', 'Never'),
('always', 'Always'),
('dynamic', 'Only when the product is added to a sales order')],
default='always',
@@ -56,54 +56,22 @@ class ProductAttributeValue(models.Model):
return super(ProductAttributeValue, self).unlink()
-class ProductProductAttributeValue(models.Model):
- _name = "product.product.attribute.value"
- _order = 'sequence, attribute_id, id'
- _description = 'Product Attribute Value'
-
- name = fields.Char('Value', related="product_attribute_value_id.name")
- product_attribute_value_id = fields.Many2one('product.attribute.value', string='Attribute Value', required=True, ondelete='cascade', index=True)
- product_tmpl_id = fields.Many2one('product.template', string='Product Template', required=True, ondelete='cascade', index=True)
- attribute_id = fields.Many2one('product.attribute', string='Attribute', related="product_attribute_value_id.attribute_id")
- sequence = fields.Integer('Sequence', related="product_attribute_value_id.sequence")
- price_extra = fields.Float(
- string='Attribute Price Extra', default=0.0, digits=dp.get_precision('Product Price'),
- help="Price Extra: Extra price for the variant with this attribute value on sale price. eg. 200 price extra, 1000 + 200 = 1200.")
- exclude_for = fields.Many2many(
- 'product.attribute.filter.line', string="Exclude for", relation="product_attribute_value_exclusion",
- help="Make this attribute value not compatible with other values of the product or some attribute values of optional and accessory products.")
-
- @api.multi
- def name_get(self):
- if not self._context.get('show_attribute', True): # TDE FIXME: not used
- return super(ProductAttributeValue, self).name_get()
- return [(value.id, "%s: %s" % (value.attribute_id.name, value.name)) for value in self]
-
-
-class ProductAttributeFilterLine(models.Model):
- _name = "product.attribute.filter.line"
- _description = 'Product Attribute Filter Line'
-
- product_tmpl_id = fields.Many2one('product.template', string='Product Template', ondelete='cascade', required=True)
- value_ids = fields.Many2many(
- 'product.product.attribute.value', relation="product_attr_filter_line_value_ids_rel",
- string='Attribute Values', domain="[('product_tmpl_id', '=', product_tmpl_id)]")
+class ProductTemplateAttributeLine(models.Model):
+ """Attributes available on product.template with their selected values in a m2m.
+ Used as a configuration model to generate the appropriate product.template.attribute.value"""
-
-class ProductAttributeLine(models.Model):
- _name = "product.attribute.line"
+ _name = "product.template.attribute.line"
_rec_name = 'attribute_id'
- _description = 'Product Attribute Line'
+ _description = 'Product Template Attribute Line'
product_tmpl_id = fields.Many2one('product.template', string='Product Template', ondelete='cascade', required=True)
attribute_id = fields.Many2one('product.attribute', string='Attribute', ondelete='restrict', required=True)
value_ids = fields.Many2many('product.attribute.value', string='Attribute Values')
- product_value_ids = fields.Many2many(
- 'product.product.attribute.value',
+ product_template_value_ids = fields.Many2many(
+ 'product.template.attribute.value',
string='Product Attribute Values',
- compute="_set_product_value_ids",
- store=False
- )
+ compute="_set_product_template_value_ids",
+ store=False)
@api.constrains('value_ids', 'attribute_id')
def _check_valid_attribute(self):
@@ -113,63 +81,63 @@ class ProductAttributeLine(models.Model):
@api.model
def create(self, values):
- res = super(ProductAttributeLine, self).create(values)
- res._update_product_product_attribute_values()
+ res = super(ProductTemplateAttributeLine, self).create(values)
+ res._update_product_template_attribute_values()
return res
def write(self, values):
- res = super(ProductAttributeLine, self).write(values)
- self._update_product_product_attribute_values()
+ res = super(ProductTemplateAttributeLine, self).write(values)
+ self._update_product_template_attribute_values()
return res
@api.depends('value_ids')
- def _set_product_value_ids(self):
- for product_attribute_line in self:
- product_attribute_line.product_value_ids = self.env['product.product.attribute.value'].search([
- ('product_tmpl_id', 'in', product_attribute_line.product_tmpl_id.ids),
- ('product_attribute_value_id.attribute_id', 'in', product_attribute_line.value_ids.mapped('attribute_id').ids)]
+ def _set_product_template_value_ids(self):
+ for product_template_attribute_line in self:
+ product_template_attribute_line.product_template_value_ids = self.env['product.template.attribute.value'].search([
+ ('product_tmpl_id', 'in', product_template_attribute_line.product_tmpl_id.ids),
+ ('product_attribute_value_id.attribute_id', 'in', product_template_attribute_line.value_ids.mapped('attribute_id').ids)]
).sorted(lambda product_product_attribute: product_product_attribute.sequence)
@api.multi
def unlink(self):
- for product_attribute_line in self:
- self.env['product.product.attribute.value'].search([
- ('product_tmpl_id', 'in', product_attribute_line.product_tmpl_id.ids),
- ('product_attribute_value_id.attribute_id', 'in', product_attribute_line.value_ids.mapped('attribute_id').ids)]).unlink()
+ for product_template_attribute_line in self:
+ self.env['product.template.attribute.value'].search([
+ ('product_tmpl_id', 'in', product_template_attribute_line.product_tmpl_id.ids),
+ ('product_attribute_value_id.attribute_id', 'in', product_template_attribute_line.value_ids.mapped('attribute_id').ids)]).unlink()
- return super(ProductAttributeLine, self).unlink()
+ return super(ProductTemplateAttributeLine, self).unlink()
- def _update_product_product_attribute_values(self):
+ def _update_product_template_attribute_values(self):
"""
- Create or unlink product.product.attribute.value based on the attribute lines.
- If the product.attribute.value is removed, remove the corresponding product.product.attribute.value
- If no product.product.attribute.value exists for the newly added product.attribute.value, create it.
+ Create or unlink product.template.attribute.value based on the attribute lines.
+ If the product.attribute.value is removed, remove the corresponding product.template.attribute.value
+ If no product.template.attribute.value exists for the newly added product.attribute.value, create it.
"""
for attribute_line in self:
- # All existing product.product.attribute.value for this template
- product_product_attribute_values_to_remove = self.env['product.product.attribute.value'].search([
+ # All existing product.template.attribute.value for this template
+ product_template_attribute_values_to_remove = self.env['product.template.attribute.value'].search([
('product_tmpl_id', '=', attribute_line.product_tmpl_id.id),
('product_attribute_value_id.attribute_id', 'in', attribute_line.value_ids.mapped('attribute_id').ids)])
# All existing product.attribute.value shared by all products
# eg (Yellow, Red, Blue, Small, Large)
- existing_product_attribute_values = product_product_attribute_values_to_remove.mapped('product_attribute_value_id')
+ existing_product_attribute_values = product_template_attribute_values_to_remove.mapped('product_attribute_value_id')
# Loop on product.attribute.values for the line (eg: Yellow, Red, Blue)
for product_attribute_value in attribute_line.value_ids:
if product_attribute_value in existing_product_attribute_values:
# property is already existing: don't touch, remove it from list to avoid unlinking it
- product_product_attribute_values_to_remove = product_product_attribute_values_to_remove.filtered(
+ product_template_attribute_values_to_remove = product_template_attribute_values_to_remove.filtered(
lambda value: product_attribute_value not in value.mapped('product_attribute_value_id')
)
else:
# property does not exist: create it
- self.env['product.product.attribute.value'].create({
+ self.env['product.template.attribute.value'].create({
'product_attribute_value_id': product_attribute_value.id,
'product_tmpl_id': attribute_line.product_tmpl_id.id})
# at this point, existing properties can be removed to reflect the modifications on value_ids
- if product_product_attribute_values_to_remove:
- product_product_attribute_values_to_remove.unlink()
+ if product_template_attribute_values_to_remove:
+ product_template_attribute_values_to_remove.unlink()
@api.model
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
@@ -180,4 +148,57 @@ class ProductAttributeLine(models.Model):
args = expression.AND([['|', ('attribute_id', operator, name), ('value_ids', operator, name)], args])
attribute_ids = self._search(args, limit=limit, access_rights_uid=name_get_uid)
return self.browse(attribute_ids).name_get()
- return super(ProductAttributeLine, self)._name_search(name=name, args=args, operator=operator, limit=limit, name_get_uid=name_get_uid)
+ return super(ProductTemplateAttributeLine, self)._name_search(name=name, args=args, operator=operator, limit=limit, name_get_uid=name_get_uid)
+
+
+class ProductTemplateAttributeValue(models.Model):
+ """Materialized relationship between attribute values
+ and product template generated by the product.template.attribute.line"""
+
+ _name = "product.template.attribute.value"
+ _order = 'sequence, attribute_id, id'
+ _description = 'Product Attribute Value'
+
+ name = fields.Char('Value', related="product_attribute_value_id.name")
+ product_attribute_value_id = fields.Many2one(
+ 'product.attribute.value', string='Attribute Value',
+ required=True, ondelete='cascade', index=True)
+ product_tmpl_id = fields.Many2one(
+ 'product.template', string='Product Template',
+ required=True, ondelete='cascade', index=True)
+ attribute_id = fields.Many2one(
+ 'product.attribute', string='Attribute',
+ related="product_attribute_value_id.attribute_id")
+ sequence = fields.Integer('Sequence', related="product_attribute_value_id.sequence")
+ price_extra = fields.Float(
+ string='Attribute Price Extra',
+ default=0.0,
+ digits=dp.get_precision('Product Price'),
+ help="""Price Extra: Extra price for the variant with
+ this attribute value on sale price. eg. 200 price extra, 1000 + 200 = 1200.""")
+ exclude_for = fields.One2many(
+ 'product.template.attribute.exclusion',
+ 'product_template_attribute_value_id',
+ string="Exclude for",
+ relation="product_template_attribute_exclusion",
+ help="""Make this attribute value not compatible with
+ other values of the product or some attribute values of optional and accessory products.""")
+
+ @api.multi
+ def name_get(self):
+ if not self._context.get('show_attribute', True): # TDE FIXME: not used
+ return super(ProductAttributeValue, self).name_get()
+ return [(value.id, "%s: %s" % (value.attribute_id.name, value.name)) for value in self]
+
+
+class ProductTemplateAttributeExclusion(models.Model):
+ _name = "product.template.attribute.exclusion"
+ _description = 'Product Template Attribute Exclusion'
+
+ product_template_attribute_value_id = fields.Many2one(
+ 'product.template.attribute.value', string="Attribute Value", ondelete='cascade')
+ product_tmpl_id = fields.Many2one(
+ 'product.template', string='Product Template', ondelete='cascade', required=True)
+ value_ids = fields.Many2many(
+ 'product.template.attribute.value', relation="product_attr_exclusion_value_ids_rel",
+ string='Attribute Values', domain="[('product_tmpl_id', '=', product_tmpl_id)]")
diff --git a/addons/product/models/product_template.py b/addons/product/models/product_template.py
index a8e0191afdc..636331219a6 100644
--- a/addons/product/models/product_template.py
+++ b/addons/product/models/product_template.py
@@ -114,7 +114,7 @@ class ProductTemplate(models.Model):
color = fields.Integer('Color Index')
is_product_variant = fields.Boolean(string='Is a product variant', compute='_compute_is_product_variant')
- attribute_line_ids = fields.One2many('product.attribute.line', 'product_tmpl_id', 'Product Attributes')
+ attribute_line_ids = fields.One2many('product.template.attribute.line', 'product_tmpl_id', 'Product Attributes')
product_variant_ids = fields.One2many('product.product', 'product_tmpl_id', 'Products', required=True)
# performance: product_variant_id provides prefetching on the first product variant only
product_variant_id = fields.Many2one('product.product', 'Product', compute='_compute_product_variant_id')
@@ -454,11 +454,11 @@ class ProductTemplate(models.Model):
# iterator of n-uple of product.attribute.value *ids*
variant_matrix = [
AttributeValues.browse(value_ids)
- for value_ids in itertools.product(*(line.value_ids.ids for line in tmpl_id.attribute_line_ids if line.value_ids[:1].attribute_id.create_variant != 'never'))
+ for value_ids in itertools.product(*(line.value_ids.ids for line in tmpl_id.attribute_line_ids if line.value_ids[:1].attribute_id.create_variant != 'no_variant'))
]
# get the value (id) sets of existing variants
- existing_variants = {frozenset(variant.attribute_value_ids.filtered(lambda r: r.attribute_id.create_variant != 'never').ids) for variant in tmpl_id.product_variant_ids}
+ existing_variants = {frozenset(variant.attribute_value_ids.filtered(lambda r: r.attribute_id.create_variant != 'no_variant').ids) for variant in tmpl_id.product_variant_ids}
# -> for each value set, create a recordset of values to create a
# variant for if the value set isn't already a variant
for value_ids in variant_matrix:
@@ -476,9 +476,9 @@ class ProductTemplate(models.Model):
# check product
for product_id in tmpl_id.product_variant_ids:
- if not product_id.active and product_id.attribute_value_ids.filtered(lambda r: r.attribute_id.create_variant != 'never') in variant_matrix:
+ if not product_id.active and product_id.attribute_value_ids.filtered(lambda r: r.attribute_id.create_variant != 'no_variant') in variant_matrix:
variants_to_activate.append(product_id)
- elif product_id.attribute_value_ids.filtered(lambda r: r.attribute_id.create_variant != 'never') not in variant_matrix:
+ elif product_id.attribute_value_ids.filtered(lambda r: r.attribute_id.create_variant != 'no_variant') not in variant_matrix:
variants_to_unlink.append(product_id)
if variants_to_activate:
@@ -521,30 +521,30 @@ class ProductTemplate(models.Model):
The filtered list of product variants
"""
self.ensure_one()
- product_product_attribute_values = self.env['product.product.attribute.value'].search([('product_tmpl_id', '=', self.id)])
+ product_template_attribute_values = self.env['product.template.attribute.value'].search([('product_tmpl_id', '=', self.id)])
if reference_product:
# append the reference_product if provided
- product_product_attribute_values |= reference_product.product_attribute_value_ids
+ product_template_attribute_values |= reference_product.product_template_attribute_value_ids
product_variants = self.product_variant_ids
- for product_product_attribute_value in product_product_attribute_values:
+ for product_template_attribute_value in product_template_attribute_values:
# CASE 1: The whole product is excluded when no attribute values are selected in the parent product
# returns empty recordset of product.product if so. What is checked is:
# If the product_attribute value doesn't belong to self (i.e. belongs to the reference product)
# and self is the excluded product template on the exclusion lines
# and the exclusions is on the product without specified product attribute values (i.e. the whole product is excluded)
- if product_product_attribute_value.product_tmpl_id != self \
- and self in product_product_attribute_value.exclude_for.mapped('product_tmpl_id') \
+ if product_template_attribute_value.product_tmpl_id != self \
+ and self in product_template_attribute_value.exclude_for.mapped('product_tmpl_id') \
and any(not exclude_for.value_ids
- for exclude_for in product_product_attribute_value.exclude_for.filtered(
+ for exclude_for in product_template_attribute_value.exclude_for.filtered(
lambda excluded_product_attribute_value: excluded_product_attribute_value.product_tmpl_id == self)):
return self.env['product.product']
- # CASE 2: Check if some of the product.product.attribute.value of the product are excluded
+ # CASE 2: Check if some of the product.template.attribute.value of the product are excluded
# for this prodcut. A variant could be excluded:
# - Either by itself (eg: The office chair with iron legs excludes the color white)
# - Or by the reference product (eg: The customizable desk with iron legs excludes the office chair with aluminium legs)
- for excluded in product_product_attribute_value.exclude_for.filtered(
+ for excluded in product_template_attribute_value.exclude_for.filtered(
lambda excluded_product_attribute_value: excluded_product_attribute_value.product_tmpl_id == self):
product_variants -= product_variants.filtered(
lambda variant:
@@ -553,9 +553,9 @@ class ProductTemplate(models.Model):
# OR the restriction is on a product_attribute_value that this variant has, eg:
# if the office chair with iron legs excludes the color white, we must check
# that this variant has iron legs to check the exclusion
- (product_product_attribute_value.product_tmpl_id != self or product_product_attribute_value in variant.product_attribute_value_ids) and
+ (product_template_attribute_value.product_tmpl_id != self or product_template_attribute_value in variant.product_template_attribute_value_ids) and
# 2/ Check the variant has one of the excluded attribute values
- any(attribute_value in excluded.value_ids for attribute_value in variant.product_attribute_value_ids))
+ any(attribute_value in excluded.value_ids for attribute_value in variant.product_template_attribute_value_ids))
return product_variants
diff --git a/addons/product/security/ir.model.access.csv b/addons/product/security/ir.model.access.csv
index 4c76d40b2f6..5af96e9dfa9 100644
--- a/addons/product/security/ir.model.access.csv
+++ b/addons/product/security/ir.model.access.csv
@@ -10,9 +10,9 @@ access_product_product_employee,product.product employee,model_product_product,b
access_product_price_history_employee,prices.history employee,model_product_price_history,base.group_user,1,0,0,0
access_product_attribute,product.attribute,model_product_attribute,base.group_user,1,0,0,0
access_product_attribute_value,product.attribute value,model_product_attribute_value,base.group_user,1,0,0,0
-access_product_product_attribute,product.product.attribute value,model_product_product_attribute_value,base.group_user,1,0,0,0
-access_product_attribute_filter_line,product.attribute filter line,model_product_attribute_filter_line,base.group_user,1,0,0,0
-access_product_attribute_line,product.attribute line,model_product_attribute_line,base.group_user,1,0,0,0
+access_product_product_attribute,product.template.attribute value,model_product_template_attribute_value,base.group_user,1,0,0,0
+access_product_template_attribute_exclusion,product..template.attribute exclusion,model_product_template_attribute_exclusion,base.group_user,1,0,0,0
+access_product_template_attribute_line,product.template.attribute line,model_product_template_attribute_line,base.group_user,1,0,0,0
access_product_category_manager,product.category.manager,model_product_category,base.group_system,1,1,1,1
access_product_template_manager,product.template.manager,model_product_template,base.group_system,1,1,1,1
access_product_packaging_manager,product.packaging.manager,model_product_packaging,base.group_system,1,1,1,1
@@ -23,6 +23,6 @@ access_product_product_manager,product.product manager,model_product_product,bas
access_product_price_history_manager,prices.history manager,model_product_price_history,base.group_system,1,1,1,1
access_product_attribute_manager,product.attribute manager,model_product_attribute,base.group_system,1,1,1,1
access_product_attribute_value_manager,product.attribute value manager,model_product_attribute_value,base.group_system,1,1,1,1
-access_product_product_attribute_manager,product.product.attribute value manager,model_product_product_attribute_value,base.group_system,1,1,1,1
-access_product_attribute_filter_line_manager,product.attribute filter line manager,model_product_attribute_filter_line,base.group_system,1,1,1,1
-access_product_attribute_line_manager,product.attribute line manager,model_product_attribute_line,base.group_system,1,1,1,1
+access_product_product_attribute_manager,product.template.attribute value manager,model_product_template_attribute_value,base.group_system,1,1,1,1
+access_product_template_attribute_exclusion_manager,product.template.attribute exclusion manager,model_product_template_attribute_exclusion,base.group_system,1,1,1,1
+access_product_template_attribute_line_manager,product.template.attribute line manager,model_product_template_attribute_line,base.group_system,1,1,1,1
diff --git a/addons/product/tests/test_product_attribute_value_config.py b/addons/product/tests/test_product_attribute_value_config.py
index 8a290aa9c02..eeb1275420a 100644
--- a/addons/product/tests/test_product_attribute_value_config.py
+++ b/addons/product/tests/test_product_attribute_value_config.py
@@ -26,11 +26,15 @@ class TestProductAttributeValueConfig(TransactionCase):
self.computer_case.create_variant_ids()
- def test_product_product_attribute_values_creation(self):
- self.assertEqual(len(self.computer_ssd_attribute_lines.product_value_ids), 2, 'Product attribute values (ssd) were not automatically created')
- self.assertEqual(len(self.computer_ram_attribute_lines.product_value_ids), 3, 'Product attribute values (ram) were not automatically created')
- self.assertEqual(len(self.computer_hdd_attribute_lines.product_value_ids), 3, 'Product attribute values (hdd) were not automatically created')
- self.assertEqual(len(self.computer_case_size_attribute_lines.product_value_ids), 3, 'Product attribute values (size) were not automatically created')
+ def test_product_template_attribute_values_creation(self):
+ self.assertEqual(len(self.computer_ssd_attribute_lines.product_template_value_ids), 2,
+ 'Product attribute values (ssd) were not automatically created')
+ self.assertEqual(len(self.computer_ram_attribute_lines.product_template_value_ids), 3,
+ 'Product attribute values (ram) were not automatically created')
+ self.assertEqual(len(self.computer_hdd_attribute_lines.product_template_value_ids), 3,
+ 'Product attribute values (hdd) were not automatically created')
+ self.assertEqual(len(self.computer_case_size_attribute_lines.product_template_value_ids), 3,
+ 'Product attribute values (size) were not automatically created')
def test_product_filtered_exclude_for(self):
"""
@@ -58,7 +62,7 @@ class TestProductAttributeValueConfig(TransactionCase):
self._add_hdd_excludes_computer_case()
hdd_4_variant = self.computer.product_variant_ids.filtered(
- lambda variant: self.hdd_4 in variant.product_attribute_value_ids.mapped('product_attribute_value_id'))[0]
+ lambda variant: self.hdd_4 in variant.product_template_attribute_value_ids.mapped('product_attribute_value_id'))[0]
self.assertEqual(len(self.computer_case.get_filtered_variants(hdd_4_variant)), 2)
self.assertFalse(self._get_variant_for_attribute_values(self.computer_case, [self.size_m], hdd_4_variant))
@@ -72,7 +76,7 @@ class TestProductAttributeValueConfig(TransactionCase):
'name': '512 GB',
'attribute_id': self.ssd_attribute.id
})
- self.computer_ssd_attribute_lines = self.env['product.attribute.line'].create({
+ self.computer_ssd_attribute_lines = self.env['product.template.attribute.line'].create({
'product_tmpl_id': self.computer.id,
'attribute_id': self.ssd_attribute.id,
'value_ids': [(6, 0, [self.ssd_256.id, self.ssd_512.id])]
@@ -92,7 +96,7 @@ class TestProductAttributeValueConfig(TransactionCase):
'name': '32 GB',
'attribute_id': self.ram_attribute.id
})
- self.computer_ram_attribute_lines = self.env['product.attribute.line'].create({
+ self.computer_ram_attribute_lines = self.env['product.template.attribute.line'].create({
'product_tmpl_id': self.computer.id,
'attribute_id': self.ram_attribute.id,
'value_ids': [(6, 0, [self.ram_8.id, self.ram_16.id, self.ram_32.id])]
@@ -112,14 +116,14 @@ class TestProductAttributeValueConfig(TransactionCase):
'name': '4 To',
'attribute_id': self.hdd_attribute.id
})
- self.computer_hdd_attribute_lines = self.env['product.attribute.line'].create({
+ self.computer_hdd_attribute_lines = self.env['product.template.attribute.line'].create({
'product_tmpl_id': self.computer.id,
'attribute_id': self.hdd_attribute.id,
'value_ids': [(6, 0, [self.hdd_1.id, self.hdd_2.id, self.hdd_4.id])]
})
def _add_ram_exclude_for(self):
- self.ram_16_excludes_hdd_1 = self.env['product.attribute.filter.line'].create({
+ self.ram_16_excludes_hdd_1 = self.env['product.template.attribute.exclusion'].create({
'product_tmpl_id': self.computer.id,
'value_ids': [(6, 0, [self._get_product_value_id(self.computer_hdd_attribute_lines, self.hdd_1).id])]
})
@@ -141,14 +145,14 @@ class TestProductAttributeValueConfig(TransactionCase):
'name': 'XL',
'attribute_id': self.size_attribute.id
})
- self.computer_case_size_attribute_lines = self.env['product.attribute.line'].create({
+ self.computer_case_size_attribute_lines = self.env['product.template.attribute.line'].create({
'product_tmpl_id': self.computer_case.id,
'attribute_id': self.size_attribute.id,
'value_ids': [(6, 0, [self.size_m.id, self.size_l.id, self.size_xl.id])]
})
def _add_hdd_excludes_computer_case(self):
- self.hdd_4_excludes_computer_case_m = self.env['product.attribute.filter.line'].create({
+ self.hdd_4_excludes_computer_case_m = self.env['product.template.attribute.exclusion'].create({
'product_tmpl_id': self.computer_case.id,
'value_ids': [(6, 0, [self._get_product_value_id(self.computer_case_size_attribute_lines, self.size_m).id])]
})
@@ -156,8 +160,8 @@ class TestProductAttributeValueConfig(TransactionCase):
'exclude_for': [(6, 0, [self.hdd_4_excludes_computer_case_m.id])]
})
- def _get_product_value_id(self, product_attribute_lines, product_attribute_value):
- return product_attribute_lines.product_value_ids.filtered(
+ def _get_product_value_id(self, product_template_attribute_lines, product_attribute_value):
+ return product_template_attribute_lines.product_template_value_ids.filtered(
lambda product_value_id: product_value_id.product_attribute_value_id == product_attribute_value)[0]
def _get_variant_for_attribute_values(self, product, attribute_values, reference_product=None):
diff --git a/addons/product/tests/test_variants.py b/addons/product/tests/test_variants.py
index e27b5fa1eab..0819c7dfdf3 100644
--- a/addons/product/tests/test_variants.py
+++ b/addons/product/tests/test_variants.py
@@ -169,7 +169,7 @@ class TestVariantsNoCreate(common.TestProductCommon):
super(TestVariantsNoCreate, self).setUp()
self.size = self.env['product.attribute'].create({
'name': 'Size',
- 'create_variant': 'never',
+ 'create_variant': 'no_variant',
'value_ids': [(0, 0, {'name': 'S'}), (0, 0, {'name': 'M'}), (0, 0, {'name': 'L'})],
})
self.size_S = self.size.value_ids[0]
diff --git a/addons/product/views/product_attribute_views.xml b/addons/product/views/product_attribute_views.xml
index 00929165e80..43cd832d743 100644
--- a/addons/product/views/product_attribute_views.xml
+++ b/addons/product/views/product_attribute_views.xml
@@ -1,58 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
-
- <record id="product_product_attribute_value_view_tree" model="ir.ui.view">
- <field name="name">product.product.attribute.value.view.tree</field>
- <field name="model">product.product.attribute.value</field>
- <field name="type">tree</field>
- <field name="arch" type="xml">
- <tree string="Attributes" create="0" delete="0">
- <field name="attribute_id"/>
- <field name="name"/>
- <field name="price_extra" />
- </tree>
- </field>
- </record>
-
- <record id="product_product_attribute_value_view_form" model="ir.ui.view">
- <field name="name">product.product.attribute.value.view.form.</field>
- <field name="model">product.product.attribute.value</field>
- <field name="type">form</field>
- <field name="arch" type="xml">
- <form string="Product Attribute" create="0" delete="0">
- <group>
- <field name="name" />
- <field name="price_extra" />
- <field name="exclude_for" widget="one2many_list" mode="tree">
- <tree editable="top">
- <field name="product_tmpl_id" />
- <field name="value_ids" widget="many2many_tags" options="{'no_create': true}" />
- </tree>
- </field>
- </group>
- </form>
- </field>
- </record>
-
- <record id="product_attribute_value_action" model="ir.actions.act_window">
- <field name="name">Product Variant Values</field>
- <field name="res_model">product.product.attribute.value</field>
- <field name="view_mode">tree,form</field>
- <field name="domain">[('product_tmpl_id', '=', active_id)]</field>
- <field name="view_ids"
- eval="[(5, 0, 0),
- (0, 0, {'view_mode': 'tree', 'view_id': ref('product.product_product_attribute_value_view_tree')}),
- (0, 0, {'view_mode': 'form', 'view_id': ref('product.product_product_attribute_value_view_form')})]" />
- <field name="context">{
- 'default_product_tmpl_id': active_id
- }</field>
- </record>
-
<record id="attribute_tree_view" model="ir.ui.view">
<field name="name">product.attribute.tree</field>
<field name="model">product.attribute</field>
<field name="arch" type="xml">
- <tree string="Variant Values" editable="top">
+ <tree string="Variant Values">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="create_variant"/>
@@ -74,14 +26,12 @@
<group name="values_ids">
<label for="value_ids" string="Attribute Values"/>
<field name="value_ids" widget="one2many_list" nolabel="1">
- <tree string="Values" editable="bottom">
+ <tree string="Values" editable="top">
+ <field name="sequence" widget="handle"/>
<field name="name"/>
</tree>
- <form string="Values">
- <field name="name"/>
- </form>
</field>
- <field name="create_variant"/>
+ <field name="create_variant" widget="radio"/>
</group>
</form>
</field>
@@ -91,7 +41,7 @@
<field name="name">Attributes</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">product.attribute</field>
- <field name="view_mode">tree</field>
+ <field name="view_mode">tree,form</field>
<field name="view_type">form</field>
</record>
@@ -115,9 +65,9 @@
<field name="view_type">form</field>
</record>
- <record id="product_attribute_line_form" model="ir.ui.view">
- <field name="name">product.attribute.line.form</field>
- <field name="model">product.attribute.line</field>
+ <record id="product_template_attribute_line_form" model="ir.ui.view">
+ <field name="name">product.template.attribute.line.form</field>
+ <field name="model">product.template.attribute.line</field>
<field name="mode">primary</field>
<field name="priority" eval="8"/>
<field name="arch" type="xml">
@@ -138,4 +88,51 @@
</field>
</record>
+ <record id="product_template_attribute_value_view_tree" model="ir.ui.view">
+ <field name="name">product.template.attribute.value.view.tree</field>
+ <field name="model">product.template.attribute.value</field>
+ <field name="type">tree</field>
+ <field name="arch" type="xml">
+ <tree string="Attributes" create="0" delete="0">
+ <field name="attribute_id"/>
+ <field name="name"/>
+ <field name="price_extra" />
+ </tree>
+ </field>
+ </record>
+
+ <record id="product_template_attribute_value_view_form" model="ir.ui.view">
+ <field name="name">product.template.attribute.value.view.form.</field>
+ <field name="model">product.template.attribute.value</field>
+ <field name="type">form</field>
+ <field name="arch" type="xml">
+ <form string="Product Attribute" create="0" delete="0">
+ <group>
+ <field name="name" />
+ <field name="price_extra" />
+ <field name="exclude_for" widget="one2many_list" mode="tree">
+ <tree editable="top">
+ <field name="product_tmpl_id" />
+ <field name="value_ids" widget="many2many_tags" options="{'no_create': true}" />
+ </tree>
+ </field>
+ </group>
+ </form>
+ </field>
+ </record>
+
+ <record id="product_attribute_value_action" model="ir.actions.act_window">
+ <field name="name">Product Variant Values</field>
+ <field name="res_model">product.template.attribute.value</field>
+ <field name="view_mode">tree,form</field>
+ <field name="domain">[('product_tmpl_id', '=', active_id)]</field>
+ <field name="view_ids"
+ eval="[(5, 0, 0),
+ (0, 0, {'view_mode': 'tree', 'view_id': ref('product.product_template_attribute_value_view_tree')}),
+ (0, 0, {'view_mode': 'form', 'view_id': ref('product.product_template_attribute_value_view_form')})]" />
+ <field name="context">{
+ 'default_product_tmpl_id': active_id
+ }</field>
+ </record>
+
</odoo>
diff --git a/addons/sale/controllers/product_configurator.py b/addons/sale/controllers/product_configurator.py
index 784d4f8b51e..8bc58f09139 100644
--- a/addons/sale/controllers/product_configurator.py
+++ b/addons/sale/controllers/product_configurator.py
@@ -90,7 +90,7 @@ class ProductConfiguratorController(http.Controller):
exclusions.parent_exclusions: exclusions coming from the reference_product
"""
- product_attribute_values = request.env['product.product.attribute.value'].search([
+ product_attribute_values = request.env['product.template.attribute.value'].search([
('product_tmpl_id', '=', product.id),
('product_attribute_value_id', 'in', product.attribute_line_ids.mapped('value_ids').ids),
])
@@ -110,7 +110,7 @@ class ProductConfiguratorController(http.Controller):
if reference_product:
parent_exclusions = [
value_id
- for filter_line in reference_product.mapped('product_attribute_value_ids.exclude_for').filtered(
+ for filter_line in reference_product.mapped('product_template_attribute_value_ids.exclude_for').filtered(
lambda filter_line: filter_line.product_tmpl_id == product
) for value_id in filter_line.value_ids.ids]
@@ -129,14 +129,14 @@ class ProductConfiguratorController(http.Controller):
return product_context
def _get_combination_info(self, product_template_id, product_id, combination, add_qty, pricelist, **kw):
- product_attribute_values = request.env['product.product.attribute.value'].browse(combination)
- filtered_product_attribute_values = product_attribute_values.filtered(
- lambda product_attribute_value: product_attribute_value.attribute_id.create_variant != 'never'
+ product_template_attribute_values = request.env['product.template.attribute.value'].browse(combination)
+ filtered_product_template_attribute_values = product_template_attribute_values.filtered(
+ lambda product_attribute_value: product_attribute_value.attribute_id.create_variant != 'no_variant'
)
context = {
'quantity': add_qty,
'pricelist': pricelist.id if pricelist else None,
- 'current_attributes_price_extra': [product_attribute_value.price_extra or 0.0 for product_attribute_value in product_attribute_values]
+ 'current_attributes_price_extra': [product_attribute_value.price_extra or 0.0 for product_attribute_value in product_template_attribute_values]
}
product_template = request.env['product.template'].with_context(context).browse(product_template_id)
@@ -149,8 +149,8 @@ class ProductConfiguratorController(http.Controller):
('product_tmpl_id', '=', product_template_id)
])
product = products.filtered(
- lambda product: all(product_attribute_value in product.product_attribute_value_ids
- for product_attribute_value in filtered_product_attribute_values)
+ lambda product: all(product_attribute_value in product.product_template_attribute_value_ids
+ for product_attribute_value in filtered_product_template_attribute_values)
)
product_id = None
@@ -159,8 +159,8 @@ class ProductConfiguratorController(http.Controller):
if(product):
product = product.with_context(
no_variant_attributes_price_extra=[product_attribute_value.price_extra or 0.0
- for product_attribute_value in product_attribute_values.filtered(
- lambda product_attribute_value: product_attribute_value.attribute_id.create_variant == 'never'
+ for product_attribute_value in product_template_attribute_values.filtered(
+ lambda product_attribute_value: product_attribute_value.attribute_id.create_variant == 'no_variant'
)
]
)
@@ -169,9 +169,9 @@ class ProductConfiguratorController(http.Controller):
price = product.price or list_price
display_name = [product_template.name]
- if filtered_product_attribute_values:
+ if filtered_product_template_attribute_values:
display_name.append(' (')
- display_name.append(', '.join(filtered_product_attribute_values.mapped('name')))
+ display_name.append(', '.join(filtered_product_template_attribute_values.mapped('name')))
display_name.append(')')
if pricelist and pricelist.currency_id != product_template.currency_id:
diff --git a/addons/sale/data/sale_demo.xml b/addons/sale/data/sale_demo.xml
index ba7ddd95d2f..25d4431a7d8 100644
--- a/addons/sale/data/sale_demo.xml
+++ b/addons/sale/data/sale_demo.xml
@@ -735,7 +735,7 @@ Thanks!</field>
<field name="is_custom">True</field>
</record>
- <record id="product.product_attribute_line_1" model="product.attribute.line">
+ <record id="product.product_template_attribute_line_1" model="product.template.attribute.line">
<field name="value_ids" eval="[(4,ref('product_attribute_value_7'))]"/>
</record>
@@ -752,33 +752,33 @@ Thanks!</field>
</record>
<!--
- Handle automatically created product.product.attribute.value.
- Check "product.product_product_attribute_value_1" for more information about this
+ Handle automatically created product.template.attribute.value.
+ Check "product.product_template_attribute_value_1" for more information about this
-->
<function model="ir.model.data" name="_update_xmlids">
<value model="base" eval="[{
- 'xml_id': 'sale.product_product_attribute_value_3',
- 'record': obj().env.ref('product.product_attribute_line_1').product_value_ids[0],
+ 'xml_id': 'sale.product_template_attribute_value_3',
+ 'record': obj().env.ref('product.product_template_attribute_line_1').product_template_value_ids[0],
'noupdate': True,
}, {
- 'xml_id': 'sale.product_product_attribute_value_4',
- 'record': obj().env.ref('product.product_attribute_line_4').product_value_ids[0],
+ 'xml_id': 'sale.product_template_attribute_value_4',
+ 'record': obj().env.ref('product.product_template_attribute_line_4').product_template_value_ids[0],
'noupdate': True,
}, {
- 'xml_id': 'sale.product_product_attribute_value_5',
- 'record': obj().env.ref('product.product_attribute_line_2').product_value_ids[1],
+ 'xml_id': 'sale.product_template_attribute_value_5',
+ 'record': obj().env.ref('product.product_template_attribute_line_2').product_template_value_ids[1],
'noupdate': True,
}]"/>
</function>
- <record id="product_attribute_filter_line_1" model="product.attribute.filter.line">
+ <record id="product_template_attribute_exclusion_1" model="product.template.attribute.exclusion">
<field name="product_tmpl_id" ref="product.product_product_4_product_template" />
- <field name="value_ids" eval="[(6,0,[ref('sale.product_product_attribute_value_5')])]"/>
+ <field name="value_ids" eval="[(6,0,[ref('sale.product_template_attribute_value_5')])]"/>
</record>
- <record id="product_attribute_filter_line_2" model="product.attribute.filter.line">
+ <record id="product_template_attribute_exclusion_2" model="product.template.attribute.exclusion">
<field name="product_tmpl_id" ref="product.product_product_11_product_template" />
- <field name="value_ids" eval="[(6,0,[ref('sale.product_product_attribute_value_4')])]"/>
+ <field name="value_ids" eval="[(6,0,[ref('sale.product_template_attribute_value_4')])]"/>
</record>
<!--
@@ -786,21 +786,21 @@ Thanks!</field>
- The "Customizable Desk's Black" attribute
- The "Office Chair's Steel" attribute
-->
- <record id="product.product_product_attribute_value_1" model="product.product.attribute.value">
- <field name="exclude_for" eval="[(6,0,[ref('sale.product_attribute_filter_line_1') ,ref('sale.product_attribute_filter_line_2')])]" />
+ <record id="product.product_template_attribute_value_1" model="product.template.attribute.value">
+ <field name="exclude_for" eval="[(6,0,[ref('sale.product_template_attribute_exclusion_1') ,ref('sale.product_template_attribute_exclusion_2')])]" />
</record>
- <record id="product_attribute_filter_line_3" model="product.attribute.filter.line">
+ <record id="product_template_attribute_exclusion_3" model="product.template.attribute.exclusion">
<field name="product_tmpl_id" ref="product.product_product_11_product_template" />
- <field name="value_ids" eval="[(6,0,[ref('product.product_product_attribute_value_2')])]"/>
+ <field name="value_ids" eval="[(6,0,[ref('product.product_template_attribute_value_2')])]"/>
</record>
<!--
The "Customizable Desk's Steel" attribute value will excude:
- The "Office Chair's Aluminium" attribute
-->
- <record id="product_product_attribute_value_3" model="product.product.attribute.value">
- <field name="exclude_for" eval="[(6,0,[ref('sale.product_attribute_filter_line_3')])]" />
+ <record id="product_template_attribute_value_3" model="product.template.attribute.value">
+ <field name="exclude_for" eval="[(6,0,[ref('sale.product_template_attribute_exclusion_3')])]" />
</record>
</data>
</odoo>
diff --git a/addons/sale/models/product_product.py b/addons/sale/models/product_product.py
index fd0a11358c5..87e160673e9 100644
--- a/addons/sale/models/product_product.py
+++ b/addons/sale/models/product_product.py
@@ -61,7 +61,7 @@ class ProductAttribute(models.Model):
type = fields.Selection([
('radio', 'Radio'),
('select', 'Select'),
- ('color', 'Color')], default='radio')
+ ('color', 'Color')], default='radio', required=True)
class ProductAttributeValue(models.Model):
@@ -76,7 +76,7 @@ class ProductAttributeValue(models.Model):
class ProductProductAttributeValue(models.Model):
- _inherit = "product.product.attribute.value"
+ _inherit = "product.template.attribute.value"
html_color = fields.Char('HTML Color Index', related="product_attribute_value_id.html_color")
is_custom = fields.Boolean('Is custom value', related="product_attribute_value_id.is_custom")
diff --git a/addons/sale/models/product_template.py b/addons/sale/models/product_template.py
index b02b1cd6baf..cbcb9526287 100644
--- a/addons/sale/models/product_template.py
+++ b/addons/sale/models/product_template.py
@@ -55,14 +55,14 @@ class ProductTemplate(models.Model):
}
return action
- def create_product_variant(self, product_attribute_value_ids):
+ def create_product_variant(self, product_template_attribute_value_ids):
self.ensure_one()
attribute_value_ids = \
- self.env['product.product.attribute.value'] \
- .browse(json.loads(product_attribute_value_ids)) \
+ self.env['product.template.attribute.value'] \
+ .browse(json.loads(product_template_attribute_value_ids)) \
.mapped('product_attribute_value_id') \
- .filtered(lambda attribute_value_id: attribute_value_id.attribute_id.create_variant != 'never')
+ .filtered(lambda attribute_value_id: attribute_value_id.attribute_id.create_variant != 'no_variant')
product_variant = self.env['product.product'].create({
'product_tmpl_id': self.id,
diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py
index b365f9c9d45..13c8943094c 100644
--- a/addons/sale/models/sale.py
+++ b/addons/sale/models/sale.py
@@ -939,9 +939,9 @@ class SaleOrderLine(models.Model):
if self._context.get('no_variant_attribute_values'):
attributes = self._context.get('no_variant_attribute_values')
res['product_no_variant_attribute_values'] = [(6, 0, [int(attribute['value']) for attribute in attributes])]
- if self._context.get('product_custom_variant_values'):
- values = self._context.get('product_custom_variant_values')
- res['product_custom_variant_values'] = [(0, 0, value) for value in values]
+ if self._context.get('product_custom_attribute_values'):
+ values = self._context.get('product_custom_attribute_values')
+ res['product_custom_attribute_value_ids'] = [(0, 0, value) for value in values]
return res
@api.depends('product_uom_qty', 'discount', 'price_unit', 'tax_id')
@@ -1131,10 +1131,10 @@ class SaleOrderLine(models.Model):
product_updatable = fields.Boolean(compute='_compute_product_updatable', string='Can Edit Product', readonly=True, default=True)
product_uom_qty = fields.Float(string='Ordered Quantity', digits=dp.get_precision('Product Unit of Measure'), required=True, default=1.0)
product_uom = fields.Many2one('uom.uom', string='Unit of Measure')
- product_custom_variant_values = fields.One2many('product.attribute.custom.value', 'sale_order_line_id', string='User entered custom product attribute values')
- # M2M holding the values of product.attribute with create_variant field set to 'never'
+ product_custom_attribute_value_ids = fields.One2many('product.attribute.custom.value', 'sale_order_line_id', string='User entered custom product attribute values')
+ # M2M holding the values of product.attribute with create_variant field set to 'no_variant'
# It allows keeping track of the extra_price associated to those attribute values and add them to the SO line description
- product_no_variant_attribute_values = fields.Many2many('product.product.attribute.value', string='Product attribute values that do not create variants')
+ product_no_variant_attribute_values = fields.Many2many('product.template.attribute.value', string='Product attribute values that do not create variants')
# Non-stored related field to allow portal user to see the image of the product he has ordered
product_image = fields.Binary('Product Image', related="product_id.image", store=False)
@@ -1441,13 +1441,13 @@ class SaleOrderLine(models.Model):
name = self.get_sale_order_line_multiline_description_sale(product)
- if self.product_custom_variant_values or self.product_no_variant_attribute_values:
+ if self.product_custom_attribute_value_ids or self.product_no_variant_attribute_values:
name += '\n'
- if self.product_custom_variant_values:
- for product_custom_variant_value in self.product_custom_variant_values:
- if product_custom_variant_value.custom_value and product_custom_variant_value.custom_value.strip():
- name += '\n' + product_custom_variant_value.attribute_value_id.name + ': ' + product_custom_variant_value.custom_value.strip()
+ if self.product_custom_attribute_value_ids:
+ for product_custom_attribute_value in self.product_custom_attribute_value_ids:
+ if product_custom_attribute_value.custom_value and product_custom_attribute_value.custom_value.strip():
+ name += '\n' + product_custom_attribute_value.attribute_value_id.name + ': ' + product_custom_attribute_value.custom_value.strip()
if self.product_no_variant_attribute_values:
for no_variant_attribute_value in self.product_no_variant_attribute_values.filtered(
diff --git a/addons/sale/security/ir.model.access.csv b/addons/sale/security/ir.model.access.csv
index 5f0e5140394..d8019d911e7 100644
--- a/addons/sale/security/ir.model.access.csv
+++ b/addons/sale/security/ir.model.access.csv
@@ -41,10 +41,10 @@ access_product_template_sale_manager,product.template salemanager,model_product_
access_product_product_sale_manager,product.product salemanager,model_product_product,sales_team.group_sale_manager,1,1,1,1
access_product_attribute_sale_manager,product.attribute manager,product.model_product_attribute,sales_team.group_sale_manager,1,1,1,1
access_product_attribute_value_sale_manager,product.attribute manager value,product.model_product_attribute_value,sales_team.group_sale_manager,1,1,1,1
-access_product_product_attribute_sale_manager,product.product.attribute manager value,product.model_product_product_attribute_value,sales_team.group_sale_manager,1,1,1,1
+access_product_product_attribute_sale_manager,product.template.attribute manager value,product.model_product_template_attribute_value,sales_team.group_sale_manager,1,1,1,1
access_product_product_attribute_custom_value,product.attribute.custom value manager,model_product_attribute_custom_value,sales_team.group_sale_manager,1,1,1,1
-access_product_attribute_filter_line_sale_manager,product.attribute manager filter line,product.model_product_attribute_filter_line,sales_team.group_sale_manager,1,1,1,1
-access_product_attribute_line_sale_manager,product.attribute manager line,product.model_product_attribute_line,sales_team.group_sale_manager,1,1,1,1
+access_product_template_attribute_exclusion_sale_manager,product.attribute manager filter line,product.model_product_template_attribute_exclusion,sales_team.group_sale_manager,1,1,1,1
+access_product_template_attribute_line_sale_manager,product.attribute manager line,product.model_product_template_attribute_line,sales_team.group_sale_manager,1,1,1,1
access_account_tax_sale_manager,account.tax sale manager,account.model_account_tax,sales_team.group_sale_salesman,1,0,0,0
access_account_journal_sale_manager,account.journal sale manager,account.model_account_journal,sales_team.group_sale_salesman,1,0,0,0
access_account_invoice_tax_sale_manager,account.invoice.tax sale manager,account.model_account_invoice_tax,sales_team.group_sale_salesman,1,0,0,0
diff --git a/addons/sale/static/src/js/product_configurator_controller.js b/addons/sale/static/src/js/product_configurator_controller.js
index d9341853b4a..6f2306daa17 100644
--- a/addons/sale/static/src/js/product_configurator_controller.js
+++ b/addons/sale/static/src/js/product_configurator_controller.js
@@ -120,7 +120,7 @@ var ProductConfiguratorFormController = FormController.extend({
product_id: productId,
quantity: parseFloat($modal.find('input[name="add_qty"]').val() || 1),
variant_values: variantValues,
- product_custom_variant_values: productCustomVariantValues,
+ product_custom_attribute_values: productCustomVariantValues,
no_variant_attribute_values: noVariantAttributeValues
};
@@ -185,7 +185,7 @@ var ProductConfiguratorFormController = FormController.extend({
* @param {Array} products the list of added products
* {integer} products.product_id: the id of the product
* {integer} products.quantity: the added quantity for this product
- * {Array} products.product_custom_variant_values:
+ * {Array} products.product_custom_attribute_values:
* see product_configurator_mixin.getCustomVariantValues
* {Array} products.no_variant_attribute_values:
* see product_configurator_mixin.getNoVariantAttributeValues
diff --git a/addons/sale/static/src/js/product_configurator_mixin.js b/addons/sale/static/src/js/product_configurator_mixin.js
index aba7ae2e6f3..f9191519b71 100644
--- a/addons/sale/static/src/js/product_configurator_mixin.js
+++ b/addons/sale/static/src/js/product_configurator_mixin.js
@@ -220,8 +220,8 @@ var ProductConfiguratorMixin = {
getNoVariantAttributeValues: function ($container) {
var noVariantAttributeValues = [];
var variantsValuesSelectors = [
- 'input.never.js_variant_change:checked',
- 'select.never.js_variant_change'
+ 'input.no_variant.js_variant_change:checked',
+ 'select.no_variant.js_variant_change'
];
$container.find(variantsValuesSelectors.join(',')).each(function (){
@@ -246,7 +246,7 @@ var ProductConfiguratorMixin = {
},
/**
- * Will return the list of selected product.product.attribute.value ids
+ * Will return the list of selected product.template.attribute.value ids
* For the modal, the "main product"'s attribute values are stored in the
* "unchanged_value_ids" data
*
diff --git a/addons/sale/static/src/js/product_configurator_modal.js b/addons/sale/static/src/js/product_configurator_modal.js
index 715018d312f..57a81271773 100644
--- a/addons/sale/static/src/js/product_configurator_modal.js
+++ b/addons/sale/static/src/js/product_configurator_modal.js
@@ -41,7 +41,7 @@ var OptionalProductsModal = Dialog.extend(ServicesMixin, ProductConfiguratorMixi
* @param {integer} params.rootProduct.product_id
* @param {integer} params.rootProduct.quantity
* @param {Array} params.rootProduct.variant_values
- * @param {Array} params.rootProduct.product_custom_variant_values
+ * @param {Array} params.rootProduct.product_custom_attribute_values
* @param {Array} params.rootProduct.no_variant_attribute_values
*/
init: function (parent, params) {
@@ -176,7 +176,7 @@ var OptionalProductsModal = Dialog.extend(ServicesMixin, ProductConfiguratorMixi
products.push({
product_id: parseInt($item.find('input.product_id').val()),
quantity: quantity,
- product_custom_variant_values: productCustomVariantValues,
+ product_custom_attribute_values: productCustomVariantValues,
no_variant_attribute_values: noVariantAttributeValues
});
});
@@ -201,14 +201,14 @@ var OptionalProductsModal = Dialog.extend(ServicesMixin, ProductConfiguratorMixi
.attr("src", "/web/image/product.product/" + productId + "/image_medium");
if (this.rootProduct &&
- (this.rootProduct.product_custom_variant_values ||
+ (this.rootProduct.product_custom_attribute_values ||
this.rootProduct.no_variant_attribute_values)) {
var $productDescription = $modalContent
.find('.main_product')
.find('td.td-product_name div.text-muted.small');
var description = $productDescription.html();
- $.each(this.rootProduct.product_custom_variant_values, function (){
+ $.each(this.rootProduct.product_custom_attribute_values, function (){
description += '<br/>' + this.attribute_value_name + ': ' + this.custom_value;
});
diff --git a/addons/sale/views/product_attribute_views.xml b/addons/sale/views/product_attribute_views.xml
index 90dec1dbd5d..13f27d3a30a 100644
--- a/addons/sale/views/product_attribute_views.xml
+++ b/addons/sale/views/product_attribute_views.xml
@@ -13,10 +13,10 @@
</field>
</record>
- <record id="product_attribute_line_form" model="ir.ui.view">
- <field name="name">product.attribute.line.form.inherit</field>
- <field name="model">product.attribute.line</field>
- <field name="inherit_id" ref="product.product_attribute_line_form"/>
+ <record id="product_template_attribute_line_form" model="ir.ui.view">
+ <field name="name">product.template.attribute.line.form.inherit</field>
+ <field name="model">product.template.attribute.line</field>
+ <field name="inherit_id" ref="product.product_template_attribute_line_form"/>
<field name="priority" eval="8"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='value_ids']/tree/field[@name='name']" position="after">
@@ -31,21 +31,20 @@
<field name="inherit_id" ref="product.product_attribute_view_form"/>
<field name="priority" eval="8"/>
<field name="arch" type="xml">
- <xpath expr="//group[@name='main_fields']" position="after">
- <group>
- <field name="type"/>
- </group>
+ <xpath expr="//field[@name='create_variant']" position="before">
+ <field name="type" widget="radio"/>
</xpath>
<xpath expr="//field[@name='value_ids']/tree/field[@name='name']" position="after">
+ <field name="is_custom" groups="product.group_product_variant" />
<field name="html_color"/>
</xpath>
</field>
</record>
- <record id="product_product_attribute_value_view_tree_inherit" model="ir.ui.view">
- <field name="name">product.product.attribute.value.view.tree.inherit</field>
- <field name="model">product.product.attribute.value</field>
- <field name="inherit_id" ref="product.product_product_attribute_value_view_tree"/>
+ <record id="product_template_attribute_value_view_tree_inherit" model="ir.ui.view">
+ <field name="name">product.template.attribute.value.view.tree.inherit</field>
+ <field name="model">product.template.attribute.value</field>
+ <field name="inherit_id" ref="product.product_template_attribute_value_view_tree"/>
<field name="arch" type="xml">
<field name="name" position="after">
<field name="html_color"/>
@@ -53,10 +52,10 @@
</field>
</record>
- <record id="product_product_attribute_value_view_form_inherit" model="ir.ui.view">
- <field name="name">product.product.attribute.value.view.form.inherit</field>
- <field name="model">product.product.attribute.value</field>
- <field name="inherit_id" ref="product.product_product_attribute_value_view_form"/>
+ <record id="product_template_attribute_value_view_form_inherit" model="ir.ui.view">
+ <field name="name">product.template.attribute.value.view.form.inherit</field>
+ <field name="model">product.template.attribute.value</field>
+ <field name="inherit_id" ref="product.product_template_attribute_value_view_form"/>
<field name="arch" type="xml">
<field name="name" position="after">
<field name="html_color" readonly="True" />
diff --git a/addons/sale/views/sale_product_configurator_templates.xml b/addons/sale/views/sale_product_configurator_templates.xml
index c63f878cf7a..cf6010ee5cb 100644
--- a/addons/sale/views/sale_product_configurator_templates.xml
+++ b/addons/sale/views/sale_product_configurator_templates.xml
@@ -185,7 +185,7 @@
t-att-data-attribute_id="variant_id.attribute_id.id"
t-attf-class="form-control js_variant_change #{variant_id.attribute_id.create_variant}"
t-att-name="'attribute-%s-%s' % (product.id, variant_id.attribute_id.id)">
- <t t-foreach="variant_id.product_value_ids" t-as="value_id">
+ <t t-foreach="variant_id.product_template_value_ids" t-as="value_id">
<option t-att-value="value_id.id"
t-att-data-value_id="value_id.product_attribute_value_id.id"
t-att-data-value_name="value_id.name"
@@ -207,7 +207,7 @@
<t t-if="variant_id.attribute_id.type == 'radio'">
<ul t-att-data-attribute_id="variant_id.attribute_id.id" class="list-unstyled">
<t t-set="inc" t-value="0"/>
- <t t-foreach="variant_id.product_value_ids" t-as="value_id">
+ <t t-foreach="variant_id.product_template_value_ids" t-as="value_id">
<li class="form-group js_attribute_value" style="margin: 0;">
<label class="col-form-label" style="margin: 0 20px;">
<input type="radio"
@@ -238,7 +238,7 @@
<t t-if="variant_id.attribute_id.type == 'color'">
<ul t-att-data-attribute_id="variant_id.attribute_id.id" class="list-inline">
<t t-set="inc" t-value="0"/>
- <li t-foreach="variant_id.product_value_ids" t-as="value_id" class="list-inline-item">
+ <li t-foreach="variant_id.product_template_value_ids" t-as="value_id" class="list-inline-item">
<label t-attf-style="background-color:#{value_id.html_color or value_id.name if not value_id.is_custom else ''}"
t-attf-class="css_attribute_color #{'active' if not inc else ''} #{'custom_value' if value_id.is_custom else ''}">
<input type="radio"
diff --git a/addons/sale/views/sale_views.xml b/addons/sale/views/sale_views.xml
index 15737e00496..3dc9ebf3d61 100644
--- a/addons/sale/views/sale_views.xml
+++ b/addons/sale/views/sale_views.xml
@@ -423,7 +423,7 @@
'default_description_sale': name
}"
/>
- <field name="product_custom_variant_values" invisible="1" />
+ <field name="product_custom_attribute_value_ids" invisible="1" />
<field name="product_no_variant_attribute_values" invisible="1" />
<field name="name" widget="section_and_note_text"/>
<field
diff --git a/addons/sale_management/tests/test_sale_ui.py b/addons/sale_management/tests/test_sale_ui.py
index 3606bfe8131..dc323229ee7 100644
--- a/addons/sale_management/tests/test_sale_ui.py
+++ b/addons/sale_management/tests/test_sale_ui.py
@@ -34,19 +34,19 @@ class TestUi(odoo.tests.HttpCase):
}, {
'name': 'PA4',
'type': 'select',
- 'create_variant': 'never'
+ 'create_variant': 'no_variant'
}, {
'name': 'PA5',
'type': 'select',
- 'create_variant': 'never'
+ 'create_variant': 'no_variant'
}, {
'name': 'PA7',
'type': 'color',
- 'create_variant': 'never'
+ 'create_variant': 'no_variant'
}, {
'name': 'PA8',
'type': 'radio',
- 'create_variant': 'never'
+ 'create_variant': 'no_variant'
}])
product_attribute_values = self.env['product.attribute.value'].create([{
@@ -55,7 +55,7 @@ class TestUi(odoo.tests.HttpCase):
'attribute_id': product_attribute.id
} for i in range(1, 11) for product_attribute in product_attributes])
- product_attribute_lines = self.env['product.attribute.line'].create([{
+ product_template_attribute_lines = self.env['product.template.attribute.line'].create([{
'attribute_id': product_attribute.id,
'product_tmpl_id': self.env.ref("product.product_product_4").id,
'value_ids': [(6, 0, product_attribute_values.filtered(
@@ -64,7 +64,7 @@ class TestUi(odoo.tests.HttpCase):
} for product_attribute in product_attributes])
self.env.ref("product.product_product_4").update({
- 'attribute_line_ids': [(4, product_attribute_line.id) for product_attribute_line in product_attribute_lines]
+ 'attribute_line_ids': [(4, product_template_attribute_line.id) for product_template_attribute_line in product_template_attribute_lines]
})
self.phantom_js("/web", "odoo.__DEBUG__.services['web_tour.tour'].run('sale_product_configurator_advanced_tour')", "odoo.__DEBUG__.services['web_tour.tour'].tours.sale_product_configurator_advanced_tour.ready", login="admin")
diff --git a/addons/stock/security/ir.model.access.csv b/addons/stock/security/ir.model.access.csv
index 99ac4a55f32..3f053ff410c 100644
--- a/addons/stock/security/ir.model.access.csv
+++ b/addons/stock/security/ir.model.access.csv
@@ -73,6 +73,6 @@ access_stock_forecast_manager,report.stock.forecast.manager,model_report_stock_f
access_stock_scrap_user,stock.scrap.user,model_stock_scrap,stock.group_stock_user,1,1,1,0
access_product_attribute_manager,product.attribute manager,product.model_product_attribute,stock.group_stock_manager,1,1,1,1
access_product_attribute_value_manager,product.attribute manager value,product.model_product_attribute_value,stock.group_stock_manager,1,1,1,1
-access_product_product_attribute_manager,product.product.attribute manager value,product.model_product_product_attribute_value,stock.group_stock_manager,1,1,1,1
-access_product_attribute_filter_line_manager,product.attribute manager filter line,product.model_product_attribute_filter_line,stock.group_stock_manager,1,1,1,1
-access_product_attribute_line_manager,product.attribute manager line,product.model_product_attribute_line,stock.group_stock_manager,1,1,1,1
+access_product_product_attribute_manager,product.product.attribute manager value,product.model_product_template_attribute_value,stock.group_stock_manager,1,1,1,1
+access_product_template_attribute_exclusion_manager,product.attribute manager filter line,product.model_product_template_attribute_exclusion,stock.group_stock_manager,1,1,1,1
+access_product_template_attribute_line_manager,product.attribute manager line,product.model_product_template_attribute_line,stock.group_stock_manager,1,1,1,1
diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py
index 85b06e188b1..5b7b00500ae 100644
--- a/addons/website_sale/controllers/main.py
+++ b/addons/website_sale/controllers/main.py
@@ -398,7 +398,7 @@ class WebsiteSale(ProductConfiguratorController):
product_id=int(product_id),
add_qty=add_qty,
set_qty=set_qty,
- product_custom_variant_values=json.loads(kw.get('product_custom_variant_values'))
+ product_custom_attribute_values=json.loads(kw.get('product_custom_attribute_values'))
)
return request.redirect("/shop/cart")
@@ -1087,10 +1087,10 @@ class WebsiteSale(ProductConfiguratorController):
add_qty=add_qty,
set_qty=set_qty,
optional_product_ids=optional_product_ids,
- product_custom_variant_values=self._get_product_custom_value(
+ product_custom_attribute_values=self._get_product_custom_value(
int(product_id),
custom_values,
- 'product_custom_variant_values'
+ 'product_custom_attribute_values'
),
no_variant_attribute_values=self._get_product_custom_value(
int(product_id),
@@ -1105,10 +1105,10 @@ class WebsiteSale(ProductConfiguratorController):
product_id=option_id,
set_qty=value.get('quantity'),
linked_line_id=value.get('line_id'),
- product_custom_variant_values=self._get_product_custom_value(
+ product_custom_attribute_values=self._get_product_custom_value(
option_id,
custom_values,
- 'product_custom_variant_values'
+ 'product_custom_attribute_values'
),
no_variant_attribute_values=self._get_product_custom_value(
option_id,
diff --git a/addons/website_sale/data/demo.xml b/addons/website_sale/data/demo.xml
index e8f0d627510..5551ff1795c 100644
--- a/addons/website_sale/data/demo.xml
+++ b/addons/website_sale/data/demo.xml
@@ -588,29 +588,29 @@
<field name="attribute_value_ids" eval="[(6,0,[ref('product.product_attribute_value_5')])]"/>
<field name="categ_id" ref="product.product_category_3"/>
</record>
- <record id="product_attribute_line_1" model="product.attribute.line">
+ <record id="product_template_attribute_line_1" model="product.template.attribute.line">
<field name="product_tmpl_id" ref="product_product_1_product_template"/>
<field name="attribute_id" ref="product.product_attribute_3"/>
<field name="value_ids" eval="[(6,0,[ref('product.product_attribute_value_5'), ref('product.product_attribute_value_6')])]"/>
</record>
<record id="product_product_1_product_template" model="product.template">
- <field name="attribute_line_ids" eval="[(6,0,[ref('product_attribute_line_1')])]"/>
+ <field name="attribute_line_ids" eval="[(6,0,[ref('product_template_attribute_line_1')])]"/>
<field name="public_categ_ids" eval="[(6,0,[ref('website_sale.services')])]"/>
</record>
<record id="product.product_product_4_product_template" model="product.template">
<field name="optional_product_ids" eval="[(6,0,[ref('product.product_product_11_product_template'), ref('product_product_1_product_template')])]"/>
</record>
- <!-- Handle automatically created product.product.attribute.value -->
+ <!-- Handle automatically created product.template.attribute.value -->
<function model="ir.model.data" name="_update_xmlids">
<value model="base" eval="[{
- 'xml_id': 'website_sale.product_product_attribute_value_1',
- 'record': obj().env.ref('website_sale.product_attribute_line_1').product_value_ids[1],
+ 'xml_id': 'website_sale.product_template_attribute_value_1',
+ 'record': obj().env.ref('website_sale.product_template_attribute_line_1').product_template_value_ids[1],
'noupdate': True,
}]"/>
</function>
- <record id="product_product_attribute_value_1" model="product.product.attribute.value">
+ <record id="product_template_attribute_value_1" model="product.template.attribute.value">
<field name="price_extra">18.00</field>
</record>
</odoo>
diff --git a/addons/website_sale/models/sale_order.py b/addons/website_sale/models/sale_order.py
index 6739efd0252..49c4117a49e 100644
--- a/addons/website_sale/models/sale_order.py
+++ b/addons/website_sale/models/sale_order.py
@@ -73,7 +73,7 @@ class SaleOrder(models.Model):
if line_id:
domain += [('id', '=', line_id)]
else:
- domain += [('product_custom_variant_values', '=', False)]
+ domain += [('product_custom_attribute_value_ids', '=', False)]
lines = self.env['sale.order.line'].sudo().search(domain)
@@ -167,9 +167,9 @@ class SaleOrder(models.Model):
if not order_line:
values = self._website_product_id_change(self.id, product_id, qty=1)
- custom_values = kwargs.get('product_custom_variant_values')
+ custom_values = kwargs.get('product_custom_attribute_values')
if custom_values:
- values['product_custom_variant_values'] = [(0, 0, {
+ values['product_custom_attribute_value_ids'] = [(0, 0, {
'attribute_value_id': custom_value['attribute_value_id'],
'custom_value': custom_value['custom_value']
}) for custom_value in custom_values]
diff --git a/addons/website_sale/security/ir.model.access.csv b/addons/website_sale/security/ir.model.access.csv
index 00480b5e449..6da9fe3be19 100644
--- a/addons/website_sale/security/ir.model.access.csv
+++ b/addons/website_sale/security/ir.model.access.csv
@@ -10,10 +10,10 @@ access_product_style,product.style.public,website_sale.model_product_style,,1,0,
access_product_supplierinfo,product.supplierinfo.public,product.model_product_supplierinfo,,1,0,0,0
access_product_attribute_public,product.attribute public,product.model_product_attribute,,1,0,0,0
access_product_attribute_value_public,product.attribute value public,product.model_product_attribute_value,,1,0,0,0
-access_product_product_attribute,product.product.attribute value public,product.model_product_product_attribute_value,,1,0,0,0
+access_product_product_attribute,product.template.attribute value public,product.model_product_template_attribute_value,,1,0,0,0
access_product_product_attribute_custom_value,product.attribute.custom value,sale.model_product_attribute_custom_value,,1,0,0,0
-access_product_attribute_filter_line,product.attribute filter line public,product.model_product_attribute_filter_line,,1,0,0,0
-access_product_attribute_line_public,product.attribute line public,product.model_product_attribute_line,,1,0,0,0
+access_product_template_attribute_exclusion,product.template.attribute exclusion public,product.model_product_template_attribute_exclusion,,1,0,0,0
+access_product_template_attribute_line_public,product.template.attribute line public,product.model_product_template_attribute_line,,1,0,0,0
access_fiscal_position_public,fiscal position public,account.model_account_fiscal_position,base.group_portal,1,0,0,0
access_payment_term,payment term public,account.model_account_payment_term,base.group_portal,1,0,0,0
access_product_supplierinfo_user,product.supplierinfo.user,product.model_product_supplierinfo,base.group_public,1,0,0,0
diff --git a/addons/website_sale/static/src/js/website_sale_options.js b/addons/website_sale/static/src/js/website_sale_options.js
index e15d02a44e7..a846727952e 100644
--- a/addons/website_sale/static/src/js/website_sale_options.js
+++ b/addons/website_sale/static/src/js/website_sale_options.js
@@ -84,7 +84,7 @@ sAnimations.registry.WebsiteSaleOptions = sAnimations.Class.extend(ProductConfig
self.rootProduct = {
product_id: productId,
quantity: parseFloat($form.find('input[name="add_qty"]').val() || 1),
- product_custom_variant_values: self.getCustomVariantValues($form.find('.js_product')),
+ product_custom_attribute_values: self.getCustomVariantValues($form.find('.js_product')),
variant_values: self.getSelectedVariantValues($form.find('.js_product')),
no_variant_attribute_values: self.getNoVariantAttributeValues($form.find('.js_product'))
};
@@ -113,9 +113,9 @@ sAnimations.registry.WebsiteSaleOptions = sAnimations.Class.extend(ProductConfig
*/
_onModalOptionsEmpty: function () {
var $productCustomVariantValues = $('<input>', {
- name: 'product_custom_variant_values',
+ name: 'product_custom_attribute_values',
type: "hidden",
- value: JSON.stringify(this.rootProduct.product_custom_variant_values)
+ value: JSON.stringify(this.rootProduct.product_custom_attribute_values)
});
this.$form.append($productCustomVariantValues);
@@ -165,7 +165,7 @@ sAnimations.registry.WebsiteSaleOptions = sAnimations.Class.extend(ProductConfig
/**
* Submits the form with additional parameters
* - lang
- * - product_custom_variant_values: The products custom variant values
+ * - product_custom_attribute_values: The products custom variant values
*
* @private
* @param {Boolean} goToShop Triggers a page refresh to the url "shop/cart"
diff --git a/addons/website_sale_comparison/data/website_sale_comparison_demo.xml b/addons/website_sale_comparison/data/website_sale_comparison_demo.xml
index 3c48233133e..3e2b9c7d682 100644
--- a/addons/website_sale_comparison/data/website_sale_comparison_demo.xml
+++ b/addons/website_sale_comparison/data/website_sale_comparison_demo.xml
@@ -49,17 +49,17 @@
<field name="attribute_id" ref="product_attribute_8"/>
</record>
- <record id="product_attribute_line_1" model="product.attribute.line">
+ <record id="product_template_attribute_line_1" model="product.template.attribute.line">
<field name="product_tmpl_id" ref="product.product_product_6_product_template"/>
<field name="attribute_id" ref="product_attribute_1"/>
<field name="value_ids" eval="[(6,0,[ref('product_attribute_value_1')])]"/>
</record>
- <record id="product_attribute_line_7" model="product.attribute.line">
+ <record id="product_template_attribute_line_7" model="product.template.attribute.line">
<field name="product_tmpl_id" ref="product.product_product_6_product_template"/>
<field name="attribute_id" ref="product_attribute_7"/>
<field name="value_ids" eval="[(6,0,[ref('product_attribute_value_7')])]"/>
</record>
- <record id="product_attribute_line_8" model="product.attribute.line">
+ <record id="product_template_attribute_line_8" model="product.template.attribute.line">
<field name="product_tmpl_id" ref="product.product_product_6_product_template"/>
<field name="attribute_id" ref="product_attribute_8"/>
<field name="value_ids" eval="[(6,0,[ref('product_attribute_value_8')])]"/>
diff --git a/addons/website_sale_options/i18n/it.po b/addons/website_sale_options/i18n/it.po
deleted file mode 100644
index 84915de3dc6..00000000000
--- a/addons/website_sale_options/i18n/it.po
+++ /dev/null
@@ -1,169 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * website_sale_options
-#
-# Translators:
-# Sergio Zanchetta <primes2h@gmail.com>, 2018
-# Martin Trigaux, 2018
-# Paolo Valier, 2018
-# Paolo Caruccio <paolo.caruccio66@gmail.com>, 2018
-# Léonie Bouchat <lbo@odoo.com>, 2018
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~11.5\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-09-21 13:18+0000\n"
-"PO-Revision-Date: 2018-09-21 13:18+0000\n"
-"Last-Translator: Léonie Bouchat <lbo@odoo.com>, 2018\n"
-"Language-Team: Italian (https://www.transifex.com/odoo/teams/41243/it/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: it\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: website_sale_options
-#: model_terms:ir.ui.view,arch_db:website_sale_options.modal
-msgid ""
-"<i class=\"fa fa-chevron-left\"/> <span class=\"d-none d-md-"
-"inline\">Continue Shopping</span><span class=\"d-inline d-md-"
-"none\">Continue</span>"
-msgstr ""
-"<i class=\"fa fa-chevron-left\"/> <span class=\"d-none d-md-"
-"inline\">Continua i tuoi acquisti</span><span class=\"d-inline d-md-"
-"none\">Continua</span>"
-
-#. module: website_sale_options
-#: model_terms:ir.ui.view,arch_db:website_sale_options.modal
-msgid ""
-"<i class=\"fa fa-shopping-cart fa-fw\"/> <span class=\"d-none d-md-"
-"inline\">Proceed to Checkout</span><span class=\"d-inline d-md-"
-"none\">Checkout</span>"
-msgstr ""
-"<i class=\"fa fa-shopping-cart fa-fw\"/> <span class=\"d-none d-md-"
-"inline\">Procedi all'acquisto</span><span class=\"d-inline d-md-"
-"none\">Acquista</span>"
-
-#. module: website_sale_options
-#: model_terms:ir.ui.view,arch_db:website_sale_options.modal
-msgid "<small>Remove <span class=\"d-none d-md-inline\"> from Cart</span></small>"
-msgstr ""
-"<small>Rimuovi <span class=\"d-none d-md-inline\"> dal "
-"carrello</span></small>"
-
-#. module: website_sale_options
-#: model_terms:ir.ui.view,arch_db:website_sale_options.modal
-msgid "<span class=\"js_item\">1 </span><span class=\"js_items d-none\">5 </span>"
-msgstr "<span class=\"js_item\">1 </span><span class=\"js_items d-none\">5 </span>"
-
-#. module: website_sale_options
-#: model_terms:ir.ui.view,arch_db:website_sale_options.modal
-msgid "<strong>Add<span class=\"d-none d-md-inline\"> to Cart</span></strong>"
-msgstr ""
-"<strong>Aggiungi<span class=\"d-none d-md-inline\"> al "
-"carrello</span></strong>"
-
-#. module: website_sale_options
-#: model_terms:ir.ui.view,arch_db:website_sale_options.modal
-msgid "Add one"
-msgstr "Aggiungi"
-
-#. module: website_sale_options
-#: model_terms:ir.ui.view,arch_db:website_sale_options.modal
-msgid "Add to Cart"
-msgstr "Aggiungi al Carrello"
-
-#. module: website_sale_options
-#: model_terms:ir.ui.view,arch_db:website_sale_options.modal
-msgid "Close"
-msgstr "Chiudi"
-
-#. module: website_sale_options
-#: model_terms:ir.ui.view,arch_db:website_sale_options.modal
-msgid "Continue shopping"
-msgstr "Continua i tuoi acquisti"
-
-#. module: website_sale_options
-#: model:ir.model.fields,field_description:website_sale_options.field_sale_order_line__linked_line_id
-msgid "Linked Order Line"
-msgstr "Riga d'Ordine correlata"
-
-#. module: website_sale_options
-#: code:addons/website_sale_options/models/sale_order.py:46
-#, python-format
-msgid "Option for:"
-msgstr "Opzione per:"
-
-#. module: website_sale_options
-#: code:addons/website_sale_options/models/sale_order.py:48
-#, python-format
-msgid "Option:"
-msgstr "Opzione:"
-
-#. module: website_sale_options
-#: model:ir.model.fields,field_description:website_sale_options.field_product_product__optional_product_ids
-#: model:ir.model.fields,field_description:website_sale_options.field_product_template__optional_product_ids
-msgid "Optional Products"
-msgstr "Prodotti Opzionali"
-
-#. module: website_sale_options
-#: model:ir.model.fields,field_description:website_sale_options.field_sale_order_line__option_line_ids
-msgid "Options Linked"
-msgstr "Opzioni Correlate"
-
-#. module: website_sale_options
-#: model_terms:ir.ui.view,arch_db:website_sale_options.modal
-msgid "Price"
-msgstr "Prezzo"
-
-#. module: website_sale_options
-#: model_terms:ir.ui.view,arch_db:website_sale_options.modal
-msgid "Proceed to checkout"
-msgstr "Procedi all'acquisto"
-
-#. module: website_sale_options
-#: model_terms:ir.ui.view,arch_db:website_sale_options.modal
-msgid "Product"
-msgstr "Prodotto"
-
-#. module: website_sale_options
-#: model:ir.model,name:website_sale_options.model_product_template
-msgid "Product Template"
-msgstr "Modello prodotto"
-
-#. module: website_sale_options
-#: model_terms:ir.ui.view,arch_db:website_sale_options.modal
-msgid "Product not available"
-msgstr "Prodotto non disponibile"
-
-#. module: website_sale_options
-#: model_terms:ir.ui.view,arch_db:website_sale_options.modal
-msgid "Quantity"
-msgstr "Quantità"
-
-#. module: website_sale_options
-#: model_terms:ir.ui.view,arch_db:website_sale_options.modal
-msgid "Remove one"
-msgstr "Rimuovere"
-
-#. module: website_sale_options
-#: model:ir.model,name:website_sale_options.model_sale_order
-msgid "Sale Order"
-msgstr "Ordini di Vendita"
-
-#. module: website_sale_options
-#: model:ir.model,name:website_sale_options.model_sale_order_line
-msgid "Sales Order Line"
-msgstr "Riga Ordine di Vendita"
-
-#. module: website_sale_options
-#: model_terms:ir.ui.view,arch_db:website_sale_options.modal
-msgid "Select Your Options:"
-msgstr "Opzioni di selezione:"
-
-#. module: website_sale_options
-#: model:ir.model.fields,help:website_sale_options.field_product_product__optional_product_ids
-#: model:ir.model.fields,help:website_sale_options.field_product_template__optional_product_ids
-msgid "Suggest optional products when adding the product to a cart."
-msgstr ""
-"Suggerire prodotti opzionali quando si aggiunge il prodotto al carrello."
diff --git a/addons/website_sale_options/static/src/js/website_sale.js b/addons/website_sale_options/static/src/js/website_sale.js
deleted file mode 100644
index 51ad391e3a1..00000000000
--- a/addons/website_sale_options/static/src/js/website_sale.js
+++ /dev/null
@@ -1,141 +0,0 @@
-odoo.define('website_sale_options.website_sale', function (require) {
-'use strict';
-
-var weContext = require('web_editor.context');
-var sAnimations = require('website.content.snippets.animation');
-require('website_sale.website_sale');
-
-sAnimations.registry.WebsiteSale.include({
- /**
- * @override
- */
- _onClickSubmit: function (ev) {
- if ($(ev.currentTarget).is('#add_to_cart, #products_grid .a-submit')) {
- return;
- }
- this._super.apply(this, arguments);
- },
-});
-
-sAnimations.registry.WebsiteSaleOptions = sAnimations.Class.extend({
- selector: '.oe_website_sale',
- read_events: {
- 'click #add_to_cart, #products_grid .product_price .a-submit': '_onClickAdd',
- },
-
- /**
- * @constructor
- */
- init: function () {
- this._super.apply(this, arguments);
-
- this._handleAdd = _.debounce(this._handleAdd.bind(this), 200, true);
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * @private
- */
- _handleAdd: function ($form) {
- var quantity = parseFloat($form.find('input[name="add_qty"]').val() || 1);
- var product_id = parseInt($form.find('input[type="hidden"][name="product_id"], input[type="radio"][name="product_id"]:checked').first().val(),10);
- this._rpc({
- route: '/shop/modal',
- params: {
- product_id: product_id,
- kwargs: {
- context: _.extend({'quantity': quantity}, weContext.get()),
- },
- },
- }).then(function (modal) {
- var $modal = $(modal);
-
- $modal.find('img:first').attr("src", "/web/image/product.product/" + product_id + "/image_medium");
-
- // disable opacity on the <form> if currently active (in case the product is
- // not published), as it interferes with bs modals
- $form.addClass('css_options');
-
- $modal.appendTo($form)
- .modal()
- .on('hidden.bs.modal', function () {
- $form.removeClass('css_options'); // possibly reactivate opacity (see above)
- $(this).remove();
- });
-
- $modal.on('click', '.a-submit', function (ev) {
- var $a = $(this);
- $form.ajaxSubmit({
- url: '/shop/cart/update_option',
- data: {lang: weContext.get().lang},
- success: function (quantity) {
- if (!$a.hasClass('js_goto_shop')) {
- window.location.pathname = window.location.pathname.replace(/shop([\/?].*)?$/, "shop/cart");
- }
- var $q = $(".my_cart_quantity");
- $q.parent().parent().removeClass("d-none", !quantity);
- $q.html(quantity).hide().fadeIn(600);
- }
- });
- $modal.modal('hide');
- ev.preventDefault();
- });
-
- $modal.on('click', '.css_attribute_color input', function (ev) {
- $modal.find('.css_attribute_color').removeClass("active");
- $modal.find('.css_attribute_color:has(input:checked)').addClass("active");
- });
-
- $modal.on("click", "a.js_add, a.js_remove", function (ev) {
- ev.preventDefault();
- var $parent = $(this).parents('.js_product:first');
- $parent.find("a.js_add, span.js_remove").toggleClass('d-none');
- $parent.find("input.js_optional_same_quantity").val( $(this).hasClass("js_add") ? 1 : 0 );
- $parent.find(".js_remove");
- });
-
- $modal.on("change", "input.js_quantity", function () {
- var qty = parseFloat($(this).val());
- if (qty === 1) {
- $(".js_remove .js_items").addClass('d-none');
- $(".js_remove .js_item").removeClass('d-none');
- } else {
- $(".js_remove .js_items").removeClass('d-none').text($(".js_remove .js_items:first").text().replace(/[0-9.,]+/, qty));
- $(".js_remove .js_item").addClass('d-none');
- }
- });
-
- $modal.find('input[name="add_qty"]').val(quantity).change();
- $('.js_add_cart_variants').each(function () {
- $('input.js_variant_change, select.js_variant_change', this).first().trigger('change');
- });
-
- $modal.on("change", 'input[name="add_qty"]', function (ev) {
- var product_id = $($modal.find('span.oe_price[data-product-id]')).first().data('product-id');
- var product_ids = [product_id];
- var $products_dom = [];
- $("ul.js_add_cart_variants[data-attribute_value_ids]").each(function () {
- var $el = $(this);
- $products_dom.push($el);
- _.each($el.data("attribute_value_ids"), function (values) {
- product_ids.push(values[0]);
- });
- });
- });
- });
- },
-
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
-
- _onClickAdd: function (ev) {
- ev.preventDefault();
- var $form = $(ev.currentTarget).closest('form');
- this._handleAdd($form);
- },
-});
-});