You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

absolute_benefit_tests.py 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. from decimal import Decimal as D
  2. from django.core import exceptions
  3. from django.test import TestCase
  4. import mock
  5. from oscar.apps.offer import models
  6. from oscar.test.basket import add_product, add_products
  7. from oscar.test import factories
  8. class TestAnAbsoluteDiscountAppliedWithCountCondition(TestCase):
  9. def setUp(self):
  10. range = models.Range.objects.create(
  11. name="All products", includes_all_products=True)
  12. self.condition = models.CountCondition.objects.create(
  13. range=range,
  14. type=models.Condition.COUNT,
  15. value=2)
  16. self.offer = mock.Mock()
  17. self.offer.applies_to_tax_exclusive_prices = False
  18. self.benefit = models.AbsoluteDiscountBenefit.objects.create(
  19. range=range,
  20. type=models.Benefit.FIXED,
  21. value=D('3.00'))
  22. self.basket = factories.create_basket(empty=True)
  23. def test_applies_correctly_to_empty_basket(self):
  24. result = self.benefit.apply(self.basket, self.condition, self.offer)
  25. self.assertEqual(D('0.00'), result.discount)
  26. self.assertEqual(0, self.basket.num_items_with_discount)
  27. self.assertEqual(0, self.basket.num_items_without_discount)
  28. def test_applies_correctly_to_basket_which_matches_condition_with_one_line(self):
  29. add_product(self.basket, price=D('12.00'), quantity=2)
  30. result = self.benefit.apply(self.basket, self.condition, self.offer)
  31. self.assertEqual(D('3.00'), result.discount)
  32. self.assertEqual(2, self.basket.num_items_with_discount)
  33. self.assertEqual(0, self.basket.num_items_without_discount)
  34. # Check the discount is applied equally to each item in the line
  35. line = self.basket.all_lines()[0]
  36. prices = line.get_price_breakdown()
  37. self.assertEqual(1, len(prices))
  38. self.assertEqual(D('10.50'), prices[0][0])
  39. def test_applies_correctly_to_basket_which_matches_condition_with_multiple_lines(self):
  40. # Use a basket with 2 lines
  41. add_products(self.basket, [
  42. (D('12.00'), 1), (D('12.00'), 1)])
  43. result = self.benefit.apply(self.basket, self.condition, self.offer)
  44. self.assertTrue(result.is_successful)
  45. self.assertFalse(result.is_final)
  46. self.assertEqual(D('3.00'), result.discount)
  47. self.assertEqual(2, self.basket.num_items_with_discount)
  48. self.assertEqual(0, self.basket.num_items_without_discount)
  49. # Check the discount is applied equally to each line
  50. for line in self.basket.all_lines():
  51. self.assertEqual(D('1.50'), line.discount_value)
  52. def test_applies_correctly_to_basket_which_matches_condition_with_multiple_lines_and_lower_total_value(self):
  53. # Use a basket with 2 lines
  54. add_products(self.basket, [
  55. (D('1.00'), 1), (D('1.50'), 1)])
  56. result = self.benefit.apply(self.basket, self.condition, self.offer)
  57. self.assertTrue(result.is_successful)
  58. self.assertFalse(result.is_final)
  59. self.assertEqual(D('2.50'), result.discount)
  60. self.assertEqual(2, self.basket.num_items_with_discount)
  61. self.assertEqual(0, self.basket.num_items_without_discount)
  62. def test_applies_correctly_to_basket_which_exceeds_condition(self):
  63. add_products(self.basket, [
  64. (D('12.00'), 2), (D('10.00'), 2)])
  65. result = self.benefit.apply(self.basket, self.condition, self.offer)
  66. self.assertEqual(D('3.00'), result.discount)
  67. self.assertEqual(4, self.basket.num_items_with_discount)
  68. self.assertEqual(0, self.basket.num_items_without_discount)
  69. def test_applies_correctly_to_basket_which_exceeds_condition_with_smaller_prices_than_discount(self):
  70. add_products(self.basket, [
  71. (D('2.00'), 2), (D('4.00'), 2)])
  72. result = self.benefit.apply(self.basket, self.condition, self.offer)
  73. self.assertEqual(D('3.00'), result.discount)
  74. self.assertEqual(4, self.basket.num_items_with_discount)
  75. self.assertEqual(0, self.basket.num_items_without_discount)
  76. def test_applies_correctly_to_basket_which_exceeds_condition_with_smaller_prices_than_discount_and_higher_prices_first(self):
  77. add_products(self.basket, [
  78. (D('2.00'), 2), (D('4.00'), 2)])
  79. result = self.benefit.apply(self.basket, self.condition, self.offer)
  80. self.assertEqual(D('3.00'), result.discount)
  81. self.assertEqual(4, self.basket.num_items_with_discount)
  82. self.assertEqual(0, self.basket.num_items_without_discount)
  83. class TestAnAbsoluteDiscount(TestCase):
  84. def setUp(self):
  85. range = models.Range.objects.create(
  86. name="All products", includes_all_products=True)
  87. self.condition = models.CountCondition.objects.create(
  88. range=range,
  89. type=models.Condition.COUNT,
  90. value=2)
  91. self.benefit = models.AbsoluteDiscountBenefit.objects.create(
  92. range=range,
  93. type=models.Benefit.FIXED,
  94. value=D('4.00'))
  95. self.offer = mock.Mock()
  96. self.offer.applies_to_tax_exclusive_prices = False
  97. self.basket = factories.create_basket(empty=True)
  98. def test_applies_correctly_when_discounts_need_rounding(self):
  99. # Split discount across 3 lines
  100. for price in [D('2.00'), D('2.00'), D('2.00')]:
  101. add_product(self.basket, price)
  102. result = self.benefit.apply(self.basket, self.condition, self.offer)
  103. self.assertEqual(D('4.00'), result.discount)
  104. # Check the discount is applied equally to each line
  105. line_discounts = [line.discount_value for line in self.basket.all_lines()]
  106. self.assertItemsEqual(
  107. line_discounts, [D('1.33'), D('1.33'), D('1.34')])
  108. class TestAnAbsoluteDiscountWithMaxItemsSetAppliedWithCountCondition(TestCase):
  109. def setUp(self):
  110. range = models.Range.objects.create(
  111. name="All products", includes_all_products=True)
  112. self.condition = models.CountCondition.objects.create(
  113. range=range,
  114. type=models.Condition.COUNT,
  115. value=2)
  116. self.benefit = models.AbsoluteDiscountBenefit.objects.create(
  117. range=range,
  118. type=models.Benefit.FIXED,
  119. value=D('3.00'),
  120. max_affected_items=1)
  121. self.offer = mock.Mock()
  122. self.offer.applies_to_tax_exclusive_prices = False
  123. self.basket = factories.create_basket(empty=True)
  124. def test_applies_correctly_to_empty_basket(self):
  125. result = self.benefit.apply(self.basket, self.condition, self.offer)
  126. self.assertEqual(D('0.00'), result.discount)
  127. self.assertEqual(0, self.basket.num_items_with_discount)
  128. self.assertEqual(0, self.basket.num_items_without_discount)
  129. def test_applies_correctly_to_basket_which_matches_condition(self):
  130. add_product(self.basket, D('12.00'), 2)
  131. result = self.benefit.apply(self.basket, self.condition, self.offer)
  132. self.assertEqual(D('3.00'), result.discount)
  133. self.assertEqual(2, self.basket.num_items_with_discount)
  134. self.assertEqual(0, self.basket.num_items_without_discount)
  135. def test_applies_correctly_to_basket_which_exceeds_condition(self):
  136. add_products(self.basket, [(D('12.00'), 2), (D('10.00'), 2)])
  137. result = self.benefit.apply(self.basket, self.condition, self.offer)
  138. self.assertEqual(D('3.00'), result.discount)
  139. self.assertEqual(2, self.basket.num_items_with_discount)
  140. self.assertEqual(2, self.basket.num_items_without_discount)
  141. def test_applies_correctly_to_basket_which_exceeds_condition_but_with_smaller_prices_than_discount(self):
  142. add_products(self.basket, [(D('2.00'), 2), (D('1.00'), 2)])
  143. result = self.benefit.apply(self.basket, self.condition, self.offer)
  144. self.assertEqual(D('1.00'), result.discount)
  145. self.assertEqual(2, self.basket.num_items_with_discount)
  146. self.assertEqual(2, self.basket.num_items_without_discount)
  147. class TestAnAbsoluteDiscountAppliedWithValueCondition(TestCase):
  148. def setUp(self):
  149. range = models.Range.objects.create(
  150. name="All products", includes_all_products=True)
  151. self.condition = models.ValueCondition.objects.create(
  152. range=range,
  153. type=models.Condition.VALUE,
  154. value=D('10.00'))
  155. self.benefit = models.AbsoluteDiscountBenefit.objects.create(
  156. range=range,
  157. type=models.Benefit.FIXED,
  158. value=D('3.00'))
  159. self.offer = mock.Mock()
  160. self.offer.applies_to_tax_exclusive_prices = False
  161. self.basket = factories.create_basket(empty=True)
  162. def test_applies_correctly_to_empty_basket(self):
  163. result = self.benefit.apply(self.basket, self.condition, self.offer)
  164. self.assertEqual(D('0.00'), result.discount)
  165. self.assertEqual(0, self.basket.num_items_with_discount)
  166. self.assertEqual(0, self.basket.num_items_without_discount)
  167. def test_applies_correctly_to_single_item_basket_which_matches_condition(self):
  168. add_products(self.basket, [(D('10.00'), 1)])
  169. result = self.benefit.apply(self.basket, self.condition, self.offer)
  170. self.assertEqual(D('3.00'), result.discount)
  171. self.assertEqual(1, self.basket.num_items_with_discount)
  172. self.assertEqual(0, self.basket.num_items_without_discount)
  173. def test_applies_correctly_to_multi_item_basket_which_matches_condition(self):
  174. add_products(self.basket, [(D('5.00'), 2)])
  175. result = self.benefit.apply(self.basket, self.condition, self.offer)
  176. self.assertEqual(D('3.00'), result.discount)
  177. self.assertEqual(2, self.basket.num_items_with_discount)
  178. self.assertEqual(0, self.basket.num_items_without_discount)
  179. def test_applies_correctly_to_multi_item_basket_which_exceeds_condition(self):
  180. add_products(self.basket, [(D('4.00'), 3)])
  181. result = self.benefit.apply(self.basket, self.condition, self.offer)
  182. self.assertEqual(D('3.00'), result.discount)
  183. self.assertEqual(3, self.basket.num_items_with_discount)
  184. self.assertEqual(0, self.basket.num_items_without_discount)
  185. def test_applies_correctly_to_multi_item_basket_which_exceeds_condition_but_matches_boundary(self):
  186. add_products(self.basket, [(D('5.00'), 3)])
  187. result = self.benefit.apply(self.basket, self.condition, self.offer)
  188. self.assertEqual(D('3.00'), result.discount)
  189. self.assertEqual(3, self.basket.num_items_with_discount)
  190. self.assertEqual(0, self.basket.num_items_without_discount)
  191. class TestAnAbsoluteDiscountWithMaxItemsSetAppliedWithValueCondition(TestCase):
  192. def setUp(self):
  193. range = models.Range.objects.create(
  194. name="All products", includes_all_products=True)
  195. self.condition = models.ValueCondition.objects.create(
  196. range=range,
  197. type=models.Condition.VALUE,
  198. value=D('10.00'))
  199. self.benefit = models.AbsoluteDiscountBenefit.objects.create(
  200. range=range,
  201. type=models.Benefit.FIXED,
  202. value=D('3.00'),
  203. max_affected_items=1)
  204. self.offer = mock.Mock()
  205. self.offer.applies_to_tax_exclusive_prices = False
  206. self.basket = factories.create_basket(empty=True)
  207. def test_applies_correctly_to_empty_basket(self):
  208. result = self.benefit.apply(self.basket, self.condition, self.offer)
  209. self.assertEqual(D('0.00'), result.discount)
  210. self.assertEqual(0, self.basket.num_items_with_discount)
  211. self.assertEqual(0, self.basket.num_items_without_discount)
  212. def test_applies_correctly_to_single_item_basket_which_matches_condition(self):
  213. add_products(self.basket, [(D('10.00'), 1)])
  214. result = self.benefit.apply(self.basket, self.condition, self.offer)
  215. self.assertEqual(D('3.00'), result.discount)
  216. self.assertEqual(1, self.basket.num_items_with_discount)
  217. self.assertEqual(0, self.basket.num_items_without_discount)
  218. def test_applies_correctly_to_multi_item_basket_which_matches_condition(self):
  219. add_products(self.basket, [(D('5.00'), 2)])
  220. result = self.benefit.apply(self.basket, self.condition, self.offer)
  221. self.assertEqual(D('3.00'), result.discount)
  222. self.assertEqual(2, self.basket.num_items_with_discount)
  223. self.assertEqual(0, self.basket.num_items_without_discount)
  224. def test_applies_correctly_to_multi_item_basket_which_exceeds_condition(self):
  225. add_products(self.basket, [(D('4.00'), 3)])
  226. result = self.benefit.apply(self.basket, self.condition, self.offer)
  227. self.assertEqual(D('3.00'), result.discount)
  228. self.assertEqual(3, self.basket.num_items_with_discount)
  229. self.assertEqual(0, self.basket.num_items_without_discount)
  230. def test_applies_correctly_to_multi_item_basket_which_exceeds_condition_but_matches_boundary(self):
  231. add_products(self.basket, [(D('5.00'), 3)])
  232. result = self.benefit.apply(self.basket, self.condition, self.offer)
  233. self.assertEqual(D('3.00'), result.discount)
  234. self.assertEqual(2, self.basket.num_items_with_discount)
  235. self.assertEqual(1, self.basket.num_items_without_discount)
  236. def test_applies_correctly_to_multi_item_basket_which_matches_condition_but_with_lower_prices_than_discount(self):
  237. add_products(self.basket, [(D('2.00'), 6)])
  238. result = self.benefit.apply(self.basket, self.condition, self.offer)
  239. self.assertEqual(D('2.00'), result.discount)
  240. self.assertEqual(5, self.basket.num_items_with_discount)
  241. self.assertEqual(1, self.basket.num_items_without_discount)
  242. class TestAnAbsoluteDiscountBenefit(TestCase):
  243. def test_requires_a_benefit_value(self):
  244. rng = models.Range.objects.create(
  245. name="", includes_all_products=True)
  246. benefit = models.Benefit.objects.create(
  247. type=models.Benefit.FIXED, range=rng
  248. )
  249. with self.assertRaises(exceptions.ValidationError):
  250. benefit.clean()