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 ...@@ -32,7 +32,7 @@ from ecommerce.courses.tests.factories import CourseFactory
from ecommerce.enterprise.tests.mixins import EnterpriseServiceMockMixin from ecommerce.enterprise.tests.mixins import EnterpriseServiceMockMixin
from ecommerce.entitlements.utils import create_or_update_course_entitlement from ecommerce.entitlements.utils import create_or_update_course_entitlement
from ecommerce.extensions.analytics.utils import translate_basket_line_for_segment 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.catalogue.tests.mixins import DiscoveryTestMixin
from ecommerce.extensions.offer.utils import format_benefit_value from ecommerce.extensions.offer.utils import format_benefit_value
from ecommerce.extensions.order.utils import UserAlreadyPlacedOrder from ecommerce.extensions.order.utils import UserAlreadyPlacedOrder
...@@ -787,6 +787,17 @@ class VoucherAddViewTests(LmsApiMockMixin, TestCase): ...@@ -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)) 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): def test_form_valid_without_basket_id(self):
""" Verify the view redirects to the basket summary view if the basket has no ID. """ """ Verify the view redirects to the basket summary view if the basket has no ID. """
self.request.basket = Basket() self.request.basket = Basket()
......
...@@ -65,15 +65,7 @@ def prepare_basket(request, products, voucher=None): ...@@ -65,15 +65,7 @@ def prepare_basket(request, products, voucher=None):
already_purchased_products = [] already_purchased_products = []
bundle = request.GET.get('bundle') bundle = request.GET.get('bundle')
if bundle: _set_basket_bundle_status(bundle, basket)
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()
if request.site.siteconfiguration.enable_embargo_check: if request.site.siteconfiguration.enable_embargo_check:
if not embargo_check(request.user, request.site, products): if not embargo_check(request.user, request.site, products):
...@@ -244,3 +236,31 @@ def basket_add_organization_attribute(basket, request_data): ...@@ -244,3 +236,31 @@ def basket_add_organization_attribute(basket, request_data):
attribute_type=organization_attribute, attribute_type=organization_attribute,
value_text=business_client.strip() 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 ...@@ -456,7 +456,10 @@ class VoucherAddView(BaseVoucherAddView): # pylint: disable=function-redefined
basket=self.request.basket, basket=self.request.basket,
attribute_type=BasketAttributeType.objects.get(name=BUNDLE) 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( messages.error(
self.request, self.request,
_("Coupon code '{code}' is not valid for this basket.").format(code=code)) _("Coupon code '{code}' is not valid for this basket.").format(code=code))
......
from decimal import Decimal from decimal import Decimal
import httpretty import httpretty
from django.urls import reverse
from oscar.core.loading import get_class from oscar.core.loading import get_class
from oscar.test.factories import RangeFactory from oscar.test.factories import RangeFactory
...@@ -8,12 +9,13 @@ from ecommerce.courses.models import Course ...@@ -8,12 +9,13 @@ from ecommerce.courses.models import Course
from ecommerce.extensions.partner.strategy import DefaultStrategy from ecommerce.extensions.partner.strategy import DefaultStrategy
from ecommerce.extensions.test import factories from ecommerce.extensions.test import factories
from ecommerce.programs.tests.mixins import ProgramTestMixin from ecommerce.programs.tests.mixins import ProgramTestMixin
from ecommerce.tests.mixins import LmsApiMockMixin
from ecommerce.tests.testcases import TestCase from ecommerce.tests.testcases import TestCase
Applicator = get_class('offer.applicator', 'Applicator') Applicator = get_class('offer.applicator', 'Applicator')
class ProgramOfferTests(ProgramTestMixin, TestCase): class ProgramOfferTests(LmsApiMockMixin, ProgramTestMixin, TestCase):
""" Verification for program offer application. """ """ Verification for program offer application. """
@httpretty.activate @httpretty.activate
...@@ -59,10 +61,17 @@ class ProgramOfferTests(ProgramTestMixin, TestCase): ...@@ -59,10 +61,17 @@ class ProgramOfferTests(ProgramTestMixin, TestCase):
basket.reset_offer_applications() basket.reset_offer_applications()
product_range = RangeFactory(products=products) product_range = RangeFactory(products=products)
voucher, __ = factories.prepare_voucher(_range=product_range, benefit_value=50) 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 # Verify that voucher-based offer takes precedence over program offer.
Applicator().apply(basket, basket.owner) 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() lines = basket.all_lines()
self.assertEqual(len(basket.offer_applications), 1) self.assertEqual(len(basket.offer_applications), 1)
self.assertEqual(basket.total_discount, Decimal(50) * len(lines)) 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