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

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