diff --git a/addons/sale_loyalty/models/sale_order.py b/addons/sale_loyalty/models/sale_order.py index 54fe6b81c0f..31655b9a6ab 100644 --- a/addons/sale_loyalty/models/sale_order.py +++ b/addons/sale_loyalty/models/sale_order.py @@ -229,6 +229,7 @@ class SaleOrder(models.Model): continue discount_lines[line.reward_identifier_code] |= line + order_lines -= self.order_line.filtered("reward_id") cheapest_line = False for lines in discount_lines.values(): line_reward = lines.reward_id @@ -252,12 +253,19 @@ class SaleOrder(models.Model): # Fixed prices are per tax discounted_amounts = {line.tax_id: abs(line.price_total) for line in lines} for line in itertools.chain(non_common_lines, common_lines): - discounted_amount = discounted_amounts[line.tax_id] + # For gift card and eWallet programs we have no tax but we can consume the amount completely + if lines.reward_id.program_id.is_payment_program: + discounted_amount = discounted_amounts[lines.tax_id] + else: + discounted_amount = discounted_amounts[line.tax_id] if discounted_amount == 0: continue remaining = remaining_amount_per_line[line] consumed = min(remaining, discounted_amount) - discounted_amounts[line.tax_id] -= consumed + if lines.reward_id.program_id.is_payment_program: + discounted_amounts[lines.tax_id] -= consumed + else: + discounted_amounts[line.tax_id] -= consumed remaining_amount_per_line[line] -= consumed discountable = 0 @@ -310,7 +318,7 @@ class SaleOrder(models.Model): converted_discount = self.currency_id._convert(min(max_discount, discountable), reward.currency_id, self.company_id, fields.Date.today()) point_cost = converted_discount / reward.discount # Gift cards and eWallets are considered gift cards and should not have any taxes - if reward.program_id.program_type in ('ewallet', 'gift_card'): + if reward.program_id.is_payment_program: return [{ 'name': reward.description, 'product_id': reward.discount_line_product_id.id, diff --git a/addons/sale_loyalty/tests/test_loyalty.py b/addons/sale_loyalty/tests/test_loyalty.py index f115128651f..fbd8c98bb1f 100644 --- a/addons/sale_loyalty/tests/test_loyalty.py +++ b/addons/sale_loyalty/tests/test_loyalty.py @@ -1,10 +1,12 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo.tests import TransactionCase, tagged, new_test_user +from odoo.tests import tagged, new_test_user +from odoo.addons.sale_loyalty.tests.common import TestSaleCouponCommon +from odoo import Command @tagged('post_install', '-at_install') -class TestLoyalty(TransactionCase): +class TestLoyalty(TestSaleCouponCommon): @classmethod def setUpClass(cls): @@ -117,3 +119,104 @@ class TestLoyalty(TransactionCase): # This last one will try to find related POS lines while user have not access to POS. order._action_cancel() self.assertFalse(order.coupon_point_ids) + + def test_distribution_amount_payment_programs(self): + """ + Check how the amount of a payment reward is distributed. + An ewallet should not be used to refund taxes. + Its amount must be distributed between the products. + """ + + # Create two products + product_a, product_b = self.env['product.product'].create([ + { + 'name': 'Product A', + 'list_price': 100, + 'sale_ok': True, + 'taxes_id': [Command.set(self.tax_15pc_excl.ids)], + }, + { + 'name': 'Product B', + 'list_price': 100, + 'sale_ok': True, + 'taxes_id': [Command.set(self.tax_15pc_excl.ids)], + }, + ]) + + # Create a coupon and a ewallet + coupon_program, ewallet_program = self.env['loyalty.program'].create([ + { + 'name': 'Coupon Program', + 'program_type': 'coupons', + 'trigger': 'with_code', + 'applies_on': 'both', + 'reward_ids': [Command.create({ + 'reward_type': 'discount', + 'discount': 100.0, + 'discount_applicability': 'specific', + 'discount_product_domain': '[("name", "=", "Product A")]', + })], + }, + { + 'name': 'eWallet Program', + 'program_type': 'ewallet', + 'applies_on': 'future', + 'trigger': 'auto', + 'rule_ids': [Command.create({ + 'reward_point_mode': 'money', + })], + 'reward_ids': [Command.create({ + 'discount_mode': 'per_point', + 'discount': 1, + 'discount_applicability': 'order', + })], + } + ]) + + coupon_partner, _ = self.env['loyalty.card'].create([ + { + 'program_id': coupon_program.id, + 'partner_id': self.partner_a.id, + 'points': 1, + 'code': '5555', + }, + { + 'program_id': ewallet_program.id, + 'partner_id': self.partner_a.id, + 'points': 115, + }, + ]) + + # Create the order + order = self.env['sale.order'].with_user(self.user_salemanager).create({ + 'partner_id': self.partner_a.id, + 'order_line': [ + Command.create({ + 'product_id': product_a.id, + }), + Command.create({ + 'product_id': product_b.id, + }), + ] + }) + + self.assertEqual(order.amount_total, 230.0) + self.assertEqual(order.amount_untaxed, 200.0) + self.assertEqual(order.amount_tax, 30.0) + + # Apply the eWallet + order._update_programs_and_rewards() + self._claim_reward(order, ewallet_program) + + self.assertEqual(order.amount_total, 115.0) + self.assertEqual(order.amount_untaxed, 85.0) + self.assertEqual(order.amount_tax, 30.0) + self.assertEqual(order.reward_amount, -115.0) + + # Apply the coupon + self._apply_promo_code(order, coupon_partner.code) + + self.assertEqual(order.amount_total, 0.0) + self.assertEqual(order.amount_untaxed, -15.0) + self.assertEqual(order.amount_tax, 15.0) + self.assertEqual(order.reward_amount, -215.0)