diff --git a/models/sale_coupon_program.py b/models/sale_coupon_program.py index 6f7e7aa4669..d0d1e9ad786 100644 --- a/models/sale_coupon_program.py +++ b/models/sale_coupon_program.py @@ -2,7 +2,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import models, _ +from odoo import models, _, api class SaleCouponProgram(models.Model): @@ -22,3 +22,20 @@ class SaleCouponProgram(models.Model): if self.reward_type == 'free_shipping' and not order.order_line.filtered(lambda line: line.is_delivery): return {'error': _('The shipping costs are not in the order lines.')} return super(SaleCouponProgram, self)._check_promo_code(order, coupon_code) + + @api.model + def _filter_on_mimimum_amount(self, order): + # To get actual total amount of SO without any reward or discount + # We exclude any line of the order that is a delivery + # get the round function of the currency: we want to imitate what the sale.order.amount is (computed field) + round_curr = order.pricelist_id.currency_id.round + order_lines = order.order_line.filtered(lambda l: not l.is_delivery) + order_amount = round_curr(sum(order_lines.mapped('price_subtotal'))) + order_tax = round_curr(sum(order_lines.mapped('price_tax'))) + discounted_amount = sum(order_lines.filtered(lambda line: line.is_reward_line).mapped('price_total')) + untaxed_amount = order_amount - discounted_amount + return self.filtered(lambda program: + program.rule_minimum_amount_tax_inclusion == 'tax_included' and + program._compute_program_amount('rule_minimum_amount', order.currency_id) <= untaxed_amount + order_tax or + program.rule_minimum_amount_tax_inclusion == 'tax_excluded' and + program._compute_program_amount('rule_minimum_amount', order.currency_id) <= untaxed_amount) diff --git a/tests/test_free_shipping_reward.py b/tests/test_free_shipping_reward.py index b7780cd6f29..b5666e20c33 100644 --- a/tests/test_free_shipping_reward.py +++ b/tests/test_free_shipping_reward.py @@ -24,6 +24,7 @@ class TestSaleCouponProgramRules(TestSaleCouponCommon): 'partner_id': self.steve.id, }) + # Price of order will be 5*1.15 = 5.75 (tax included) order.write({'order_line': [ (0, False, { 'product_id': self.product_B.id, @@ -40,6 +41,24 @@ class TestSaleCouponProgramRules(TestSaleCouponCommon): order.recompute_coupon_lines() self.assertEqual(len(order.order_line.ids), 2) + # Test Case 1b: amount is not reached but is on a threshold + # The amount of deliverable product + the one of the delivery exceeds the minimum amount + # yet the program shouldn't be applied + + # Order price will be 5.75 + 81.74*1.15 = 99.75 + order.write({'order_line': [ + (0, False, { + 'product_id': self.product_B.id, + 'name': 'Product 1B', + 'product_uom': self.uom_unit.id, + 'product_uom_qty': 1.0, + 'price_unit': 81.74, + }) + ]}) + + order.recompute_coupon_lines() + self.assertEqual(len(order.order_line.ids), 3) + # Test case 2: the amount is sufficient, the shipping should # be reimbursed order.write({'order_line': [ @@ -53,11 +72,11 @@ class TestSaleCouponProgramRules(TestSaleCouponCommon): ]}) order.recompute_coupon_lines() - self.assertEqual(len(order.order_line.ids), 4) + self.assertEqual(len(order.order_line.ids), 5) # Test case 3: the amount is not sufficient now, the reward should be removed order.write({'order_line': [ (2, order.order_line.filtered(lambda line: line.product_id.id == self.product_A.id).id, False) ]}) order.recompute_coupon_lines() - self.assertEqual(len(order.order_line.ids), 2) + self.assertEqual(len(order.order_line.ids), 3)