From c83cac4bfe1707493414c13183484f6dbd7d6741 Mon Sep 17 00:00:00 2001 From: pgu-odoo Date: Tue, 20 Dec 2016 14:40:45 +0100 Subject: [PATCH 01/58] [ADD] website_sale_coupon: Allow to use coupons in ecommerce --- __init__.py | 5 ++++ __manifest__.py | 13 ++++++++++ controllers/__init__.py | 4 +++ controllers/main.py | 11 ++++++++ models/__init__.py | 4 +++ models/sale_order.py | 12 +++++++++ static/src/js/sale_coupon.js | 36 ++++++++++++++++++++++++++ views/website_sale_templates.xml | 44 ++++++++++++++++++++++++++++++++ 8 files changed, 129 insertions(+) create mode 100644 __init__.py create mode 100644 __manifest__.py create mode 100644 controllers/__init__.py create mode 100644 controllers/main.py create mode 100644 models/__init__.py create mode 100644 models/sale_order.py create mode 100644 static/src/js/sale_coupon.js create mode 100644 views/website_sale_templates.xml diff --git a/__init__.py b/__init__.py new file mode 100644 index 00000000000..ac0011716f6 --- /dev/null +++ b/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import controllers +import models diff --git a/__manifest__.py b/__manifest__.py new file mode 100644 index 00000000000..4e5f2e17110 --- /dev/null +++ b/__manifest__.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. +{ + 'name': "Website Sale Coupon", + 'summary': """Allows to use discount coupons in ecommerce orders""", + 'description': """Integrate coupons mechanism in ecommerce.""", + 'category': 'Website', + 'version': '1.0', + 'depends': ['website_sale', 'sale_coupon'], + 'data': [ + 'views/website_sale_templates.xml', + ], +} diff --git a/controllers/__init__.py b/controllers/__init__.py new file mode 100644 index 00000000000..f48716d3a69 --- /dev/null +++ b/controllers/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import main diff --git a/controllers/main.py b/controllers/main.py new file mode 100644 index 00000000000..90ab72a014e --- /dev/null +++ b/controllers/main.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +from odoo import http +from odoo.http import request + +class Website_coupon(http.Controller): + + @http.route(['/shop/apply_coupon'], type='json', auth="public", website=True) + def shop_apply_coupon(self, promo_code, **post): + order = request.website.sale_get_order() + coupon_status = request.env['sale.coupon.apply.code'].apply_coupon(order, promo_code) + return coupon_status diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 00000000000..76bcb3512a1 --- /dev/null +++ b/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import sale_order diff --git a/models/sale_order.py b/models/sale_order.py new file mode 100644 index 00000000000..7bc53960d0a --- /dev/null +++ b/models/sale_order.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +from odoo import models, fields, api + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + def _compute_cart_info(self): + super(SaleOrder, self)._compute_cart_info() + for order in self: + reward_lines = order.website_order_line.filtered(lambda line: line.is_reward_line) + order.cart_quantity -= int(sum(reward_lines.mapped('product_uom_qty'))) diff --git a/static/src/js/sale_coupon.js b/static/src/js/sale_coupon.js new file mode 100644 index 00000000000..7bfdc42707c --- /dev/null +++ b/static/src/js/sale_coupon.js @@ -0,0 +1,36 @@ +odoo.define('website_sale_coupon', function (require) { +"use strict"; + +var ajax = require('web.ajax'); +var core = require('web.core'); +var _t = core._t; + +$(document).ready(function () { + $('body').on('click', '.check_coupon', function (ev) { + var promocode = $("div.coupon_box").find("input[name='promo_code']").val(); + ajax.jsonRpc('/shop/apply_coupon', 'call', {'promo_code': promocode}) + .then(function(data) { + if (data['error']){ + if ($("div.coupon_box").parent().find(".nocoupon_alert").length == 0){ + $("div.coupon_box").append( + '
'+ + ''+ + data['error']+ + '
'); + } + } + if (data['generated_coupon']){ + if ($("div.coupon_box").parent().find(".nocoupon_alert").length == 0){ + $("div.coupon_box").append( + '
'+ + ''+ + _.str.sprintf(_t("Your reward %s is available on a next order with this coupon code: %s"), data['generated_coupon']['reward'], data['generated_coupon']['code'])+ + '
'); + } + } + location.reload(); + }); + }); + +}); +}); diff --git a/views/website_sale_templates.xml b/views/website_sale_templates.xml new file mode 100644 index 00000000000..b61df4faa11 --- /dev/null +++ b/views/website_sale_templates.xml @@ -0,0 +1,44 @@ + + + - From f71c33230a70202a85ad1068ce1f722d482345ed Mon Sep 17 00:00:00 2001 From: Romain Derie Date: Thu, 22 Mar 2018 17:03:06 +0100 Subject: [PATCH 31/58] [FIX] website_sale_coupon: change tour trigger since class remove The `oe_website_sale` class got removed by commit https://github.com/odoo/odoo/commit/809776914613b28814b24419b4a9ba40d40f7b9b The tour that needed that class need to be changed to trigger on something else --- static/src/js/website_sale_coupon.test.js | 26 +++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/static/src/js/website_sale_coupon.test.js b/static/src/js/website_sale_coupon.test.js index 1ce783c54c0..60bafbee212 100644 --- a/static/src/js/website_sale_coupon.test.js +++ b/static/src/js/website_sale_coupon.test.js @@ -11,6 +11,15 @@ tour.register('shop_sale_coupon', { wait_for: base.ready() }, [ + /* 0. Test 'Show # found' customize option works correctly */ + { + content: "open customize menu", + trigger: '#customize-menu > a', + }, + { + content: "click on 'Show # found'", + trigger: "#customize-menu a:contains(Show # found)", + }, /* 1. Buy 1 iPad Mini, enable coupon code & insert 10% code */ { content: "type iPad Mini in search", @@ -23,7 +32,7 @@ tour.register('shop_sale_coupon', { }, { content: "select iPad Mini", - extra_trigger: '.oe_website_sale_search', // Wait to be on search results or it sometimes throws concurent error (sent search form + click on product on /shop) + 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:contains("iPad Mini")', }, { @@ -91,7 +100,7 @@ tour.register('shop_sale_coupon', { trigger: '#wrap:not(:has(#cart_products))', run: function () {}, // it's a check }, - /* 4. Disabled coupon box */ + /* 4. Disabled customize options coupon box & 'Show # found'*/ { content: "open customize menu", extra_trigger: '.oe_website_sale .oe_cart', @@ -101,6 +110,19 @@ tour.register('shop_sale_coupon', { 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)", + }, ] ); From 73d2c5b5b6a829c2867d81723ad2bb265fd486f0 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Wed, 20 Jun 2018 14:00:06 +0200 Subject: [PATCH 32/58] [IMP] product: Unify/Clean product demo data PURPOSE ======= 1. Unified product demo data 2. Less demo: one per use case 3. Demo data for all models Specification ============= 1. Refactor all the brol in the demo data 2. Adapt the tests to make them green, as some products are renamed, removed, created in python instead of as demo data ... --- static/src/js/website_sale_coupon.test.js | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/static/src/js/website_sale_coupon.test.js b/static/src/js/website_sale_coupon.test.js index 7fb6d1a9a13..8171dbfedc1 100644 --- a/static/src/js/website_sale_coupon.test.js +++ b/static/src/js/website_sale_coupon.test.js @@ -20,23 +20,23 @@ tour.register('shop_sale_coupon', { content: "click on 'Show # found'", trigger: "#customize-menu a:contains(Show # found)", }, - /* 1. Buy 1 iPad Mini, enable coupon code & insert 10% code */ + /* 1. Buy 1 Large Cabinet, enable coupon code & insert 10% code */ { - content: "type iPad Mini in search", + content: "type Large Cabinet in search", trigger: 'form input[name="search"]', - run: "text iPad Mini", + run: "text Large Cabinet", }, { content: "start search", trigger: 'form:has(input[name="search"]) .oe_search_button', }, { - content: "select iPad Mini", + content: "select Large Cabinet", 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:contains("iPad Mini")', + trigger: '.oe_product_cart a:contains("Large Cabinet")', }, { - content: "add 3 iPad Mini into cart", + content: "add 3 Large Cabinet into cart", trigger: '#product_details input[name="add_qty"]', run: "text 3", }, @@ -72,30 +72,30 @@ 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 some cabinet to get a free one, play with quantity */ { - content: "add more iPad Mini into cart", + content: "add more Large Cabinet 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")', + trigger: '.reward_product:contains("Free Product - Large Cabinet")', run: function () {}, // it's a check }, { - content: "remove one iPad from cart", + content: "remove one cabinet from cart", trigger: '#cart_products a.js_add_cart_json:first', }, { content: "check free product is removed", - trigger: '#wrap:not(:has(.reward_product:contains("Free Product - iPad Mini")))', + trigger: '#wrap:not(:has(.reward_product:contains("Free Product - Large Cabinet")))', run: function () {}, // it's a check }, /* 3. Empty cart and disable coupon */ { - content: "remove iPad Mini from cart", + content: "remove Large Cabinet from cart", trigger: '#cart_products input.js_quantity:first', run: "text 0", }, From c186a1b0967b9fff71ebd6802fcda62ede606f08 Mon Sep 17 00:00:00 2001 From: Yannick Tivisse Date: Wed, 11 Jul 2018 11:37:40 +0200 Subject: [PATCH 33/58] [IMP] product,...: Improve demo data * Update demo data sheet for barcode * Modify product "Book: Office Renovation for Dummies: - Name: "eBook: Office Renovation for Dummies" - Image: - Internal category: All/Services/Saleable * Tips internal reference * Product 10.0 % Discount = - Name : "10.0% discount on total amount" (no space between 0 and %) - Internal reference : 10PERCENTDISC * Free product - large cabinet - Should be a stockable - in category All/Saleable/Office Furniture - Name : Free Gift - Pencil Holder - Picture * Desk Customizable : - Change its website sequence (should be displayed first in the shop) * https://github.com/odoo/odoo/pull/25263 * Need to clean attributes and attribute values and attribute categories : - Attributes, keep : Brand, Color, Dimensions, Legs, Weight - Attribute Category : Rename Storage into "Material" * Events - Suppress Functional Webinar and Technical Training * PoS Categories : - Suppress - Create PoS Category : Desks (put desks in it) - Create PoS Category : Chairs (put chairs in it) - Create PoS Category : Miscellaneous (and put all other products in it) * Products available in PoS - Remove : Air Flight, Car Travel Expense, Bpost domestic bpack 24h pro, Bpost World Express Pro, Customer Care, DHL USA Delivery, DHL USA -> International, Deposit, Expenses, Fdex International, Fedexs US, Hotel Accomodation, Restaurant Expenses, * Product: https://drive.google.com/a/odoo.com/file/d/1KjxcB88aDfe8qAzIpFlkBmMj5ekA9eIf/view?usp=drivesdk Timesheet : description to update https://docs.google.com/spreadsheets/d/1nB8l8pFcRXRFnudgDJVyZZp3McaHNovlyvY0jE1ZZ54/edit?usp=sharing * Project : less projects, more tasks Remove "Internal - GAP Analysis", "Internal Projects" Rename Support into After-Sales Services Task to rename: Data import + Doc ==> Office planning Move the task Internal Training from "Support" to "Office Design" * AGR Project : add the following tasks and link it to the right SO line Decoration : SO Line ==> Senior Architect Planning : SO Line ==> Senior Architect Furniture SO Line ==> Senior Architect Furniture Delivery : not linked to an SO line * Marketing Automation Partnership offer ==> to rename into "Commercial prospection" Steps to rename: Offer free trial ==> Offer free catalog After 7 days (if not yet partners) ==> After 7 days (if not yet customers) Message for sale person ==> keep the same * Mass mailings: http://nimb.ws/zrKsZ9 http://nimb.ws/CbNpwd * Events: Suppress Functional Webinar and Technical Training Tracks to update : https://docs.google.com/spreadsheets/d/1sKkJrltXY3Gz7ue3mVysZxaWGybKpx5fIhgUBnhbNDs/edit?usp=sharing * Helpdesk Rename team into Customer Care Create 2 tickets in default team Urgent : Kitchen collapsing (assigned to admin) : link to the task SO053: [SERV_585189] Customer Care (Prepaid Hours) No pririoty : Where can I download a catalog ? (not assigned). Ticket type = question --- static/src/js/website_sale_coupon.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/src/js/website_sale_coupon.test.js b/static/src/js/website_sale_coupon.test.js index 8171dbfedc1..2e7c1316bb5 100644 --- a/static/src/js/website_sale_coupon.test.js +++ b/static/src/js/website_sale_coupon.test.js @@ -69,7 +69,7 @@ tour.register('shop_sale_coupon', { }, { content: "check reward product", - trigger: '.reward_product:contains("10.0 % discount on total amount")', + trigger: '.reward_product:contains("10.0% discount on total amount")', run: function () {}, // it's a check }, /* 2. Add some cabinet to get a free one, play with quantity */ From adb84f760ba70cf749cdddf4590d28501978de1d Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Wed, 16 May 2018 17:03:40 +0200 Subject: [PATCH 34/58] [REF] *: BS4, review panel and well structure See https://github.com/odoo/odoo/commit/59237ea81988bdcd0cc746422a7c94bf2eadaaa2 --- views/website_sale_templates.xml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/views/website_sale_templates.xml b/views/website_sale_templates.xml index 05d1090a77a..2f6756e28d5 100644 --- a/views/website_sale_templates.xml +++ b/views/website_sale_templates.xml @@ -25,39 +25,39 @@ -
-
+
+
You have successfully applied following promo code:
-
-
+
+
You have successfully applied following promo code:
-
-
+
+
Your reward is available on a next order with this promo code:
-
-
+
+
Invalid or expired promo code
-
-
+
+
From 1f68e93e4b2c98385ff9a82d01eb4603e2885643 Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Fri, 25 May 2018 17:00:11 +0200 Subject: [PATCH 35/58] [REF] *: BS4, 'pull-left/right' -> 'float-left/right' See https://github.com/odoo/odoo/commit/d48b8593dde7c6adf679359e2dd64dee288e9544 --- views/website_sale_templates.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/views/website_sale_templates.xml b/views/website_sale_templates.xml index 2f6756e28d5..e1ac0a7fb26 100644 --- a/views/website_sale_templates.xml +++ b/views/website_sale_templates.xml @@ -27,7 +27,7 @@
- You have successfully applied following promo code: + You have successfully applied following promo code:
@@ -35,7 +35,7 @@
- You have successfully applied following promo code: + You have successfully applied following promo code:
@@ -43,7 +43,7 @@
- Your reward is available on a next order with this promo code: + Your reward is available on a next order with this promo code:
@@ -51,14 +51,14 @@
- Invalid or expired promo code + Invalid or expired promo code
- +
From 2187efc2aaef840f21e95baf7f46a4dbf6863a22 Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Thu, 2 Aug 2018 11:26:37 +0200 Subject: [PATCH 36/58] [FIX] website_sale_coupon: adapt to https://github.com/odoo/odoo/commit/b1107cba38e296f17d03d9919be67e6789a407be --- static/src/js/website_sale_coupon.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/src/js/website_sale_coupon.test.js b/static/src/js/website_sale_coupon.test.js index 2e7c1316bb5..6195c9db5cd 100644 --- a/static/src/js/website_sale_coupon.test.js +++ b/static/src/js/website_sale_coupon.test.js @@ -55,7 +55,7 @@ tour.register('shop_sale_coupon', { }, { content: "click on 'I have a promo code'", - trigger: 'a.show_coupon', + trigger: '.show_coupon', }, { content: "insert promo code '10pc'", From dfb3bc933f383c09141018bbd23a9cf44519cf84 Mon Sep 17 00:00:00 2001 From: Priyanka Kakadiya Date: Fri, 16 Feb 2018 15:46:57 +0530 Subject: [PATCH 37/58] [REF] various: update manifests for better wording and names Time has come to update a bit applications name, summary and website links. Purpose is to have a website page correctly set for applications to be used in conjunction with the newly-introduced discover button. Names and summaries are updated to be more updated and shiny. This commit is related to task ID 47179 and 1861544. Related PR are #1834 and #2365. First one is about classic applications while second one is about website applications. Co-Authored-By: Nimesh Jethva --- __manifest__.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/__manifest__.py b/__manifest__.py index b468355f257..bec5b9dfa79 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -1,9 +1,13 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. { - 'name': "Website Sale Coupon", - 'summary': """Allows to use discount coupons in ecommerce orders""", - 'description': """Integrate coupons mechanism in ecommerce.""", + 'name': "Coupons & Promotions for eCommerce", + 'summary': """Use coupon & promotion programs in your eCommerce store""", + 'description': """ +Create coupon and promotion codes to share in order to boost your sales (free products, discounts, etc.). Shoppers can use them in the eCommerce checkout. + +Coupon & promotion programs can be edited in the Catalog menu of the Website app. + """, 'category': 'Website', 'version': '1.0', 'depends': ['website_sale', 'sale_coupon'], From f7ffbcd1c83b21cd214a36ecbc30b3c990da5d2a Mon Sep 17 00:00:00 2001 From: stefanorigano Date: Mon, 19 Feb 2018 10:57:37 +0100 Subject: [PATCH 38/58] [REF] *: improve app icons, add SVG version - Uniform colors and design - Replace duplicated icons (eg. mrp_maintenance / maintenance) - Improve misleading icons (eg. sale_subscription) - Add icons for new apps Add SVG versions to lossless future editing and print/marketing use. See https://github.com/odoo/odoo/commit/1d9208c4fa92d00090b37b8e5dbed48087a7f52f task-54681 --- static/description/icon.png | Bin 0 -> 8196 bytes static/description/icon.svg | 1 + 2 files changed, 1 insertion(+) create mode 100644 static/description/icon.png create mode 100644 static/description/icon.svg diff --git a/static/description/icon.png b/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f88909cc0de7b6797cba9f7a515dacdf2b2e7560 GIT binary patch literal 8196 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 \ No newline at end of file From 863b86ea9fefddcc72c76376af97a00211ccc0b4 Mon Sep 17 00:00:00 2001 From: Jeremy Kersten Date: Tue, 14 Aug 2018 11:34:40 +0200 Subject: [PATCH 39/58] [FIX] website_sale_coupon: make test determinist avoid race condition when refresh of /shop finish after the search click. Now we start on the search page directly, that make tour faster and that don't remove any usefull test, since the purpose of this test is to test coupon and not the search view already tested by others tests. --- static/src/js/website_sale_coupon.test.js | 25 +---------------------- 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/static/src/js/website_sale_coupon.test.js b/static/src/js/website_sale_coupon.test.js index 6195c9db5cd..88b3b885ced 100644 --- a/static/src/js/website_sale_coupon.test.js +++ b/static/src/js/website_sale_coupon.test.js @@ -7,32 +7,13 @@ var base = require("web_editor.base"); tour.register('shop_sale_coupon', { test: true, - url: '/shop', + url: '/shop?search=Large%20Cabinet', wait_for: base.ready() }, [ - /* 0. Test 'Show # found' customize option works correctly */ - { - content: "open customize menu", - trigger: '#customize-menu > a', - }, - { - content: "click on 'Show # found'", - trigger: "#customize-menu a:contains(Show # found)", - }, /* 1. Buy 1 Large Cabinet, enable coupon code & insert 10% code */ - { - content: "type Large Cabinet in search", - trigger: 'form input[name="search"]', - run: "text Large Cabinet", - }, - { - content: "start search", - trigger: 'form:has(input[name="search"]) .oe_search_button', - }, { content: "select Large Cabinet", - 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:contains("Large Cabinet")', }, { @@ -123,10 +104,6 @@ tour.register('shop_sale_coupon', { extra_trigger: '.oe_website_sale #products_grid', trigger: '#customize-menu > a', }, - { - content: "click on 'Show # found'", - trigger: "#customize-menu a:contains(Show # found)", - }, ] ); From 53e6684a8692c95a4823a269135e75ca6fc35efa Mon Sep 17 00:00:00 2001 From: Jeremy Kersten Date: Tue, 14 Aug 2018 15:45:38 +0200 Subject: [PATCH 40/58] [TYPO] website_sale_coupon: quick fix While we find why PhantomJs crash on this tour that works for humans --- tests/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/__init__.py b/tests/__init__.py index 9f48e6f73be..3f0b039decb 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1 +1 @@ -from . import test_shop_sale_coupon +# from . import test_shop_sale_coupon From adc41c0ed2580ac5b8f066c2761b218a44f3c45b Mon Sep 17 00:00:00 2001 From: Romain Derie Date: Tue, 14 Aug 2018 17:29:41 +0200 Subject: [PATCH 41/58] [FIX] webite_sale_coupon: adapt test with new checkout Since commit 006ce9c88763053de9003b2a197a89258d534865 set the `Promo Code` view active (customize_show), the `shop_sale_coupon` JS tour should not be working anymore. But it was working since this change introduced a race condition that was luckily making the test to succeed (most of the time). (This bug appeared weeks ago on nightly build) Basically, the tour flow was: 1. Activating `Promo Code` in customize option -> Cause a page refresh 2. Waiting for the DOM to contains the promo code input (shown after page refresh performed by step 1.) 3. Typing a promo code and submitting it 4. Waiting for the discount line to appear (after page load performed by step 3 form submit.) This test was working fine as the promo code customize option was disabled. Thus, the promo code input would only be visible after the refresh of step 1. It would always wait for the refresh to occur before continuing. No race condition possible. Since the change of 006ce9c887 that activated that view by default: - The promo code input would already be on the DOM when the tour was executed - Clicking on the customize option would actually disable it. Then, since the input was already in the DOM, the step 2 would instantly continue without waiting for the refresh of step 1 and step 3 would be performed while step 1 would still be refreshing the page. You would then land sometimes on the refresh of step 1, sometimes on the refresh of step 3, depending of the test speed (and so the runbot). In case you landed on refresh of step 1, step 4 would never find the discount line. ---- SOLUTION ---- Click on the customize option only if not already enabled. (The customize step could have simply been removed as it is now activated by default. But this is better to prevent the test to fail in case we change that customize active state in the future or to simply run the test more than once) --- static/src/js/website_sale_coupon.test.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/static/src/js/website_sale_coupon.test.js b/static/src/js/website_sale_coupon.test.js index 88b3b885ced..61573c728a8 100644 --- a/static/src/js/website_sale_coupon.test.js +++ b/static/src/js/website_sale_coupon.test.js @@ -31,11 +31,17 @@ tour.register('shop_sale_coupon', { trigger: '#customize-menu > a', }, { - content: "click on 'Promo Code'", + content: "enable 'Promo Code' if needed", trigger: "#customize-menu a:contains(Promo Code)", + run: function () { + if (!$('#customize-menu a:contains(Promo Code) input').prop('checked')) { + $('#customize-menu a:contains(Promo Code)').click(); + } + } }, { content: "click on 'I have a promo code'", + extra_trigger: '.show_coupon', trigger: '.show_coupon', }, { @@ -99,11 +105,6 @@ tour.register('shop_sale_coupon', { 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', - }, ] ); From fe72773b20c20db2d2502cdb44f6d68c71511b5c Mon Sep 17 00:00:00 2001 From: Romain Derie Date: Thu, 30 Aug 2018 18:12:24 +0200 Subject: [PATCH 42/58] [IMP] sale_coupon: adapt to multi-websites This commit adds the `website.multi.mixin` to coupon program to make it multi-websites. It also adds the website_id field in the views and adapt SQL constraint on code which now became multi-websites friendly. --- tests/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/__init__.py b/tests/__init__.py index 3f0b039decb..9f48e6f73be 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1 +1 @@ -# from . import test_shop_sale_coupon +from . import test_shop_sale_coupon From 269f8c5601b350e4939f296a3148abe65632b0ba Mon Sep 17 00:00:00 2001 From: Romain Derie Date: Wed, 16 May 2018 15:07:39 +0200 Subject: [PATCH 43/58] [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 44/58] [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)", - }, ] ); From 8cefc928b1fc1c3fcf30ce1460fc3ebf3378456e Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Fri, 14 Sep 2018 12:43:46 +0200 Subject: [PATCH 45/58] [FIX] *: use responsive BS text utilities instead of our own * website_form_editor, website_sale_coupon See https://github.com/odoo/odoo/commit/737d272997da341922f7d720c4a7c9f01c041a59 --- views/website_sale_templates.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/website_sale_templates.xml b/views/website_sale_templates.xml index e1ac0a7fb26..c1674f82905 100644 --- a/views/website_sale_templates.xml +++ b/views/website_sale_templates.xml @@ -70,7 +70,7 @@ Discount: - + + + sale.coupon.program.common.form + sale.coupon.program + + + + + + + + + sale.coupon.program.tree + sale.coupon.program + + + + + + + + \ No newline at end of file From b2318ced509a4a17bb4f714dd5fa24eb56a13264 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Warnon?= Date: Tue, 25 Sep 2018 18:54:07 +0200 Subject: [PATCH 47/58] [IMP] product,(website_)sale: Allow users to fully configure products Purpose ======= - Configure a product from a sales order as easily as in the ecommerce The backend uses the same template as the frontend to configure a products and its options - Display optionnal products of optionnal products in the "sale options" section - Allow adding exlusions to some combinations of the product and/or to some combinations of the optionnal and accessory products - Product attributes can now have custom values. These values are specified by the user during the SO creation. They are also available on the webshop frontend. - Product attributes can be configured to create variants "on demand". This means that product.templates having these attributes will only create product.product variant when that combination is actually selected in a sale order/in the web shop. Product attribute values are also improved to handle extra prices and exclusions in all three modes ("never", "dynamic, "always") Specification ============= 1. Improve the "Add to cart" with optional products - When you add an option to cart, move the product to the cart & show the options of this option - Don't open the option wizard if no option available (in case this module becomes generic, it's better to skip this step when it's not needed) - Display the options right under their related product in the cart 2. Do not allow to add the product as an optional product for itself Why ? - No functional sense - Creates issues in the add to cart wizard 3. Exclude some attribute combinations within the product or with related options and accessory products - Rename VARIANT PRICES button -> CONFIGURE VARIANTS - When you click this button and select a value, open form view rather than inline edition - Form view of variant values: - Attribute - Value - HTML Color Index - Attribute Price Extra - Not Compatible with: o2m tab with 2 fields: Product AND Attribute Value (of this product) [m2m tag selection] - in any new line, autocomplete the current product by default - Tooltip: A list of product and attribute values that you want to exclude for this product's attribue value. Also applies on optionnal and accessory products. 4. New option to configure a product in a sales order line - This option is only visible if the corresponding option is activated in the sales settings - It opens a wizard to select a product template, configure related attribute values and select optional products (based on frontend view) - Attributes not compatible with other selected attributes should not be selectable - Must work like in the frontend 5. Create variants on demand - add a new mode to generate variants as they are added to the order/cart (needed for big # of combinations managed in the inventory and/or produced in mrp with specific boms) - replace the boolean by a selection radio tooltip for this new field: Always: Variants are generated straight away when the attribute values are combined in the product configuration. Only when the product is added to a sales order: Variants are generated once the combination is selected in the sales order. This is advised when variants are customized on demand and manufactured as such in Odoo. Never: Advised when variants are not needed in Odoo. The selected values just show up in the description of sales order lines. - ability to define attribute extra prices & exclude combinations in all variant modes - if at least one attribute of the product in set to "on demand" mode, generate all the variants "on demand" --- models/sale_order.py | 4 ++-- static/src/js/website_sale_coupon.test.js | 19 +++++-------------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/models/sale_order.py b/models/sale_order.py index a009b938c3d..2b1bbc050ea 100644 --- a/models/sale_order.py +++ b/models/sale_order.py @@ -19,7 +19,7 @@ class SaleOrder(models.Model): return error @api.multi - def _cart_update(self, product_id=None, line_id=None, add_qty=0, set_qty=0, attributes=None, **kwargs): - res = super(SaleOrder, self)._cart_update(product_id=product_id, line_id=line_id, add_qty=add_qty, set_qty=set_qty, attributes=attributes, **kwargs) + def _cart_update(self, product_id=None, line_id=None, add_qty=0, set_qty=0, **kwargs): + res = super(SaleOrder, self)._cart_update(product_id=product_id, line_id=line_id, add_qty=add_qty, set_qty=set_qty, **kwargs) self.recompute_coupon_lines() return res diff --git a/static/src/js/website_sale_coupon.test.js b/static/src/js/website_sale_coupon.test.js index 61573c728a8..45696dc4233 100644 --- a/static/src/js/website_sale_coupon.test.js +++ b/static/src/js/website_sale_coupon.test.js @@ -91,6 +91,11 @@ tour.register('shop_sale_coupon', { trigger: '#wrap:not(:has(#cart_products))', run: function () {}, // it's a check }, + { + content: "check cart is empty (2)", + trigger: 'div.js_cart_lines:contains(Your cart is empty!)', + run: function () {}, // it's a check + }, /* 4. Disabled customize options coupon box & 'Show # found'*/ { content: "open customize menu", @@ -107,18 +112,4 @@ tour.register('shop_sale_coupon', { }, ] ); - -//Check if website_sale_options is installed since its adding an extra step to add to cart -if ('website_sale_options.website_sale' in odoo.__DEBUG__.services) { - var steps = tour.tours.shop_sale_coupon.steps; - for (var k=0; k Date: Fri, 28 Sep 2018 15:11:19 +0200 Subject: [PATCH 48/58] [FIX] website_sale_coupon: adapt test tour to new checkout flow --- static/src/js/website_sale_coupon.test.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/static/src/js/website_sale_coupon.test.js b/static/src/js/website_sale_coupon.test.js index e2b9c3cfcbf..89637c82df3 100644 --- a/static/src/js/website_sale_coupon.test.js +++ b/static/src/js/website_sale_coupon.test.js @@ -169,16 +169,11 @@ tour.register('shop_sale_coupon', { /* 4. Check /shop/payment does not break the `merged discount lines split per tax` (eg: with _compute_tax_id) */ { content: "go to checkout", - trigger: 'a[href="/shop/checkout"]', - }, - { - content: "Confirm checkout", - extra_trigger: "div.all_shipping .panel", - trigger: 'a[href="/shop/confirm_order"]', + trigger: 'a[href="/shop/checkout?express=1"]', }, { content: "check total is unchanged", - trigger: '.oe_currency_value:contains("967.50")', + trigger: 'tr#order_total .oe_currency_value:contains("967.50")', run: function () {}, // it's a check }, ] From 5a589e8c0abda256f6e34f07aff8dc307cb1d76e Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Tue, 2 Oct 2018 16:36:23 +0200 Subject: [PATCH 49/58] [FIX] website_sale_coupon: force option activation to avoid race condition --- tests/test_shop_sale_coupon.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/test_shop_sale_coupon.py b/tests/test_shop_sale_coupon.py index 982cbd5de8b..5f3a128ee2b 100644 --- a/tests/test_shop_sale_coupon.py +++ b/tests/test_shop_sale_coupon.py @@ -2,10 +2,18 @@ import odoo.tests + class TestUi(odoo.tests.HttpCase): post_install = True at_install = False def test_01_admin_shop_sale_coupon_tour(self): - self.phantom_js("/", "odoo.__DEBUG__.services['web_tour.tour'].run('shop_sale_coupon')", "odoo.__DEBUG__.services['web_tour.tour'].tours.shop_sale_coupon.ready", login="admin") + # pre enable "Show # found" option to avoid race condition... + self.env.ref("website_sale.search count").write({"active": True}) + self.phantom_js( + "/", + "odoo.__DEBUG__.services['web_tour.tour'].run('shop_sale_coupon')", + "odoo.__DEBUG__.services['web_tour.tour'].tours.shop_sale_coupon.ready", + login="admin", + ) From 76e7a7082bf70d8adf91b339d857061ab0871d6f Mon Sep 17 00:00:00 2001 From: "Lucas Perais (lpe)" Date: Wed, 12 Sep 2018 08:50:00 +0000 Subject: [PATCH 50/58] [FIX] website_sale_coupon: update freeshipping on delivery change Since this module is not bridged to website_sale_delivery, there was no support for changing the freeshipping line when changing the delivery carrier Now it has, at the price of ugly code OPW 1871526 closes odoo/enterprise#2651 --- controllers/main.py | 24 ++++++++++++++++++++++++ models/sale_order.py | 8 ++++++++ views/website_sale_templates.xml | 6 ++++++ 3 files changed, 38 insertions(+) diff --git a/controllers/main.py b/controllers/main.py index 0def9e81217..0e0c87e3d2f 100644 --- a/controllers/main.py +++ b/controllers/main.py @@ -27,3 +27,27 @@ class WebsiteSale(WebsiteSale): order = request.website.sale_get_order() order.recompute_coupon_lines() return super(WebsiteSale, self).cart(**post) + + def _update_website_sale_coupon(self, **post): + order = request.website.sale_get_order() + order.recompute_coupon_lines() + free_shipping_lines = order._get_free_shipping_lines() + currency = order.currency_id + result = {} + if free_shipping_lines: + amount_free_shipping = sum(free_shipping_lines.mapped('price_subtotal')) + result.update({ + 'new_amount_delivery': self._format_amount(0.0, currency), + 'new_amount_untaxed': self._format_amount(order.amount_untaxed, currency), + 'new_amount_tax': self._format_amount(order.amount_tax, currency), + 'new_amount_total': self._format_amount(order.amount_total, currency), + 'new_amount_order_discounted': self._format_amount(order.reward_amount - amount_free_shipping, currency) + }) + return result + + # Override + # Add in the rendering the free_shipping_line + def _get_shop_payment_values(self, order, **kwargs): + values = super(WebsiteSale, self)._get_shop_payment_values(order, **kwargs) + values['free_shipping_lines'] = order._get_free_shipping_lines() + return values diff --git a/models/sale_order.py b/models/sale_order.py index e96e81a92f8..0fa6efb784f 100644 --- a/models/sale_order.py +++ b/models/sale_order.py @@ -61,3 +61,11 @@ class SaleOrder(models.Model): res = super(SaleOrder, self)._cart_update(product_id=product_id, line_id=line_id, add_qty=add_qty, set_qty=set_qty, attributes=attributes, **kwargs) self.recompute_coupon_lines() return res + + def _get_free_shipping_lines(self): + self.ensure_one() + free_shipping_prgs_ids = self.no_code_promo_program_ids.filtered(lambda p: p.reward_type == 'free_shipping') + if not free_shipping_prgs_ids: + return self.env['sale.order.line'] + free_shipping_product_ids = free_shipping_prgs_ids.mapped('discount_line_product_id') + return self.order_line.filtered(lambda l: l.product_id in free_shipping_product_ids) diff --git a/views/website_sale_templates.xml b/views/website_sale_templates.xml index 922751ecff5..392914e3c73 100644 --- a/views/website_sale_templates.xml +++ b/views/website_sale_templates.xml @@ -79,4 +79,10 @@
+ + From ef7b24eb758995bbd8bca01c7a835952d47893ff Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Tue, 16 Oct 2018 19:02:17 +0200 Subject: [PATCH 51/58] [FIX] wbesite_sale_coupon: inherit to correct view Adaptation of a0a811861332e34fb3ee0bcdedccfe7057685a5d to new version. --- views/website_sale_templates.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/website_sale_templates.xml b/views/website_sale_templates.xml index f0f62e1e514..978eb86d285 100644 --- a/views/website_sale_templates.xml +++ b/views/website_sale_templates.xml @@ -80,7 +80,7 @@ -