Commit 548aa9e6 by Waheed Ahmed

Fix add voucher view to use multi use coupon for bundles.

LEARNER-5436
parent 7185ca4a
......@@ -32,7 +32,7 @@ from ecommerce.courses.tests.factories import CourseFactory
from ecommerce.enterprise.tests.mixins import EnterpriseServiceMockMixin
from ecommerce.entitlements.utils import create_or_update_course_entitlement
from ecommerce.extensions.analytics.utils import translate_basket_line_for_segment
from ecommerce.extensions.basket.utils import get_basket_switch_data
from ecommerce.extensions.basket.utils import _set_basket_bundle_status, get_basket_switch_data
from ecommerce.extensions.catalogue.tests.mixins import DiscoveryTestMixin
from ecommerce.extensions.offer.utils import format_benefit_value
from ecommerce.extensions.order.utils import UserAlreadyPlacedOrder
......@@ -787,6 +787,17 @@ class VoucherAddViewTests(LmsApiMockMixin, TestCase):
)
self.assert_form_valid_message("Coupon code '{code}' is not valid for this basket.".format(code=voucher.code))
def test_multi_use_voucher_valid_for_bundle(self):
""" Verify multi use coupon works when voucher is used against a bundle. """
self.mock_access_token_response()
self.mock_account_api(self.request, self.user.username, data={'is_active': True})
voucher, product = prepare_voucher(code=COUPON_CODE, benefit_value=10, usage=Voucher.MULTI_USE)
new_product = factories.ProductFactory(categories=[], stockrecords__partner__short_code='second')
self.basket.add_product(product)
self.basket.add_product(new_product)
_set_basket_bundle_status('test-bundle', self.basket)
self.assert_form_valid_message("Coupon code '{code}' added to basket.".format(code=voucher.code))
def test_form_valid_without_basket_id(self):
""" Verify the view redirects to the basket summary view if the basket has no ID. """
self.request.basket = Basket()
......
......@@ -65,15 +65,7 @@ def prepare_basket(request, products, voucher=None):
already_purchased_products = []
bundle = request.GET.get('bundle')
if bundle:
BasketAttribute.objects.update_or_create(
basket=basket,
attribute_type=BasketAttributeType.objects.get(name=BUNDLE),
defaults={'value_text': bundle}
)
basket.clear_vouchers()
else:
BasketAttribute.objects.filter(basket=basket, attribute_type__name=BUNDLE).delete()
_set_basket_bundle_status(bundle, basket)
if request.site.siteconfiguration.enable_embargo_check:
if not embargo_check(request.user, request.site, products):
......@@ -244,3 +236,31 @@ def basket_add_organization_attribute(basket, request_data):
attribute_type=organization_attribute,
value_text=business_client.strip()
)
def _set_basket_bundle_status(bundle, basket):
"""
Sets the basket's bundle status
Note: This is a refactor of the existing code. Not sure
what the intentions of the side effects are.
Side effect:
clears any vouchers if it's a bundle
Args:
bundle (str): The Bundle ID?
basket (Basket): The basket to set the bundle attribute for
Returns:
"""
if bundle:
BasketAttribute.objects.update_or_create(
basket=basket,
attribute_type=BasketAttributeType.objects.get(name=BUNDLE),
defaults={'value_text': bundle}
)
basket.clear_vouchers()
else:
BasketAttribute.objects.filter(basket=basket, attribute_type__name=BUNDLE).delete()
......@@ -456,7 +456,10 @@ class VoucherAddView(BaseVoucherAddView): # pylint: disable=function-redefined
basket=self.request.basket,
attribute_type=BasketAttributeType.objects.get(name=BUNDLE)
)
if len(bundle_attribute) > 0 and not voucher.offers.first().condition.program_uuid:
is_bundle_purchase = len(bundle_attribute) > 0
voucher_program_uuid = voucher.offers.first().condition.program_uuid
is_voucher_valid_for_bundle = voucher_program_uuid or voucher.usage == Voucher.MULTI_USE
if is_bundle_purchase and not is_voucher_valid_for_bundle:
messages.error(
self.request,
_("Coupon code '{code}' is not valid for this basket.").format(code=code))
......
from decimal import Decimal
import httpretty
from django.urls import reverse
from oscar.core.loading import get_class
from oscar.test.factories import RangeFactory
......@@ -8,12 +9,13 @@ from ecommerce.courses.models import Course
from ecommerce.extensions.partner.strategy import DefaultStrategy
from ecommerce.extensions.test import factories
from ecommerce.programs.tests.mixins import ProgramTestMixin
from ecommerce.tests.mixins import LmsApiMockMixin
from ecommerce.tests.testcases import TestCase
Applicator = get_class('offer.applicator', 'Applicator')
class ProgramOfferTests(ProgramTestMixin, TestCase):
class ProgramOfferTests(LmsApiMockMixin, ProgramTestMixin, TestCase):
""" Verification for program offer application. """
@httpretty.activate
......@@ -59,10 +61,17 @@ class ProgramOfferTests(ProgramTestMixin, TestCase):
basket.reset_offer_applications()
product_range = RangeFactory(products=products)
voucher, __ = factories.prepare_voucher(_range=product_range, benefit_value=50)
basket.vouchers.add(voucher)
self.mock_account_api(self.request, basket.owner.username, data={'is_active': True})
self.client.login(username=basket.owner.username, password=self.password)
self.client.post(reverse('basket:vouchers-add'), data={'code': voucher.code})
response = self.client.get(reverse('basket:summary'))
basket = response.context['basket']
# Apply offers and verify that voucher-based offer takes precedence over program offer
Applicator().apply(basket, basket.owner)
# Verify that voucher-based offer takes precedence over program offer.
actual_offer_discounts = [discount['offer'] for discount in basket.offer_discounts]
actual_voucher_discounts = [discount['offer'] for discount in basket.voucher_discounts]
self.assertEqual(actual_offer_discounts, [])
self.assertEqual(actual_voucher_discounts, [voucher.offers.first()])
lines = basket.all_lines()
self.assertEqual(len(basket.offer_applications), 1)
self.assertEqual(basket.total_discount, Decimal(50) * len(lines))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment