diff options
| author | Aurélien Warnon <awa@odoo.com> | 2018-09-26 11:45:00 +0200 |
|---|---|---|
| committer | Aurélien Warnon <awa@odoo.com> | 2018-09-26 15:08:18 +0200 |
| commit | 326dabce09d634e029e68281694ec695fa665666 (patch) | |
| tree | f7e1e482c7a6ae31adfe9f00527a12079b68eb16 /addons | |
| parent | ee766ab93343f7920558be3e99cdd693b54d07ee (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')
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); - }, -}); -}); |
