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.

test_offers.py 6.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. from decimal import ROUND_HALF_UP
  2. from decimal import Decimal as D
  3. from django.conf import settings
  4. from django.test import RequestFactory, TestCase
  5. from django.urls import reverse
  6. from oscar.core.loading import get_class, get_classes, get_model
  7. from oscar.test.factories import UserFactory
  8. Basket = get_model("basket", "Basket")
  9. Product = get_model("catalogue", "Product")
  10. factory = RequestFactory()
  11. Applicator = get_class("offer.applicator", "Applicator")
  12. Selector, UK = get_classes("partner.strategy", ["Selector", "UK"])
  13. class UKSelector(Selector):
  14. def strategy(self, request=None, user=None, **kwargs):
  15. return UK(request)
  16. def money(amount):
  17. return amount.quantize(D("0.01"), ROUND_HALF_UP)
  18. def get_user_basket(user, request):
  19. editable_baskets = Basket.objects.filter(status__in=["Open", "Saved"])
  20. basket, __ = editable_baskets.get_or_create(owner=user)
  21. basket.strategy = UKSelector().strategy(request=request, user=user)
  22. basket.reset_offer_applications()
  23. if not basket.is_empty:
  24. Applicator().apply(basket, user, request)
  25. request.session[settings.OSCAR_BASKET_COOKIE_OPEN] = basket.pk
  26. request.session.save()
  27. return basket
  28. class OfferTest(TestCase):
  29. fixtures = ["catalogue", "offer"]
  30. def check_general_truths(self, basket):
  31. inverse_tax_multiplier = D(1) / (D(1) + UK.rate)
  32. calculated_total_excl_tax = money(
  33. inverse_tax_multiplier * basket.total_incl_tax
  34. )
  35. self.assertEqual(
  36. calculated_total_excl_tax,
  37. basket.total_excl_tax,
  38. "The total price without tax should conform to the standard "
  39. "formula for calculating tax (as a percentage)",
  40. )
  41. self.assertAlmostEqual(
  42. basket.total_excl_tax_excl_discounts / basket.total_incl_tax_excl_discounts,
  43. basket.total_excl_tax / basket.total_incl_tax,
  44. 4,
  45. "The ratio of price with tax and without tax should be the same for the "
  46. "price with and without discounts. If that is not the case people would "
  47. "be able to change the tax they must pay by gaming the discount.",
  48. )
  49. self.assertNotAlmostEqual(
  50. basket.total_excl_tax_excl_discounts - basket.total_excl_tax,
  51. basket.total_incl_tax_excl_discounts - basket.total_incl_tax,
  52. 2,
  53. "The discount over the total excluding tax can never be the same as "
  54. "the discount over the total including tax. Otherwise our tax rate"
  55. "would not be linear over the amount.",
  56. )
  57. self.assertEqual(
  58. basket.total_excl_tax + basket.total_tax,
  59. basket.total_incl_tax,
  60. "The tax summation should amount to the total_incl_tax"
  61. )
  62. def test_offer_incl_tax(self):
  63. "The offer should be calculated as if it was declared including tax"
  64. with self.settings(OSCAR_OFFERS_INCL_TAX=True):
  65. self.assertEqual(Basket.objects.count(), 0)
  66. admin = UserFactory()
  67. self.client.force_login(admin)
  68. # throw an item in the basket
  69. basket_add_url = reverse("basket:add", args=(2,))
  70. body = {"quantity": 1}
  71. response = self.client.post(basket_add_url, body)
  72. # throw another item in the basket so the offer activates
  73. basket_add_url = reverse("basket:add", args=(3,))
  74. body = {"quantity": 2}
  75. response = self.client.post(basket_add_url, body)
  76. request = factory.post(basket_add_url, body)
  77. request.user = admin
  78. request.session = self.client.session
  79. basket = get_user_basket(admin, request)
  80. self.assertEqual(response.status_code, 302)
  81. self.assertEqual(Basket.objects.count(), 1)
  82. # now go and check if the offer was applied correctly
  83. self.assertEqual(
  84. basket.total_incl_tax_excl_discounts - basket.total_incl_tax,
  85. D("10.00"),
  86. "The offer should be a flat 10 pound discount on the total "
  87. "including tax",
  88. )
  89. self.assertEqual(
  90. basket.total_discount,
  91. D("10.00"),
  92. "The total discount property should properly reflect the discount"
  93. "applied.",
  94. )
  95. self.check_general_truths(basket)
  96. def test_offer_excl_tax(self):
  97. "The offer should be calculated as if it was declared excluding tax"
  98. with self.settings(OSCAR_OFFERS_INCL_TAX=False):
  99. self.assertEqual(Basket.objects.count(), 0)
  100. admin = UserFactory()
  101. self.client.force_login(admin)
  102. # throw an item in the basket
  103. basket_add_url = reverse("basket:add", args=(2,))
  104. body = {"quantity": 1}
  105. response = self.client.post(basket_add_url, body)
  106. # throw another item in the basket so the offer activates
  107. basket_add_url = reverse("basket:add", args=(3,))
  108. body = {"quantity": 2}
  109. response = self.client.post(basket_add_url, body)
  110. # now go and check if dat offer was handled correctly
  111. request = factory.post(basket_add_url, body)
  112. request.user = admin
  113. request.session = self.client.session
  114. basket = get_user_basket(admin, request)
  115. self.assertEqual(response.status_code, 302)
  116. self.assertEqual(Basket.objects.count(), 1)
  117. # now go and check if the offer was applied correctly
  118. self.assertEqual(
  119. basket.total_excl_tax_excl_discounts - basket.total_excl_tax,
  120. D("10.00"),
  121. "The offer should be a flat 10 pound discount on the total "
  122. "excluding tax",
  123. )
  124. self.assertEqual(
  125. basket.total_discount,
  126. D("10.00"),
  127. "The total discount property should properly reflect the discount"
  128. "applied.",
  129. )
  130. self.check_general_truths(basket)