Open kgilpin opened 1 month ago
Exclusive voucher offers are not being enforced correctly. Users can add two exclusive vouchers to their basket, which violates the exclusivity constraint. Depending on the order in which the vouchers are added, they may or may not be added successfully.
The root cause of this problem is likely the logic that handles the validation and application of vouchers to the basket. The logic for adding vouchers should check if any exclusive vouchers are already applied to the basket and prevent adding another if so. The issue could be in both the validation logic and the application process of vouchers in the basket handling code.
File: src/oscar/apps/basket/views.py
form_valid
method in VoucherAddView
to include the logic to check for existing exclusive vouchers before adding a new voucher.File: src/oscar/apps/offer/applicator.py
File: tests/integration/basket/test_views.py
TestVoucherAddView
and TestVoucherRemoveView
classes to ensure exclusive vouchers cannot be applied if another exclusive voucher is already present in the basket.File: tests/integration/basket/test_utils.py
src/oscar/apps/basket/views.py:
form_valid
method, check if an exclusive voucher already exists in the basket. If so, display an error message and prevent adding the new voucher.def form_valid(self, form):
code = form.cleaned_data["code"]
if not self.request.basket.id:
return redirect_to_referrer(self.request, "basket:summary")
if self.request.basket.contains_voucher(code):
messages.error(
self.request,
_("You have already added the '%(code)s' voucher to your basket")
% {"code": code},
)
else:
try:
voucher = self.voucher_model._default_manager.get(code=code)
except self.voucher_model.DoesNotExist:
messages.error(
self.request,
_("No voucher found with code '%(code)s'") % {"code": code},
)
else:
# Check for existing exclusive vouchers
if voucher.exclusive and any(v.exclusive for v in self.request.basket.vouchers.all()):
messages.error(
self.request,
_("Cannot add an exclusive voucher when another exclusive voucher is already in the basket.")
)
else:
self.apply_voucher_to_basket(voucher)
return redirect_to_referrer(self.request, "basket:summary")
src/oscar/apps/offer/applicator.py:
def get_offers(self, basket, user=None):
offers = super(Applicator, self).get_offers(basket, user)
exclusive_voucher_applied = any(v.exclusive for v in basket.vouchers.all())
for voucher in basket.vouchers.all():
available_to_user, __ = voucher.is_available_to_user(user=user)
if voucher.is_active() and available_to_user:
basket_offers = voucher.offers.all()
for offer in basket_offers:
if exclusive_voucher_applied and offer.exclusive:
continue
offer.set_voucher(voucher)
offers = list(chain(offers, basket_offers))
return offers
tests/integration/basket/test_views.py:
class TestVoucherAddView(TestCase):
def test_exclusive_voucher_addition(self):
voucher1 = factories.VoucherFactory(exclusive=True)
voucher2 = factories.VoucherFactory(exclusive=True)
self.assertTrue(voucher1.is_active())
self.assertTrue(voucher2.is_active())
data1 = {"code": voucher1.code}
data2 = {"code": voucher2.code}
request = RequestFactory().post("/", data=data1)
request.basket.save()
view = views.VoucherAddView.as_view()
response = view(request) # Add the first exclusive voucher
self.assertEqual(response.status_code, 302)
self.assertEqual(request.basket.vouchers.count(), 1)
request = RequestFactory().post("/", data=data2)
response = view(request) # Attempt to add a second exclusive voucher
self.assertEqual(response.status_code, 302)
self.assertEqual(request.basket.vouchers.count(), 1) # Should not be added
# Add more tests for different scenarios of voucher addition order
By implementing these changes, the exclusivity constraint will be correctly enforced for vouchers in the basket, and the user will be appropriately notified if an exclusive voucher cannot be added.
Exclusive voucher offers are not exclusive
Issue Summary
When you create two new offers with the voucher offer type and make it exclusive, it's still possible to add both to your basket. Though reproducing was a bit strange, as it depends on the order you're adding the voucher.
I had to add my latest created voucher first and then the first one I created. The other way around resulted in an message that it wasn't possible to add that voucher to my basket.
Steps to Reproduce