diff --git a/openedx/features/course_experience/utils.py b/openedx/features/course_experience/utils.py index eb105bba1e..a22b0d7ee5 100644 --- a/openedx/features/course_experience/utils.py +++ b/openedx/features/course_experience/utils.py @@ -196,7 +196,7 @@ def get_first_purchase_offer_banner_fragment(user, course): if (FIRST_PURCHASE_OFFER_BANNER_DISPLAY.is_enabled() and user and course and - can_receive_discount(user=user, course_key_string=unicode(course.id))): + can_receive_discount(user=user, course=course)): # Translator: xgettext:no-python-format offer_message = _(u'{banner_open}{percentage}% off your first upgrade.{span_close}' u' Discount automatically applied.{div_close}') diff --git a/openedx/features/discounts/applicability.py b/openedx/features/discounts/applicability.py index ff04c12876..43114a8248 100644 --- a/openedx/features/discounts/applicability.py +++ b/openedx/features/discounts/applicability.py @@ -2,7 +2,13 @@ """ Contains code related to computing discount percentage and discount applicability. + +WARNING: +Keep in mind that the code in this file only applies to discounts controlled in the lms like the first purchase offer, +not other discounts like coupons or enterprise/program offers configured in ecommerce. + """ +from course_modes.models import CourseMode from openedx.core.djangoapps.waffle_utils import WaffleFlag, WaffleFlagNamespace # .. feature_toggle_name: discounts.enable_discounting @@ -23,7 +29,7 @@ DISCOUNT_APPLICABILITY_FLAG = WaffleFlag( ) -def can_receive_discount(user, course_key_string): # pylint: disable=unused-argument +def can_receive_discount(user, course): # pylint: disable=unused-argument """ Check all the business logic about whether this combination of user and course can receive a discount. @@ -34,6 +40,16 @@ def can_receive_discount(user, course_key_string): # pylint: disable=unused-arg # TODO: Add additional conditions to return False here + # Course end date needs to be in the future + if course.has_ended(): + return False + + # Course needs to have a non-expired verified mode + modes_dict = CourseMode.modes_for_course_dict(course=course, include_expired=False) + verified_mode = modes_dict.get('verified', None) + if not verified_mode: + return False + return True diff --git a/openedx/features/discounts/tests/test_applicability.py b/openedx/features/discounts/tests/test_applicability.py index adee31e7f3..5ebbe89bf0 100644 --- a/openedx/features/discounts/tests/test_applicability.py +++ b/openedx/features/discounts/tests/test_applicability.py @@ -1,11 +1,16 @@ """Tests of openedx.features.discounts.applicability""" # -*- coding: utf-8 -*- +from datetime import timedelta +from django.utils.timezone import now + +from course_modes.tests.factories import CourseModeFactory +from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag from student.tests.factories import UserFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory -from ..applicability import can_receive_discount +from ..applicability import can_receive_discount, DISCOUNT_APPLICABILITY_FLAG class TestApplicability(ModuleStoreTestCase): @@ -18,8 +23,25 @@ class TestApplicability(ModuleStoreTestCase): super(TestApplicability, self).setUp() self.user = UserFactory.create() self.course = CourseFactory.create(run='test', display_name='test') + CourseModeFactory.create(course_id=self.course.id, mode_slug='verified') def test_can_receive_discount(self): # Right now, no one should be able to receive the discount - applicability = can_receive_discount(user=self.user, course_key_string=self.course.id) + applicability = can_receive_discount(user=self.user, course=self.course) + self.assertEqual(applicability, False) + + @override_waffle_flag(DISCOUNT_APPLICABILITY_FLAG, active=True) + def test_can_receive_discount_course_requirements(self): + """ + Ensure first purchase offer banner only displays for courses with a non-expired verified mode + """ + applicability = can_receive_discount(user=self.user, course=self.course) + self.assertEqual(applicability, True) + + no_verified_mode_course = CourseFactory(end=now() + timedelta(days=30)) + applicability = can_receive_discount(user=self.user, course=no_verified_mode_course) + self.assertEqual(applicability, False) + + course_that_has_ended = CourseFactory(end=now() - timedelta(days=30)) + applicability = can_receive_discount(user=self.user, course=course_that_has_ended) self.assertEqual(applicability, False) diff --git a/openedx/features/discounts/views.py b/openedx/features/discounts/views.py index 6f3b473f34..5fb70e09bd 100644 --- a/openedx/features/discounts/views.py +++ b/openedx/features/discounts/views.py @@ -7,6 +7,8 @@ The Discount API Views should return information about discounts that apply to t from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication from edx_rest_framework_extensions.auth.session.authentication import SessionAuthenticationAllowInactiveUser +from opaque_keys.edx.keys import CourseKey +from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.cors_csrf.decorators import ensure_csrf_cookie_cross_domain from openedx.core.djangoapps.oauth_dispatch.jwt import create_jwt_for_user from openedx.core.lib.api.authentication import OAuth2AuthenticationAllowInactiveUser @@ -65,7 +67,9 @@ class CourseUserDiscount(DeveloperErrorViewMixin, APIView): """ Return the discount percent, if the user has appropriate permissions. """ - discount_applicable = can_receive_discount(user=request.user, course_key_string=course_key_string) + course_key = CourseKey.from_string(course_key_string) + course = CourseOverview.get_from_id(course_key) + discount_applicable = can_receive_discount(user=request.user, course=course) discount_percent = discount_percentage() payload = {'discount_applicable': discount_applicable, 'discount_percent': discount_percent} return Response({