Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 18 additions & 10 deletions openedx/features/discounts/applicability.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"""


import logging
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo

Expand All @@ -17,6 +18,7 @@
from django.utils import timezone
from django.utils.dateparse import parse_datetime
from edx_toggles.toggles import WaffleFlag
from openedx_filters.learning.filters import DiscountEligibilityCheckRequested

from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.entitlements.models import CourseEntitlement
Expand All @@ -28,6 +30,8 @@
from lms.djangoapps.experiments.stable_bucketing import stable_bucketing_hash_group
from openedx.features.discounts.models import DiscountPercentageConfig, DiscountRestrictionConfig

log = logging.getLogger(__name__)

# .. toggle_name: discounts.enable_first_purchase_discount_override
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
Expand Down Expand Up @@ -124,11 +128,13 @@ def can_show_streak_discount_coupon(user, course):
if not is_mode_upsellable(user, enrollment):
return False

# We can't import this at Django load time within the openedx tests settings context
from openedx.features.enterprise_support.utils import is_enterprise_learner

# Don't give discount to enterprise users
if is_enterprise_learner(user):
# Allow plugins to mark this user as ineligible for the discount.
try:
DiscountEligibilityCheckRequested.run_filter(
user=user, course_key=course.id,
)
except DiscountEligibilityCheckRequested.DiscountIneligible as exc:
log.info("User is ineligible for streak discount: %s", exc.message)
return False

return True
Expand Down Expand Up @@ -179,11 +185,13 @@ def can_receive_discount(user, course, discount_expiration_date=None):
if CourseEntitlement.objects.filter(user=user).exists():
return False

# We can't import this at Django load time within the openedx tests settings context
from openedx.features.enterprise_support.utils import is_enterprise_learner

# Don't give discount to enterprise users
if is_enterprise_learner(user):
# Allow plugins to mark this user as ineligible for the discount.
try:
DiscountEligibilityCheckRequested.run_filter(
user=user, course_key=course.id,
)
except DiscountEligibilityCheckRequested.DiscountIneligible as exc:
log.info("User is ineligible for discount: %s", exc.message)
return False

# Turn holdback on
Expand Down
45 changes: 34 additions & 11 deletions openedx/features/discounts/tests/test_applicability.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
from django.contrib.sites.models import Site
from django.utils.timezone import now
from edx_toggles.toggles.testutils import override_waffle_flag
from enterprise.models import EnterpriseCustomer, EnterpriseCustomerUser
from openedx_filters.learning.filters import DiscountEligibilityCheckRequested

from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
from common.djangoapps.entitlements.tests.factories import CourseEntitlementFactory
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
from lms.djangoapps.courseware.toggles import COURSEWARE_MFE_MILESTONES_STREAK_DISCOUNT
from lms.djangoapps.experiments.models import ExperimentData
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.features.discounts.models import DiscountRestrictionConfig
Expand All @@ -25,7 +26,12 @@
)
from xmodule.modulestore.tests.factories import CourseFactory # pylint: disable=wrong-import-order

from ..applicability import DISCOUNT_APPLICABILITY_FLAG, _is_in_holdback_and_bucket, can_receive_discount
from ..applicability import (
DISCOUNT_APPLICABILITY_FLAG,
_is_in_holdback_and_bucket,
can_receive_discount,
can_show_streak_discount_coupon,
)


@ddt.ddt
Expand All @@ -52,6 +58,13 @@ def setUp(self):
self.mock_holdback = holdback_patcher.start()
self.addCleanup(holdback_patcher.stop)

# By default, the filter does not raise, meaning the user is eligible.
discount_filter_patcher = patch(
'openedx.features.discounts.applicability.DiscountEligibilityCheckRequested.run_filter',
)
self.mock_discount_filter = discount_filter_patcher.start()
self.addCleanup(discount_filter_patcher.stop)

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=self.course)
Expand Down Expand Up @@ -136,17 +149,13 @@ def test_can_receive_discount_entitlement(self, entitlement_mode):
assert applicability == (entitlement_mode is None)

@override_waffle_flag(DISCOUNT_APPLICABILITY_FLAG, active=True)
def test_can_receive_discount_false_enterprise(self):
def test_can_receive_discount_false_when_filter_marks_ineligible(self):
"""
Ensure that enterprise users do not receive the discount.
Ensure that when the eligibility filter raises DiscountIneligible,
no discount is received.
"""
enterprise_customer = EnterpriseCustomer.objects.create(
name='Test EnterpriseCustomer',
site=self.site
)
EnterpriseCustomerUser.objects.create(
user_id=self.user.id,
enterprise_customer=enterprise_customer
self.mock_discount_filter.side_effect = DiscountEligibilityCheckRequested.DiscountIneligible(
message="test: user is ineligible for discount"
)

applicability = can_receive_discount(user=self.user, course=self.course)
Expand Down Expand Up @@ -180,3 +189,17 @@ def test_holdback_expiry(self):
Mock(now=Mock(return_value=datetime(2020, 8, 1, 0, 1, tzinfo=ZoneInfo("UTC"))), wraps=datetime),
):
assert not _is_in_holdback_and_bucket(self.user)

@override_waffle_flag(COURSEWARE_MFE_MILESTONES_STREAK_DISCOUNT, active=True)
def test_can_show_streak_discount_coupon_false_when_filter_marks_ineligible(self):
"""
Ensure that when the eligibility filter raises DiscountIneligible,
the streak discount coupon is not shown.
"""
CourseEnrollmentFactory(is_active=True, course_id=self.course.id, user=self.user)
self.mock_discount_filter.side_effect = DiscountEligibilityCheckRequested.DiscountIneligible(
message="test: user is ineligible for streak discount"
)

applicability = can_show_streak_discount_coupon(user=self.user, course=self.course)
assert applicability is False
2 changes: 1 addition & 1 deletion requirements/edx/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -862,7 +862,7 @@ openedx-events==11.2.0
# openedx-authz
# openedx-core
# ora2
openedx-filters==3.5.0
openedx-filters==3.7.0
# via
# -r requirements/edx/kernel.in
# edx-enterprise
Expand Down
2 changes: 1 addition & 1 deletion requirements/edx/development.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1414,7 +1414,7 @@ openedx-events==11.2.0
# openedx-authz
# openedx-core
# ora2
openedx-filters==3.5.0
openedx-filters==3.7.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
Expand Down
2 changes: 1 addition & 1 deletion requirements/edx/doc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1042,7 +1042,7 @@ openedx-events==11.2.0
# openedx-authz
# openedx-core
# ora2
openedx-filters==3.5.0
openedx-filters==3.7.0
# via
# -r requirements/edx/base.txt
# edx-enterprise
Expand Down
2 changes: 1 addition & 1 deletion requirements/edx/testing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1082,7 +1082,7 @@ openedx-events==11.2.0
# openedx-authz
# openedx-core
# ora2
openedx-filters==3.5.0
openedx-filters==3.7.0
# via
# -r requirements/edx/base.txt
# edx-enterprise
Expand Down
Loading