diff options
| author | Nicolas Lempereur <nle@odoo.com> | 2018-02-28 16:11:42 +0100 |
|---|---|---|
| committer | Nicolas Lempereur <nle@odoo.com> | 2018-03-05 13:10:11 +0100 |
| commit | a86200597ff3e0a8fbbf3b9272f7a3b80b1f5bfe (patch) | |
| tree | dadacff1db260db7048a2a8d21f5dd2049079626 | |
| parent | 3807e71801490873c1885cd71e27ac89b6d254c8 (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
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) > 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) < 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) { |
