From 895c7601d2c12abd52fb897e70f30d72ca8a24c1 Mon Sep 17 00:00:00 2001
From: roen-odoo <roen@odoo.com>
Date: Tue, 25 Apr 2023 15:00:05 +0000
Subject: [PATCH] [FIX] pos_sale: remove auto promotion from settled orders

Current behavior:
When you apply a promotion on a sale order, then settle it in a PoS
session, the promotion will be applied two times. One time from the sale
order, and one from the PoS.

Steps to reproduce:
- Create a promotion program with a discount of 50% with no code needed
- Add the promotion program to the PoS
- Create a sale order with a product of 200$ and apply the promotion
- The total price of the order should be 100$
- Settle the sale order in a PoS session
- The promotion is applied twice and the total price is 50$ instead of
  100$

opw-3247545

closes odoo/odoo#119673

Signed-off-by: Joseph Caburnay (jcb) <jcb@odoo.com>
---
 .../SaleOrderManagementScreen.js              |  3 +-
 .../static/tests/tours/pos_sale_tours.js      | 10 +++++
 addons/pos_sale/tests/test_pos_sale_flow.py   | 42 +++++++++++++++++++
 3 files changed, 54 insertions(+), 1 deletion(-)

diff --git a/addons/pos_sale/static/src/js/OrderManagementScreen/SaleOrderManagementScreen.js b/addons/pos_sale/static/src/js/OrderManagementScreen/SaleOrderManagementScreen.js
index 1b6af4b99b4b..f13542a2df59 100644
--- a/addons/pos_sale/static/src/js/OrderManagementScreen/SaleOrderManagementScreen.js
+++ b/addons/pos_sale/static/src/js/OrderManagementScreen/SaleOrderManagementScreen.js
@@ -293,7 +293,8 @@ odoo.define('pos_sale.SaleOrderManagementScreen', function (require) {
             sale_order[0].amount_unpaid = saleOrdersAmountUnpaid[sale_order[0].id];
 
             const sale_lines = await this._getSOLines(sale_order[0].order_line);
-            sale_order[0].order_line = sale_lines;
+            const promo_products_to_remove = this.env.pos.promo_programs.flatMap(program => program.promo_code_usage === 'no_code_needed' ? program.discount_line_product_id[0] : []);
+            sale_order[0].order_line = sale_lines.filter(line => !promo_products_to_remove.includes(line.product_id[0]));
 
             return sale_order[0];
         }
diff --git a/addons/pos_sale/static/tests/tours/pos_sale_tours.js b/addons/pos_sale/static/tests/tours/pos_sale_tours.js
index b410ff33065a..a6f3355812e3 100644
--- a/addons/pos_sale/static/tests/tours/pos_sale_tours.js
+++ b/addons/pos_sale/static/tests/tours/pos_sale_tours.js
@@ -42,4 +42,14 @@ odoo.define('pos_sale.tour', function (require) {
     ReceiptScreen.check.isShown();
 
     Tour.register('PosSettleOrder2', { test: true, url: '/pos/ui' }, getSteps());
+
+
+    startSteps();
+
+    ProductScreen.do.confirmOpeningPopup();
+    ProductScreen.do.clickQuotationButton();
+    ProductScreen.do.selectFirstOrder();
+    ProductScreen.check.totalAmountIs(100);
+
+    Tour.register('PosSettleOrderWithPromotions', { test: true, url: '/pos/ui' }, getSteps());
 });
diff --git a/addons/pos_sale/tests/test_pos_sale_flow.py b/addons/pos_sale/tests/test_pos_sale_flow.py
index 07561bd52928..8b2db558ad0d 100644
--- a/addons/pos_sale/tests/test_pos_sale_flow.py
+++ b/addons/pos_sale/tests/test_pos_sale_flow.py
@@ -119,3 +119,45 @@ class TestPoSSale(TestPointOfSaleHttpCommon):
         self.assertEqual(orderline_product_a.move_ids.product_uom_qty, 0)
         # 1 item to deliver for product b.
         self.assertEqual(orderline_product_b.move_ids.product_uom_qty, 1)
+
+    def test_settle_order_with_promotions(self):
+        self.promotion_program = self.env['coupon.program'].create({
+            'name': '50% on current order',
+            'program_type': 'promotion_program',
+            'promo_code_usage': 'no_code_needed',
+            'reward_type': 'discount',
+            'discount_type': 'percentage',
+            'discount_percentage': 50,
+            'discount_apply_on': 'on_order',
+        })
+
+        self.product = self.env['product.product'].create({
+            'name': 'Product',
+            'available_in_pos': True,
+            'type': 'product',
+            'lst_price': 200.0,
+            'taxes_id': False,
+        })
+
+        sale_order = self.env['sale.order'].create({
+            'partner_id': self.env.ref('base.res_partner_2').id,
+            'order_line': [(0, 0, {
+                'product_id': self.product.id,
+                'name': self.product.name,
+                'product_uom_qty': 1,
+                'product_uom': self.env.ref('uom.product_uom_unit').id,
+                'price_unit': self.product.lst_price,
+            })],
+        })
+
+        #validate the sale order
+        sale_order.recompute_coupon_lines()
+        sale_order.action_confirm()
+
+        #add the promo program to the pos config
+        self.main_pos_config.write({
+            'use_coupon_programs': True,
+            'promo_program_ids': [(6, 0, [self.promotion_program.id])],
+        })
+        self.main_pos_config.open_session_cb()
+        self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'PosSettleOrderWithPromotions', login="accountman")
-- 
GitLab