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; } } }