From d013b32d90e334d78013b9010cec30eab2e34caa Mon Sep 17 00:00:00 2001 From: roen-odoo Date: Fri, 3 Feb 2023 14:28:37 +0000 Subject: [PATCH] [FIX] pos_loyalty: free product correctly added to order with loyalty Current behavior: When you have a loyalty program with a free product, and a rule with a minimum amout. The free product is not added to the order if the free product is already in the order. Steps to reproduce: -Set a loyalty program with the following Conditions: Minimum Qty: 1 Minimum Spend: $5 Grant: 10 Points/Order -Reward: Type: Free Product Item: Any item (eg: Bacon Burger) Exchange: 30 Points 1. Open POS that has this loyalty program. 2. Process sales with a Test User until user has 30 points. 3. Attempt to redeem the Free Product under "Reward". Note: I modified the other test, IMO it makes sense that when triggering the rewards it adds Product A and Product A * -1. Because if the product that was already in the order become free, the rule to trigger the rewards is not met anymore. opw-3135794 closes odoo/odoo#113313 X-original-commit: 253d308a26f1e6479c1d3c5342e1cede6f635198 Signed-off-by: Trinh Jacky (trj) Signed-off-by: Engels Robin (roen) --- addons/pos_loyalty/static/src/js/Loyalty.js | 4 +- .../src/tours/PosLoyaltyLoyaltyProgramTour.js | 2 +- .../src/tours/PosLoyaltyRewardButtonTour.js | 14 ++++++ addons/pos_loyalty/tests/test_frontend.py | 50 +++++++++++++++++++ 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/addons/pos_loyalty/static/src/js/Loyalty.js b/addons/pos_loyalty/static/src/js/Loyalty.js index 05d5f125d8b..2a714d411e8 100644 --- a/addons/pos_loyalty/static/src/js/Loyalty.js +++ b/addons/pos_loyalty/static/src/js/Loyalty.js @@ -1046,9 +1046,10 @@ patch(Order.prototype, "pos_loyalty.Order", { if (reward.reward_type === "discount" && totalIsZero) { continue; } + let unclaimedQty; if (reward.reward_type === "product" && !reward.multi_product) { const product = this.pos.db.get_product_by_id(reward.reward_product_ids[0]); - const unclaimedQty = this._computeUnclaimedFreeProductQty( + unclaimedQty = this._computeUnclaimedFreeProductQty( reward, couponProgram.coupon_id, product, @@ -1061,6 +1062,7 @@ patch(Order.prototype, "pos_loyalty.Order", { result.push({ coupon_id: couponProgram.coupon_id, reward: reward, + potentialQty: unclaimedQty, }); } } diff --git a/addons/pos_loyalty/static/src/tours/PosLoyaltyLoyaltyProgramTour.js b/addons/pos_loyalty/static/src/tours/PosLoyaltyLoyaltyProgramTour.js index b3f22fa45b6..5e9baa68337 100644 --- a/addons/pos_loyalty/static/src/tours/PosLoyaltyLoyaltyProgramTour.js +++ b/addons/pos_loyalty/static/src/tours/PosLoyaltyLoyaltyProgramTour.js @@ -136,7 +136,7 @@ ProductScreen.do.clickPartnerButton(); // This deselects the customer. PosLoyalty.do.unselectPartner(); PosLoyalty.check.customerIs("Customer"); -PosLoyalty.check.orderTotalIs("3.20"); +PosLoyalty.check.orderTotalIs("6.40"); PosLoyalty.exec.finalizeOrder("Cash", "10"); registry.category("web_tour.tours").add("PosLoyaltyLoyaltyProgram2", { test: true, url: "/pos/web", steps: getSteps() }); diff --git a/addons/pos_loyalty/static/src/tours/PosLoyaltyRewardButtonTour.js b/addons/pos_loyalty/static/src/tours/PosLoyaltyRewardButtonTour.js index b0f331e67db..75fcdc4bdea 100644 --- a/addons/pos_loyalty/static/src/tours/PosLoyaltyRewardButtonTour.js +++ b/addons/pos_loyalty/static/src/tours/PosLoyaltyRewardButtonTour.js @@ -128,3 +128,17 @@ PosLoyalty.check.orderTotalIs("4.81"); PosLoyalty.exec.finalizeOrder("Cash", "10"); registry.category("web_tour.tours").add("PosLoyaltyFreeProductTour", { test: true, url: "/pos/web", steps: getSteps() }); +startSteps(); + +ProductScreen.do.confirmOpeningPopup(); +ProductScreen.do.clickHomeCategory(); + +ProductScreen.do.clickPartnerButton(); +ProductScreen.do.clickCustomer('AAA Partner'); +ProductScreen.exec.addOrderline('Test Product A', '1'); +PosLoyalty.check.isRewardButtonHighlighted(true); +PosLoyalty.do.clickRewardButton(); +PosLoyalty.check.hasRewardLine('Free Product - Test Product A', '-11.50', '1.00'); +PosLoyalty.check.isRewardButtonHighlighted(false); + +registry.category("web_tour.tours").add("PosLoyaltyFreeProductTour2", { test: true, url: "/pos/web", steps: getSteps() }); diff --git a/addons/pos_loyalty/tests/test_frontend.py b/addons/pos_loyalty/tests/test_frontend.py index dd12eafb1b9..eef51614b1d 100644 --- a/addons/pos_loyalty/tests/test_frontend.py +++ b/addons/pos_loyalty/tests/test_frontend.py @@ -765,3 +765,53 @@ class TestUi(TestPointOfSaleHttpCommon): "PosCouponTour5", login="accountman", ) + + def test_loyalty_program_using_same_product(self): + """ + - Create a loyalty program giving free product A for 30 points + - Trigger the condition of the program using the same product A + """ + LoyaltyProgram = self.env['loyalty.program'] + (LoyaltyProgram.search([])).write({'pos_ok': False}) + self.product_a = self.env["product.product"].create({ + "name": "Test Product A", + "type": "product", + "list_price": 10, + "available_in_pos": True, + }) + + self.loyalty_program = self.env['loyalty.program'].create({ + 'name': 'Loyalty Program Test', + 'program_type': 'loyalty', + 'trigger': 'auto', + 'applies_on': 'both', + 'pos_ok': True, + 'pos_config_ids': [Command.link(self.main_pos_config.id)], + 'rule_ids': [(0, 0, { + 'reward_point_mode': 'order', + 'reward_point_amount': 10, + 'minimum_amount': 5, + 'minimum_qty': 1, + })], + 'reward_ids': [(0, 0, { + 'reward_type': 'product', + 'required_points': 30, + 'reward_product_id': self.product_a.id, + 'reward_product_qty': 1, + })], + }) + + partner_aaa = self.env['res.partner'].create({'name': 'AAA Partner'}) + self.env['loyalty.card'].create({ + 'partner_id': partner_aaa.id, + 'program_id': self.loyalty_program.id, + 'points': 30, + }) + + self.main_pos_config.open_ui() + + self.start_tour( + "/pos/web?config_id=%d" % self.main_pos_config.id, + "PosLoyaltyFreeProductTour2", + login="accountman", + )