From 269f8c5601b350e4939f296a3148abe65632b0ba Mon Sep 17 00:00:00 2001 From: Romain Derie Date: Wed, 16 May 2018 15:07:39 +0200 Subject: [PATCH 1/2] [FIX] website_sale_coupon: merge 'sibling' discount lines in cart A discount on products with different taxes will result in as many discount lines as there is different taxes. This commit will merge all these lines in a single one on the front-end (cart) to not change the behavior/display for the end user. This will be purely visual as it does not affect SO lines. task-1866977 Related to #2224 --- models/sale_order.py | 38 +++++++++++++ static/src/js/website_sale_coupon.test.js | 69 +++++++++++++++++++---- 2 files changed, 96 insertions(+), 11 deletions(-) diff --git a/models/sale_order.py b/models/sale_order.py index a009b938c3d..e96e81a92f8 100644 --- a/models/sale_order.py +++ b/models/sale_order.py @@ -6,6 +6,44 @@ from odoo.http import request class SaleOrder(models.Model): _inherit = "sale.order" + @api.one + def _compute_website_order_line(self): + """ This method will merge multiple discount lines generated by a same program + into a single one (temporary line with `new()`). + This case will only occur when the program is a discount applied on multiple + products with different taxes. + In this case, each taxes will have their own discount line. This is required + to have correct amount of taxes according to the discount. + But we wan't these lines to be `visually` merged into a single one in the + e-commerce since the end user should only see one discount line. + This is only possible since we don't show taxes in cart. + eg: + line 1: 10% discount on product with tax `A` - $15 + line 2: 10% discount on product with tax `B` - $11.5 + line 3: 10% discount on product with tax `C` - $10 + would be `hidden` and `replaced` by + line 1: 10% discount - $36.5 + + Note: The line will be created without tax(es) and the amount will be computed + depending if B2B or B2C is enabled. + """ + super(SaleOrder, self)._compute_website_order_line() + for program in self._get_applied_programs_with_rewards_on_current_order(): + program_lines = self.order_line.filtered(lambda line: line.product_id == program.discount_line_product_id) + if len(program_lines) > 1: + price_unit = sum(l.price_subtotal if self.env.user.has_group('sale.group_show_price_subtotal') else l.price_total for l in program_lines) + self.website_order_line += self.env['sale.order.line'].new({ + 'product_id': program_lines[0].product_id.id, + 'price_unit': price_unit, + 'name': program_lines[0].name, + 'product_uom_qty': 1, + 'product_uom': program_lines[0].product_uom.id, + 'order_id': self.id, + 'is_reward_line': True, + }) + self.website_order_line -= program_lines + + def _compute_cart_info(self): super(SaleOrder, self)._compute_cart_info() for order in self: diff --git a/static/src/js/website_sale_coupon.test.js b/static/src/js/website_sale_coupon.test.js index 60bafbee212..deb5ba81527 100644 --- a/static/src/js/website_sale_coupon.test.js +++ b/static/src/js/website_sale_coupon.test.js @@ -36,9 +36,9 @@ tour.register('shop_sale_coupon', { trigger: '.oe_product_cart a:contains("iPad Mini")', }, { - content: "add 3 iPad Mini into cart", + content: "add 2 iPad Mini into cart", trigger: '#product_details input[name="add_qty"]', - run: "text 3", + run: "text 2", }, { content: "click on 'Add to Cart' button", @@ -68,31 +68,79 @@ tour.register('shop_sale_coupon', { trigger: '.reward_product:contains("10.0 % discount on total amount")', run: function () {}, // it's a check }, - /* 2. Add some iPad to get a free one, play with quantity */ + /* 2. Add an Ice Cream in the cart (which has different tax than iPad) and ensure only one discount line is shown */ + { + content: "go to shop", + trigger: '.reward_product:contains("10.0 % discount on total amount")', + run: function () { + location.href = '/shop' + }, + }, + { + content: "type Ice Cream in search", + trigger: 'form input[name="search"]', + run: "text Ice Cream", + }, + { + content: "start search", + trigger: 'form:has(input[name="search"]) .oe_search_button', + }, + { + content: "select Ice Cream", + extra_trigger: '.oe_search_found', // Wait to be on search results or it sometimes throws concurent error (sent search form + click on product on /shop) + trigger: '.oe_product_cart a:containsExact("Ice Cream")', + }, + { + content: "click on 'Add to Cart' button", + trigger: "a:contains(Add to Cart)", + }, + { + content: "check reduction amount got recomputed and merged both discount lines into one only", + extra_trigger: '.oe_currency_value:contains("‑74.00")', + trigger: '.oe_website_sale .oe_cart', + run: function () {}, // it's a check + }, + /* 3. Add some iPad to get a free one, play with quantity */ + { + content: "add one iPad", + trigger: '#cart_products input.js_quantity', + run: "text 3", + }, + { + content: "check reduction amount got recomputed when changing qty", + trigger: '.oe_currency_value:contains("‑106.00")', + run: function () {}, // it's a check + }, { content: "add more iPad Mini into cart", trigger: '#cart_products input.js_quantity', run: "text 4", }, { - content: "check reduction amount got recomputed", - extra_trigger: '.oe_currency_value:contains("96.00")', // For some reason, "-96.00" won't work - trigger: '.reward_product:contains("Free Product - iPad Mini")', + content: "check free product is added", + trigger: '#wrap:has(.reward_product:contains("Free Product - iPad Mini"))', run: function () {}, // it's a check }, { content: "remove one iPad from cart", - trigger: '#cart_products a.js_add_cart_json:first', + trigger: '#cart_products input.js_quantity[value="4"]', + run: "text 3", }, { content: "check free product is removed", trigger: '#wrap:not(:has(.reward_product:contains("Free Product - iPad Mini")))', run: function () {}, // it's a check }, - /* 3. Empty cart and disable coupon */ + /* 4. Empty cart and disable coupon */ { content: "remove iPad Mini from cart", - trigger: '#cart_products input.js_quantity:first', + trigger: '#cart_products input.js_quantity[value="3"]', + run: "text 0", + }, + { + content: "remove Ice Cream from cart", + extra_trigger: '#wrap:not(:has(.td-product_name:contains("iPad Mini")))', // wait iPad got removed + trigger: '#cart_products input.js_quantity[value="1"]', run: "text 0", }, { @@ -100,7 +148,7 @@ tour.register('shop_sale_coupon', { trigger: '#wrap:not(:has(#cart_products))', run: function () {}, // it's a check }, - /* 4. Disabled customize options coupon box & 'Show # found'*/ + /* 5. Disabled customize options coupon box & 'Show # found'*/ { content: "open customize menu", extra_trigger: '.oe_website_sale .oe_cart', @@ -135,7 +183,6 @@ if ('website_sale_options.website_sale' in odoo.__DEBUG__.services) { content: "click in modal on 'Proceed to checkout' button", trigger: 'a:contains("Proceed to checkout")', }); - break; } } } From 2f392ec561fe85826757e61cf3adc2911a2c24f4 Mon Sep 17 00:00:00 2001 From: Romain Derie Date: Mon, 10 Sep 2018 18:18:46 +0200 Subject: [PATCH 2/2] [FIX] (website_)sale_coupon: `_compute_tax_id` with tax on reward line It should get tax from the SOL and not the SO's product as the tax is applied on the SOL only (the reward product has no tax by default). --- static/src/js/website_sale_coupon.test.js | 88 ++++++++++++----------- 1 file changed, 47 insertions(+), 41 deletions(-) diff --git a/static/src/js/website_sale_coupon.test.js b/static/src/js/website_sale_coupon.test.js index deb5ba81527..68348f7c6ed 100644 --- a/static/src/js/website_sale_coupon.test.js +++ b/static/src/js/website_sale_coupon.test.js @@ -4,6 +4,7 @@ odoo.define('website_sale_coupon.test', function (require) { require("website_sale.tour"); var tour = require("web_tour.tour"); var base = require("web_editor.base"); +var ajax = require('web.ajax'); tour.register('shop_sale_coupon', { test: true, @@ -19,6 +20,11 @@ tour.register('shop_sale_coupon', { { content: "click on 'Show # found'", trigger: "#customize-menu a:contains(Show # found)", + run: function () { + if (!$('#customize-menu a:contains(Show # found) input').prop('checked')) { + $('#customize-menu a:contains(Show # found)').click(); + } + }, }, /* 1. Buy 1 iPad Mini, enable coupon code & insert 10% code */ { @@ -52,6 +58,11 @@ tour.register('shop_sale_coupon', { { content: "click on 'Coupon Code'", trigger: "#customize-menu a:contains(Coupon Code)", + run: function () { + if (!$('#customize-menu a:contains(Coupon Code) input').prop('checked')) { + $('#customize-menu a:contains(Coupon Code)').click(); + } + }, }, { content: "insert promo code '10pc'", @@ -68,27 +79,47 @@ tour.register('shop_sale_coupon', { trigger: '.reward_product:contains("10.0 % discount on total amount")', run: function () {}, // it's a check }, - /* 2. Add an Ice Cream in the cart (which has different tax than iPad) and ensure only one discount line is shown */ + /* 2. Add a product with tax in the cart (which has different tax than iPad) and ensure only one discount line is shown */ { content: "go to shop", trigger: '.reward_product:contains("10.0 % discount on total amount")', run: function () { - location.href = '/shop' + ajax.jsonRpc('/web/dataset/call', 'call', { + model: 'account.tax', + method: 'create', + args: [{ + 'name':'15% tax incl', + 'amount': 15, + }], + }).then(function (tax_id) { + ajax.jsonRpc('/web/dataset/call', 'call', { + model: 'product.template', + method: 'create', + args: [{ + 'name': 'Taxed Product', + 'taxes_id': [([6, false, [tax_id]])], + 'list_price': 100, + 'website_published': true, + }], + }).then(function (data) { + location.href = '/shop'; + }); + }); }, }, { - content: "type Ice Cream in search", + content: "type Taxed Product in search", trigger: 'form input[name="search"]', - run: "text Ice Cream", + run: "text Taxed Product", }, { content: "start search", trigger: 'form:has(input[name="search"]) .oe_search_button', }, { - content: "select Ice Cream", + content: "select Taxed Product", extra_trigger: '.oe_search_found', // Wait to be on search results or it sometimes throws concurent error (sent search form + click on product on /shop) - trigger: '.oe_product_cart a:containsExact("Ice Cream")', + trigger: '.oe_product_cart a:containsExact("Taxed Product")', }, { content: "click on 'Add to Cart' button", @@ -96,7 +127,7 @@ tour.register('shop_sale_coupon', { }, { content: "check reduction amount got recomputed and merged both discount lines into one only", - extra_trigger: '.oe_currency_value:contains("‑74.00")', + extra_trigger: '.oe_currency_value:contains("-74.00"):not(#cart_total .oe_currency_value:contains("-74.00"))', trigger: '.oe_website_sale .oe_cart', run: function () {}, // it's a check }, @@ -108,7 +139,7 @@ tour.register('shop_sale_coupon', { }, { content: "check reduction amount got recomputed when changing qty", - trigger: '.oe_currency_value:contains("‑106.00")', + trigger: '.oe_currency_value:contains("-106.00")', run: function () {}, // it's a check }, { @@ -131,46 +162,21 @@ tour.register('shop_sale_coupon', { trigger: '#wrap:not(:has(.reward_product:contains("Free Product - iPad Mini")))', run: function () {}, // it's a check }, - /* 4. Empty cart and disable coupon */ + /* 4. Check /shop/payment does not break the `merged discount lines split per tax` (eg: with _compute_tax_id) */ { - content: "remove iPad Mini from cart", - trigger: '#cart_products input.js_quantity[value="3"]', - run: "text 0", + content: "go to checkout", + trigger: 'a[href="/shop/checkout"]', }, { - content: "remove Ice Cream from cart", - extra_trigger: '#wrap:not(:has(.td-product_name:contains("iPad Mini")))', // wait iPad got removed - trigger: '#cart_products input.js_quantity[value="1"]', - run: "text 0", + content: "Confirm checkout", + extra_trigger: "div.all_shipping .panel", + trigger: 'a[href="/shop/confirm_order"]', }, { - content: "check cart is empty", - trigger: '#wrap:not(:has(#cart_products))', + content: "check total is unchanged", + trigger: '.oe_currency_value:contains("967.50")', run: function () {}, // it's a check }, - /* 5. Disabled customize options coupon box & 'Show # found'*/ - { - content: "open customize menu", - extra_trigger: '.oe_website_sale .oe_cart', - trigger: '#customize-menu > a', - }, - { - content: "click on 'Coupon Code'", - trigger: "#customize-menu a:contains(Coupon Code)", - }, - { - content: "click on 'Continue Shopping'", - trigger: "a:contains(Continue Shopping)", - }, - { - content: "open customize menu", - extra_trigger: '.oe_website_sale #products_grid', - trigger: '#customize-menu > a', - }, - { - content: "click on 'Show # found'", - trigger: "#customize-menu a:contains(Show # found)", - }, ] );