summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Lempereur <nle@odoo.com>2018-02-28 16:11:42 +0100
committerNicolas Lempereur <nle@odoo.com>2018-03-05 13:10:11 +0100
commita86200597ff3e0a8fbbf3b9272f7a3b80b1f5bfe (patch)
treedadacff1db260db7048a2a8d21f5dd2049079626
parent3807e71801490873c1885cd71e27ac89b6d254c8 (diff)
[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
-rw-r--r--addons/website_sale/controllers/main.py2
-rw-r--r--addons/website_sale/data/demo.xml1
-rw-r--r--addons/website_sale/static/src/js/website_sale.js40
-rw-r--r--addons/website_sale/views/templates.xml11
-rw-r--r--addons/website_sale_options/static/src/js/website_sale.test.js93
-rw-r--r--addons/website_sale_options/views/website_sale_options_templates.xml2
-rw-r--r--addons/website_sale_stock/static/src/js/website_sale_stock.js9
7 files changed, 119 insertions, 39 deletions
diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py
index d3a44363fab..3cf961ada09 100644
--- a/addons/website_sale/controllers/main.py
+++ b/addons/website_sale/controllers/main.py
@@ -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):
diff --git a/addons/website_sale/data/demo.xml b/addons/website_sale/data/demo.xml
index 80401d1095c..d195702f5ad 100644
--- a/addons/website_sale/data/demo.xml
+++ b/addons/website_sale/data/demo.xml
@@ -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>
diff --git a/addons/website_sale/static/src/js/website_sale.js b/addons/website_sale/static/src/js/website_sale.js
index 874b9f698b5..1544f199088 100644
--- a/addons/website_sale/static/src/js/website_sale.js
+++ b/addons/website_sale/static/src/js/website_sale.js
@@ -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]));
diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml
index cae42a8416d..4325d669fda 100644
--- a/addons/website_sale/views/templates.xml
+++ b/addons/website_sale/views/templates.xml
@@ -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) &gt; 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">
diff --git a/addons/website_sale_options/static/src/js/website_sale.test.js b/addons/website_sale_options/static/src/js/website_sale.test.js
index 2f60f2f0902..3b4b6970f9d 100644
--- a/addons/website_sale_options/static/src/js/website_sale.test.js
+++ b/addons/website_sale_options/static/src/js/website_sale.test.js
@@ -46,10 +46,103 @@ tour.register('shop_customize', {
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)",
},
diff --git a/addons/website_sale_options/views/website_sale_options_templates.xml b/addons/website_sale_options/views/website_sale_options_templates.xml
index fdbd0efc349..6fb938231f1 100644
--- a/addons/website_sale_options/views/website_sale_options_templates.xml
+++ b/addons/website_sale_options/views/website_sale_options_templates.xml
@@ -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) &lt; 0.01 and 'hidden' }}"
t-field="product.lst_price"
diff --git a/addons/website_sale_stock/static/src/js/website_sale_stock.js b/addons/website_sale_stock/static/src/js/website_sale_stock.js
index 2a3a7f8cc6f..134d8c45ad7 100644
--- a/addons/website_sale_stock/static/src/js/website_sale_stock.js
+++ b/addons/website_sale_stock/static/src/js/website_sale_stock.js
@@ -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) {