[FIX] website_sale: update when changing quantity
There is several issue happening when changing quantity:
1) if website_sale_stock is installed, the displayed price is not
updated when changing quantities.
2) if the "Add to Cart" price is not updated, it erroneously displays
the price multiplied by the quantity.
3) when prices are updated, it only update attribute_value_ids.
4) when variant are displayed as list view, visually the last list item
is selected instead of the first, also changing quantity doesn't
update price as it should be given pricelist.
The first issue is caused by invalid JSON in data-attribute_value_ids
attribute. The code modify the `.data('attribute_value_ids')` thinking
it will be permanent. But if the value was invalid JSON we always parse
it on the fly inside a temporary object so the object is never changed.
The second issue is because originally, the "data-attribute_value_ids"
inside "Add to Cart" was set wrongly (when rendering the view) because
get_attribute_value_ids got the website_price multiplied by quantity.
The third issue was even with website_sale_stock is not installed
`.data('attribute_value_ids')` was only updated for one of the possible
multiple ones in the page.
With this change, we had json.dumps to ensure data-attribute_value_ids
is good JSON, and we correct errors so it is fixed for instance only
updating sources without updating website_sale and website_sale_options
modules.
note: when views are updated, this also solves a bigger issue when
python2 is used (because attribute_value_ids contained u'text' strings.
master equivalent of #23392 without js crutches for xml not updated
opw-1816479
closes #23467
This commit is contained in:
@@ -151,7 +151,7 @@ class WebsiteSale(http.Controller):
|
||||
else:
|
||||
price = variant.website_public_price / quantity
|
||||
visible_attribute_ids = [v.id for v in variant.attribute_value_ids if v.attribute_id.id in visible_attrs_ids]
|
||||
attribute_value_ids.append([variant.id, visible_attribute_ids, variant.website_price, price])
|
||||
attribute_value_ids.append([variant.id, visible_attribute_ids, variant.website_price / quantity, price])
|
||||
return attribute_value_ids
|
||||
|
||||
def _get_search_order(self, post):
|
||||
|
||||
@@ -545,6 +545,7 @@ Weight: 31 grams</field>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
<field name="product_tmpl_id" ref="product.product_product_4_product_template"/>
|
||||
<field name="price_discount">20</field>
|
||||
<field name="min_quantity">2</field>
|
||||
<field name="compute_price">formula</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -88,18 +88,12 @@ odoo.define('website_sale.website_sale', function (require) {
|
||||
|
||||
$(oe_website_sale).on("change", 'input[name="add_qty"]', function (event) {
|
||||
var product_ids = [];
|
||||
var product_dom = $(".js_product .js_add_cart_variants[data-attribute_value_ids]");
|
||||
var product_dom = $(event.target).closest(".js_product").find(".js_add_cart_variants");
|
||||
var qty = $(event.target).closest('form').find('input[name="add_qty"]').val();
|
||||
if (!product_dom.length) {
|
||||
return;
|
||||
}
|
||||
var attribute_value_ids = product_dom.data("attribute_value_ids");
|
||||
if(_.isString(attribute_value_ids)) {
|
||||
attribute_value_ids = JSON.parse(attribute_value_ids.replace(/'/g, '"'));
|
||||
}
|
||||
_.each(attribute_value_ids, function(entry) {
|
||||
product_ids.push(entry[0]);});
|
||||
var qty = $(event.target).closest('form').find('input[name="add_qty"]').val();
|
||||
product_ids.push(entry[0]);
|
||||
});
|
||||
|
||||
if ($("#product_detail").length) {
|
||||
// display the reduction from the pricelist in function of the quantity
|
||||
@@ -109,7 +103,7 @@ odoo.define('website_sale.website_sale', function (require) {
|
||||
for(var j=0; j < current.length; j++){
|
||||
current[j][2] = data[current[j][0]];
|
||||
}
|
||||
product_dom.attr("data-attribute_value_ids", JSON.stringify(current)).trigger("change");
|
||||
product_dom.trigger("change");
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -210,8 +204,7 @@ odoo.define('website_sale.website_sale', function (require) {
|
||||
$('input[name="'+$input.attr("name")+'"]').add($input).filter(function () {
|
||||
var $prod = $(this).closest('*:has(input[name="product_id"])');
|
||||
return !$prod.length || +$prod.find('input[name="product_id"]').val() === product_id;
|
||||
}).val(new_qty);
|
||||
$input.change();
|
||||
}).val(new_qty).change();
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -298,27 +291,14 @@ odoo.define('website_sale.website_sale', function (require) {
|
||||
}
|
||||
}
|
||||
|
||||
$(oe_website_sale).on('change', 'input.js_product_change', function () {
|
||||
var self = this;
|
||||
var $parent = $(this).closest('.js_product');
|
||||
$.when(base.ready()).then(function() {
|
||||
$parent.find(".oe_default_price:first .oe_currency_value").html( price_to_str(+$(self).data('lst_price')) );
|
||||
$parent.find(".oe_price:first .oe_currency_value").html(price_to_str(+$(self).data('price')) );
|
||||
});
|
||||
update_product_image(this, +$(this).val());
|
||||
});
|
||||
|
||||
$(oe_website_sale).on('change', 'input.js_variant_change, select.js_variant_change, ul[data-attribute_value_ids]', function (ev) {
|
||||
var $ul = $(ev.target).closest('.js_add_cart_variants');
|
||||
var $parent = $ul.closest('.js_product');
|
||||
$(oe_website_sale).on('change', 'input.js_variant_change, select.js_variant_change, input.js_product_change, [data-attribute_value_ids]', function (ev) {
|
||||
var $parent = $(ev.target).closest('.js_product');
|
||||
var $ul = $parent.find('.js_add_cart_variants');
|
||||
var $product_id = $parent.find('.product_id').first();
|
||||
var $price = $parent.find(".oe_price:first .oe_currency_value");
|
||||
var $default_price = $parent.find(".oe_default_price:first .oe_currency_value");
|
||||
var $optional_price = $parent.find(".oe_optional:first .oe_currency_value");
|
||||
var variant_ids = $ul.data("attribute_value_ids");
|
||||
if(_.isString(variant_ids)) {
|
||||
variant_ids = JSON.parse(variant_ids.replace(/'/g, '"'));
|
||||
}
|
||||
var values = [];
|
||||
var unchanged_values = $parent.find('div.oe_unchanged_value_ids').data('unchanged_value_ids') || [];
|
||||
|
||||
@@ -326,12 +306,14 @@ odoo.define('website_sale.website_sale', function (require) {
|
||||
values.push(+$(this).val());
|
||||
});
|
||||
values = values.concat(unchanged_values);
|
||||
var list_variant_id = parseInt($parent.find('input.js_product_change:checked').val());
|
||||
|
||||
$parent.find("label").removeClass("text-muted css_not_available");
|
||||
|
||||
var product_id = false;
|
||||
for (var k in variant_ids) {
|
||||
if (_.isEmpty(_.difference(variant_ids[k][1], values))) {
|
||||
if (_.isEmpty(_.difference(variant_ids[k][1], values)) ||
|
||||
variant_ids[k][0] === list_variant_id) {
|
||||
$.when(base.ready()).then(function() {
|
||||
$price.html(price_to_str(variant_ids[k][2]));
|
||||
$default_price.html(price_to_str(variant_ids[k][3]));
|
||||
|
||||
@@ -458,8 +458,7 @@
|
||||
<div class="col-sm-5 col-lg-4 col-lg-offset-1" id="product_details">
|
||||
<h1 itemprop="name" t-field="product.name">Product Name</h1>
|
||||
<span itemprop="url" style="display:none;" t-esc="'%sshop/product/%s' % (request.httprequest.url_root, slug(product))"/>
|
||||
<t t-set="attribute_value_ids" t-value="get_attribute_value_ids(product)"/>
|
||||
<form action="/shop/cart/update" class="js_add_cart_variants" t-att-data-attribute_value_ids="attribute_value_ids" method="POST">
|
||||
<form action="/shop/cart/update" method="POST">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()" />
|
||||
<div class="js_product" t-if="product.product_variant_ids">
|
||||
<t t-placeholder="select">
|
||||
@@ -600,14 +599,16 @@
|
||||
|
||||
<template id="product_variants" inherit_id="website_sale.product" active="False" customize_show="True" name="List View of Variants">
|
||||
<xpath expr="//t[@t-placeholder='select']" position="replace">
|
||||
<t t-set="attribute_value_ids" t-value="get_attribute_value_ids(product)"/>
|
||||
<ul class="hidden js_add_cart_variants" t-att-data-attribute_value_ids="json.dumps(attribute_value_ids)"/>
|
||||
<input type="hidden" t-if="len(product.product_variant_ids) == 1" class="product_id" name="product_id" t-att-value="product.product_variant_id.id"/>
|
||||
<t t-if="len(product.product_variant_ids) > 1">
|
||||
<label label-default="label-default" class="radio" t-foreach="product.product_variant_ids" t-as="variant_id">
|
||||
<input type="radio" name="product_id" class="js_product_change" t-att-checked="'checked' if variant_id_index == 0 else ''" t-att-value="variant_id.id" t-att-data-lst_price="compute_currency(variant_id.lst_price)" t-att-data-price="variant_id.price"/>
|
||||
<input type="radio" name="product_id" class="js_product_change" t-att-checked="'checked' if variant_id_index == 0 else None" t-att-value="variant_id.id" t-att-data-lst_price="compute_currency(variant_id.lst_price)" t-att-data-price="variant_id.price"/>
|
||||
<span t-esc="variant_id.name_get()[0][1]"/>
|
||||
<t t-set="diff_price" t-value="variant_id.price - variant_id.product_tmpl_id.price"/>
|
||||
<span class="badge" t-if="diff_price != 0">
|
||||
<t t-esc="diff_price > 0 and '+' or '-'"/><span t-esc="abs(diff_price)" t-esc-options='{"widget": "monetary", "display_currency": "website.currency_id"}'/>
|
||||
<t t-esc="diff_price > 0 and '+' or '-'"/><span t-esc="abs(diff_price)" t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"/>
|
||||
</span>
|
||||
</label>
|
||||
</t>
|
||||
@@ -616,7 +617,7 @@
|
||||
|
||||
<template id="variants">
|
||||
<t t-set="attribute_value_ids" t-value="get_attribute_value_ids(product)"/>
|
||||
<ul t-attf-class="list-unstyled js_add_cart_variants #{ul_class}" t-att-data-attribute_value_ids="attribute_value_ids">
|
||||
<ul t-attf-class="list-unstyled js_add_cart_variants #{ul_class}" t-att-data-attribute_value_ids="json.dumps(attribute_value_ids)">
|
||||
<t t-foreach="product.attribute_line_ids.sorted(key=lambda x: x.attribute_id.sequence)" t-as="variant_id">
|
||||
<li t-if="len(variant_id.value_ids) > 1">
|
||||
|
||||
|
||||
@@ -45,10 +45,103 @@ tour.register('shop_customize', {
|
||||
extra_trigger: 'body:not(:has(.oe_website_sale .oe_product_cart:eq(2)))',
|
||||
trigger: '.oe_product_cart a:contains("iPad")',
|
||||
},
|
||||
{
|
||||
content: "open customize menu",
|
||||
trigger: '#customize-menu > a',
|
||||
extra_trigger: "#product_detail",
|
||||
},
|
||||
{
|
||||
content: "click on 'List View of Variants'",
|
||||
trigger: "#customize-menu a:contains(List View of Variants)",
|
||||
},
|
||||
{
|
||||
content: "check page loaded after list of variant customization enabled",
|
||||
trigger: '.js_product_change',
|
||||
run: function () {}, // it's a check
|
||||
},
|
||||
{
|
||||
content: "check price is 750 and set quantity to 2",
|
||||
trigger: ".js_product:first input.quantity:propValue(1)",
|
||||
extra_trigger: ".product_price .oe_price .oe_currency_value:containsExact(750.00)",
|
||||
run: "text 2",
|
||||
},
|
||||
{
|
||||
content: "verify pricelist based on quantity has effect",
|
||||
trigger: ".product_price .oe_price .oe_currency_value:containsExact(600.00)",
|
||||
run: function () {}, // it's a check
|
||||
},
|
||||
{
|
||||
content: "check pricelit has been applied and switch to 32 GB variant",
|
||||
trigger: ".js_product label:contains('32 GB')",
|
||||
},
|
||||
{
|
||||
content: "verify that price has changed when changing variant",
|
||||
trigger: ".product_price .oe_price .oe_currency_value:not(:containsExact(600.00))",
|
||||
run: function () {}, // it's a check
|
||||
},
|
||||
{
|
||||
content: "open customize menu",
|
||||
trigger: '#customize-menu > a',
|
||||
},
|
||||
{
|
||||
content: "remove 'List View of Variants'",
|
||||
trigger: "#customize-menu a:contains(List View of Variants):has(input:checked)",
|
||||
},
|
||||
{
|
||||
content: "check page loaded after list of variant customization disabled",
|
||||
trigger: ".js_product:not(:has(.js_product_change))",
|
||||
run: function () {}, // it's a check
|
||||
},
|
||||
{
|
||||
content: "check price is 750 and set quantity to 2",
|
||||
trigger: ".js_product:first input.quantity:propValue(1)",
|
||||
extra_trigger: ".product_price .oe_price .oe_currency_value:containsExact(750.00)",
|
||||
run: "text 2",
|
||||
},
|
||||
{
|
||||
content: "verify pricelist based on quantity has effect",
|
||||
trigger: ".product_price .oe_price .oe_currency_value:containsExact(600.00)",
|
||||
run: function () {}, // it's a check
|
||||
},
|
||||
{
|
||||
content: "switch to 32 GB variant",
|
||||
trigger: ".js_product label:contains('32 GB')",
|
||||
},
|
||||
{
|
||||
content: "verify that price has changed when changing variant",
|
||||
trigger: ".product_price .oe_price .oe_currency_value:not(:containsExact(600.00))",
|
||||
run: function () {}, // it's a check
|
||||
},
|
||||
{
|
||||
content: "switch back to 16 GB variant",
|
||||
trigger: ".js_product label:contains('16 GB')",
|
||||
},
|
||||
{
|
||||
content: "click on 'Add to Cart' button",
|
||||
trigger: "a:contains(Add to Cart)",
|
||||
},
|
||||
{
|
||||
content: "price is lowered by pricelist and not multiplied by quantity",
|
||||
trigger: "#product_confirmation .oe_price .oe_currency_value:containsExact(600.00)",
|
||||
extra_trigger: "#product_confirmation input.quantity:propValue(2)",
|
||||
run: function () {}, // it's a check
|
||||
},
|
||||
{
|
||||
content: "set quantity to 1",
|
||||
trigger: "#product_confirmation .js_add_cart_json .fa-minus",
|
||||
},
|
||||
{
|
||||
content: "check that product page has been updated",
|
||||
trigger: ".js_product:first input.quantity:propValue(1)",
|
||||
extra_trigger: ".product_price .oe_price .oe_currency_value:containsExact(750.00)",
|
||||
run: function () {}, // it's a check
|
||||
},
|
||||
{
|
||||
content: "check that add to cart modal has been updated",
|
||||
trigger: "#product_confirmation .oe_price .oe_currency_value:containsExact(750.00)",
|
||||
extra_trigger: "#product_confirmation input.quantity:propValue(1)",
|
||||
run: function () {}, // it's a check
|
||||
},
|
||||
{
|
||||
content: "add an optional Warranty",
|
||||
trigger: ".js_product:contains(Warranty) a:contains(Add to Cart)",
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-center td-price" name="price">
|
||||
<ul class="hidden js_add_cart_variants" t-att-data-attribute_value_ids="main_product_attr_ids"></ul>
|
||||
<ul class="hidden js_add_cart_variants" t-att-data-attribute_value_ids="json.dumps(main_product_attr_ids)"></ul>
|
||||
<div class="hidden oe_unchanged_value_ids" t-att-data-unchanged_value_ids="main_product_attr_ids[0][1]" ></div>
|
||||
<span t-attf-class="text-danger oe_default_price oe_striked_price {{ (compute_currency(product.lst_price) - product.website_price) < 0.01 and 'hidden' }}"
|
||||
t-field="product.lst_price"
|
||||
|
||||
@@ -39,15 +39,18 @@ $('.oe_website_sale').each(function() {
|
||||
$(oe_website_sale).on('change', 'ul[data-attribute_value_ids]', function(event) {
|
||||
var $ul = $(event.target).closest('.js_add_cart_variants');
|
||||
var $parent = $ul.closest('.js_product');
|
||||
var variant_ids = JSON.parse($ul.data("attribute_value_ids").replace(/'/g, '"'));
|
||||
var variant_ids = $ul.data("attribute_value_ids");
|
||||
var values = [];
|
||||
$parent.find('input.js_variant_change:checked, select.js_variant_change').each(function() {
|
||||
values.push(+$(this).val());
|
||||
});
|
||||
var list_variant_id = parseInt($parent.find('input.js_product_change:checked').val());
|
||||
var qty = $parent.find('input[name="add_qty"]').val();
|
||||
for (var k in variant_ids) {
|
||||
if (_.isEmpty(_.difference(variant_ids[k][1], values))) {
|
||||
var info = variant_ids[k][4];
|
||||
if (_.isEmpty(_.difference(variant_ids[k][1], values)) ||
|
||||
variant_ids[k][0] === list_variant_id) {
|
||||
// clone so permanent object is not modified
|
||||
var info = _.clone(variant_ids[k][4]);
|
||||
if(_.contains(['always', 'threshold'], info['inventory_availability'])) {
|
||||
info['virtual_available'] -= parseInt(info['cart_qty']);
|
||||
if (info['virtual_available'] < 0) {
|
||||
|
||||
Reference in New Issue
Block a user