[IMP] product, *: add image option for color attribute

*: website_sale, sale_product_configurator

With this commit, Image options for the color variant have been
implemented. Now, users can use images to represent pattern colors such
as "leopard" instead of solely selecting a solid color.

- User can add, remove or change the color attribute value image from
  the backend. If no image has been uploaded, it will use the
  corresponding HEX color to represent color/patterns.

task-3381738

Part-of: odoo/odoo#132685
This commit is contained in:
divy-odoo
2023-10-23 20:07:14 +00:00
committed by Antoine Vandevenne (anv)
parent c13332a186
commit c05336d273
8 changed files with 56 additions and 18 deletions
@@ -44,6 +44,12 @@ class ProductAttributeValue(models.Model):
" to display the color if the attribute type is 'Color'.")
display_type = fields.Selection(related='attribute_id.display_type')
color = fields.Integer(string="Color Index", default=_get_default_color)
image = fields.Image(
string="Image",
help="You can upload an image that will be used as the color of the attribute value.",
max_width=70,
max_height=70,
)
_sql_constraints = [
('value_company_uniq',
@@ -60,6 +60,7 @@ class ProductTemplateAttributeValue(models.Model):
is_custom = fields.Boolean(related='product_attribute_value_id.is_custom')
display_type = fields.Selection(related='product_attribute_value_id.display_type')
color = fields.Integer(string="Color", default=_get_default_color)
image = fields.Image(related='product_attribute_value_id.image')
_sql_constraints = [
('attribute_value_unique',
@@ -46,7 +46,12 @@
<field name="name"/>
<field name="display_type" column_invisible="True"/>
<field name="is_custom" groups="product.group_product_variant" column_invisible="parent.display_type == 'multi'"/>
<field name="html_color" column_invisible="parent.display_type != 'color'" widget="color"/>
<field name="html_color" column_invisible="parent.display_type != 'color'" invisible="image" widget="color"/>
<field name="image"
class="oe_avatar text-start float-none"
column_invisible="parent.display_type != 'color'"
options="{'size': [70, 70]}"
widget="image"/>
<field name="default_extra_price"/>
</tree>
</field>
@@ -95,7 +100,8 @@
<field name="attribute_id" optional="hide"/>
<field name="name"/>
<field name="display_type" optional="hide"/>
<field name="html_color" invisible="display_type != 'color'" widget="color"/>
<field name="html_color" invisible="display_type != 'color' or image" widget="color"/>
<field name="image" invisible="display_type != 'color'" widget="image" options="{'size': [70, 70]}"/>
<field name="ptav_active" optional="hide"/>
<field name="price_extra" widget="monetary" options="{'field_digits': True}"/>
<field name="currency_id" column_invisible="True"/>
@@ -113,7 +119,8 @@
<field name="ptav_active" readonly="1" invisible="ptav_active"/>
<field name="name"/>
<field name="display_type" invisible="1"/>
<field name="html_color" invisible="display_type != 'color'"/>
<field name="html_color" invisible="display_type != 'color' or image"/>
<field name="image" invisible="display_type != 'color' or not image" widget="image" options="{'size': [70, 70]}"/>
<field name="price_extra" widget="monetary" options="{'field_digits': True}"/>
<field name="currency_id" invisible="1"/>
<field name="exclude_for" widget="one2many" mode="tree">
@@ -236,6 +236,7 @@ class ProductConfiguratorController(Controller):
'name': str,
'price_extra': float,
'html_color': str|False,
'image': str|False,
'is_custom': bool
}],
'selected_attribute_id': int,
@@ -270,7 +271,7 @@ class ProductConfiguratorController(Controller):
attribute=dict(**ptal.attribute_id.read(['id', 'name', 'display_type'])[0]),
attribute_values=[
dict(
**ptav.read(['name', 'html_color', 'is_custom'])[0],
**ptav.read(['name', 'html_color', 'image', 'is_custom'])[0],
price_extra=ptav.currency_id._convert(
ptav.price_extra,
currency,
@@ -29,6 +29,7 @@ export class ProductTemplateAttributeLine extends Component {
id: Number,
name: String,
html_color: [Boolean, String], // backend sends 'false' when there is no color
image: [Boolean, String], // backend sends 'false' when there is no image set
is_custom: Boolean,
price_extra: Number,
excluded: { type: Boolean, optional: true },
@@ -92,9 +92,11 @@
<ul class="list-inline">
<li t-foreach="this.props.attribute_values" t-as="ptav" t-key="ptav.id"
class="list-inline-item me-1">
<t t-set="img_style" t-value="ptav.image ? 'background:url(/web/image/product.template.attribute.value/'+ptav.id+'/image); background-size:cover;' : ''"/>
<t t-set="color_style" t-value="ptav.is_custom ? '' : 'background-color:' + ptav.html_color"/>
<label
t-att-title="ptav.name"
t-attf-style="background-color:#{ptav.is_custom ? '' : ptav.html_color}"
t-attf-style="#{img_style or color_style}"
t-att-class="{'o_sale_product_configurator_ptav_color': true,
'active': this.props.selected_attribute_value_ids.includes(ptav.id),
'custom_value': ptav.is_custom,
+23 -10
View File
@@ -493,6 +493,27 @@
<template id="website_sale.products_categories_top" active="True" name="Categories in top-nav"/>
<template id="website_sale.products_attributes_top" active="False" name="Attributes in top-nav"/>
<template id="o_wsale_offcanvas_color_attribute" name="Color type attribute in filter">
<t t-foreach="a.value_ids" t-as="v">
<t t-set="img_style"
t-value="'background:url(/web/image/product.attribute.value/%s/image); background-size:cover;' % v.id if v.image else ''"
/>
<t t-set="color_style"
t-value="'background: ' + str(v.html_color or v.name if not v.is_custom else '')"
/>
<label t-attf-style="#{img_style or color_style}"
t-attf-class="css_attribute_color mb-1 #{'active' if v.id in attrib_set else ''}"
>
<input type="checkbox"
name="attrib"
t-att-value="'%s-%s' % (a.id, v.id)"
t-att-checked="'checked' if v.id in attrib_set else None"
t-att-title="v.name"
/>
</label>
</t>
</template>
<!-- OffCanvas Nav -->
<template id="website_sale.o_wsale_offcanvas" name="Offcanvas">
<aside id="o_wsale_offcanvas"
@@ -645,11 +666,7 @@
</div>
</div>
<div t-if="a.display_type == 'color'" class="pt-1 pb-3">
<t t-foreach="a.value_ids" t-as="v">
<label t-attf-style="background-color:#{v.html_color or v.name}" t-attf-class="css_attribute_color mb-1 #{'active' if v.id in attrib_set else ''}">
<input type="checkbox" name="attrib" t-att-value="'%s-%s' % (a.id,v.id)" t-att-checked="'checked' if v.id in attrib_set else None" t-att-title="v.name" />
</label>
</t>
<t t-call="website_sale.o_wsale_offcanvas_color_attribute"/>
</div>
</div>
</div>
@@ -870,11 +887,7 @@
</div>
</t>
<div t-if="a.display_type == 'color'" class="mb-3">
<t t-foreach="a.value_ids" t-as="v">
<label t-attf-style="background-color:#{v.html_color or v.name}" t-attf-class="css_attribute_color mb-1 #{'active' if v.id in attrib_set else ''}">
<input type="checkbox" name="attrib" t-att-value="'%s-%s' % (a.id,v.id)" t-att-checked="'checked' if v.id in attrib_set else None" t-att-title="v.name" />
</label>
</t>
<t t-call="website_sale.o_wsale_offcanvas_color_attribute"/>
</div>
</div>
</div>
@@ -95,9 +95,16 @@
<t t-elif="ptal.attribute_id.display_type == 'color'">
<ul t-att-data-attribute_id="ptal.attribute_id.id" t-attf-class="list-inline o_wsale_product_attribute #{'d-none' if single_and_custom else ''}">
<li t-foreach="ptal.product_template_value_ids._only_active()" t-as="ptav" class="list-inline-item me-1">
<label t-attf-style="background-color:#{ptav.html_color or ptav.product_attribute_value_id.name if not ptav.is_custom else ''}"
t-attf-class="css_attribute_color #{'active' if ptav in combination else ''} #{'custom_value' if ptav.is_custom else ''} #{'transparent' if (not ptav.is_custom and not ptav.html_color) else ''}">
<input type="radio"
<t t-set="img_style"
t-value="'background:url(/web/image/product.template.attribute.value/%s/image); background-size:cover;' % ptav.id if ptav.image else ''"
/>
<t t-set="color_style"
t-value="'background:' + str(ptav.html_color or ptav.name if not ptav.is_custom else '')"
/>
<label t-attf-style="#{img_style or color_style}"
t-attf-class="css_attribute_color #{'active' if ptav in combination else ''} #{'custom_value' if ptav.is_custom else ''} #{'transparent' if (not ptav.is_custom and not ptav.html_color) else ''}"
>
<input type="radio"
t-attf-class="js_variant_change #{ptal.attribute_id.create_variant}"
t-att-checked="ptav in combination"
t-att-name="'ptal-%s' % ptal.id"