Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

test_absolute_benefit.py 16KB

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