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.

utils.py 3.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. from itertools import chain
  2. import logging
  3. from django.db.models import Q
  4. from django.utils.timezone import now
  5. from oscar.core.loading import get_model
  6. from oscar.apps.offer import results
  7. ConditionalOffer = get_model('offer', 'ConditionalOffer')
  8. logger = logging.getLogger('oscar.offers')
  9. class OfferApplicationError(Exception):
  10. pass
  11. class Applicator(object):
  12. def apply(self, request, basket):
  13. """
  14. Apply all relevant offers to the given basket.
  15. The request is passed too as sometimes the available offers
  16. are dependent on the user (eg session-based offers).
  17. """
  18. offers = self.get_offers(request, basket)
  19. self.apply_offers(basket, offers)
  20. def apply_offers(self, basket, offers):
  21. applications = results.OfferApplications()
  22. for offer in offers:
  23. num_applications = 0
  24. # Keep applying the offer until either
  25. # (a) We reach the max number of applications for the offer.
  26. # (b) The benefit can't be applied successfully.
  27. while num_applications < offer.get_max_applications(basket.owner):
  28. result = offer.apply_benefit(basket)
  29. num_applications += 1
  30. if not result.is_successful:
  31. break
  32. applications.add(offer, result)
  33. if result.is_final:
  34. break
  35. # Store this list of discounts with the basket so it can be
  36. # rendered in templates
  37. basket.offer_applications = applications
  38. def get_offers(self, request, basket):
  39. """
  40. Return all offers to apply to the basket.
  41. This method should be subclassed and extended to provide more
  42. sophisticated behaviour. For instance, you could load extra offers
  43. based on the session or the user type.
  44. """
  45. site_offers = self.get_site_offers()
  46. basket_offers = self.get_basket_offers(basket, request.user)
  47. user_offers = self.get_user_offers(request.user)
  48. session_offers = self.get_session_offers(request)
  49. return list(chain(
  50. session_offers, basket_offers, user_offers, site_offers))
  51. def get_site_offers(self):
  52. """
  53. Return site offers that are available to all users
  54. """
  55. cutoff = now()
  56. date_based = Q(
  57. Q(start_datetime__lte=cutoff),
  58. Q(end_datetime__gte=cutoff) | Q(end_datetime=None),
  59. )
  60. nondate_based = Q(start_datetime=None, end_datetime=None)
  61. qs = ConditionalOffer.objects.filter(
  62. date_based | nondate_based,
  63. offer_type=ConditionalOffer.SITE,
  64. status=ConditionalOffer.OPEN)
  65. # Using select_related with the condition/benefit ranges doesn't seem
  66. # to work. I think this is because both the related objects have the
  67. # FK to range with the same name.
  68. return qs.select_related('condition', 'benefit')
  69. def get_basket_offers(self, basket, user):
  70. """
  71. Return basket-linked offers such as those associated with a voucher
  72. code
  73. """
  74. offers = []
  75. if not basket.id:
  76. return offers
  77. for voucher in basket.vouchers.all():
  78. if voucher.is_active() and voucher.is_available_to_user(user):
  79. basket_offers = voucher.offers.all()
  80. for offer in basket_offers:
  81. offer.set_voucher(voucher)
  82. offers = list(chain(offers, basket_offers))
  83. return offers
  84. def get_user_offers(self, user):
  85. """
  86. Returns offers linked to this particular user.
  87. Eg: student users might get 25% off
  88. """
  89. return []
  90. def get_session_offers(self, request):
  91. """
  92. Returns temporary offers linked to the current session.
  93. Eg: visitors coming from an affiliate site get a 10% discount
  94. """
  95. return []