diff --git a/common/djangoapps/course_modes/tests/test_views.py b/common/djangoapps/course_modes/tests/test_views.py
index 4f92311ebb17..8482c102ec7b 100644
--- a/common/djangoapps/course_modes/tests/test_views.py
+++ b/common/djangoapps/course_modes/tests/test_views.py
@@ -136,7 +136,7 @@ def test_no_id_redirect_otto(self):
prof_course = CourseFactory.create()
CourseModeFactory(mode_slug=CourseMode.NO_ID_PROFESSIONAL_MODE, course_id=prof_course.id,
min_price=100, sku='TEST', bulk_sku="BULKTEST")
- ecomm_test_utils.update_commerce_config()
+ ecomm_test_utils.update_commerce_config(enabled=True)
# Enroll the user in the test course
CourseEnrollmentFactory(
is_active=False,
@@ -148,6 +148,7 @@ def test_no_id_redirect_otto(self):
url = reverse('course_modes_choose', args=[str(prof_course.id)])
response = self.client.get(url)
self.assertRedirects(response, 'http://payment-mfe?sku=TEST', fetch_redirect_response=False)
+ ecomm_test_utils.update_commerce_config(enabled=False)
@httpretty.activate
@ddt.data(
diff --git a/lms/djangoapps/commerce/management/commands/configure_commerce.py b/lms/djangoapps/commerce/management/commands/configure_commerce.py
index 592ebab08858..fda5dce431a5 100644
--- a/lms/djangoapps/commerce/management/commands/configure_commerce.py
+++ b/lms/djangoapps/commerce/management/commands/configure_commerce.py
@@ -27,6 +27,11 @@ class Command(BaseCommand):
help = 'Enable/Disable commerce configuration, including configuration of E-Commerce checkout.'
def add_arguments(self, parser):
+ parser.add_argument('--disable-checkout-on-ecommerce',
+ dest='checkout_on_ecommerce',
+ action='store_false',
+ default=True,
+ help='Do not checkout to E-Commerce even when configuration is enabled.')
parser.add_argument('--disable',
dest='disable',
action='store_true',
@@ -42,14 +47,22 @@ def handle(self, *args, **options):
options:
disable (bool): if True then disable configuration, enable otherwise
+ checkout_on_ecommerce (bool): Enable E-Commerce checkout if True, disable otherwise.
"""
disable = options.get('disable')
+ checkout_on_ecommerce = options.get('checkout_on_ecommerce')
# We are keeping id=1, because as of now, there are only one commerce configuration for the system.
CommerceConfiguration.objects.update_or_create(
id=1,
defaults={
'enabled': not disable,
+ 'checkout_on_ecommerce_service': checkout_on_ecommerce,
}
)
- logger.info(f'Commerce Configuration {"disabled" if disable else "enabled"}.')
+ logger.info(
+ 'Commerce Configuration {configuration_status} with checkout on ecommerce {checkout_status}.'.format(
+ configuration_status="disabled" if disable else "enabled",
+ checkout_status="enabled" if checkout_on_ecommerce else "disabled",
+ ),
+ )
diff --git a/lms/djangoapps/commerce/management/commands/tests/test_configure_commerce.py b/lms/djangoapps/commerce/management/commands/tests/test_configure_commerce.py
index 01b77bbf5f26..3faad323f7a1 100644
--- a/lms/djangoapps/commerce/management/commands/tests/test_configure_commerce.py
+++ b/lms/djangoapps/commerce/management/commands/tests/test_configure_commerce.py
@@ -38,6 +38,15 @@ def test_commerce_configuration(self):
commerce_configuration = CommerceConfiguration.current()
assert not commerce_configuration.enabled
+ # Verify commerce configuration can be disabled from command
+ call_command(
+ "configure_commerce",
+ '--disable-checkout-on-ecommerce',
+ )
+
+ commerce_configuration = CommerceConfiguration.current()
+ assert not commerce_configuration.checkout_on_ecommerce_service
+
def test_site_associated_commerce_configuration(self):
"""
This test is added here to fail when site_id field is added.
diff --git a/lms/djangoapps/commerce/models.py b/lms/djangoapps/commerce/models.py
index 22c7d9d7173d..f9d950482df7 100644
--- a/lms/djangoapps/commerce/models.py
+++ b/lms/djangoapps/commerce/models.py
@@ -24,6 +24,11 @@ class Meta:
DEFAULT_RECEIPT_PAGE_URL = '/checkout/receipt/?order_number='
DEFAULT_ORDER_DASHBOARD_URL = '/dashboard/orders/'
+ checkout_on_ecommerce_service = models.BooleanField(
+ default=False,
+ help_text=_('Use the checkout page hosted by the E-Commerce service.')
+ )
+
basket_checkout_page = models.CharField(
max_length=255,
default='http://localhost:1998',
diff --git a/lms/djangoapps/commerce/tests/__init__.py b/lms/djangoapps/commerce/tests/__init__.py
index d932f5acba6b..06787afb69c6 100644
--- a/lms/djangoapps/commerce/tests/__init__.py
+++ b/lms/djangoapps/commerce/tests/__init__.py
@@ -13,7 +13,6 @@
from openedx.core.djangoapps.oauth_dispatch.jwt import create_jwt_for_user
JSON = 'application/json'
-TEST_PUBLIC_URL_ROOT = 'http://www.example.com'
TEST_API_URL = 'http://www-internal.example.com/api'
TEST_BASKET_ID = 7
TEST_ORDER_NUMBER = '100004'
diff --git a/lms/djangoapps/commerce/tests/test_utils.py b/lms/djangoapps/commerce/tests/test_utils.py
index e21d95f438d3..7c57d13a3bc6 100644
--- a/lms/djangoapps/commerce/tests/test_utils.py
+++ b/lms/djangoapps/commerce/tests/test_utils.py
@@ -30,9 +30,10 @@
from common.djangoapps.entitlements.tests.factories import CourseEntitlementFactory
-def update_commerce_config(checkout_page='http://payment-mfe'):
+def update_commerce_config(enabled=False, checkout_page='http://payment-mfe'):
""" Enable / Disable CommerceConfiguration model """
CommerceConfiguration.objects.create(
+ checkout_on_ecommerce_service=enabled,
basket_checkout_page=checkout_page,
)
@@ -59,7 +60,7 @@ def setUp(self):
self.request_factory = RequestFactory()
self.user = UserFactory.create()
self.request = self.request_factory.get("foo")
- update_commerce_config()
+ update_commerce_config(enabled=True)
super().setUp()
def test_is_enabled(self):
@@ -67,6 +68,12 @@ def test_is_enabled(self):
is_enabled = EcommerceService().is_enabled(self.user)
assert is_enabled
+ config = CommerceConfiguration.current()
+ config.checkout_on_ecommerce_service = False
+ config.save()
+ is_not_enabled = EcommerceService().is_enabled(self.user)
+ assert not is_not_enabled
+
@override_switch(settings.DISABLE_ACCOUNT_ACTIVATION_REQUIREMENT_SWITCH, active=True)
def test_is_enabled_activation_requirement_disabled(self):
"""Verify that is_enabled() returns True when ecomm checkout is enabled. """
diff --git a/lms/djangoapps/commerce/utils.py b/lms/djangoapps/commerce/utils.py
index d4cb20503685..be5a2ab2947a 100644
--- a/lms/djangoapps/commerce/utils.py
+++ b/lms/djangoapps/commerce/utils.py
@@ -90,7 +90,7 @@ def is_enabled(self, user):
"""
user_is_active = user.is_active or is_account_activation_requirement_disabled()
allow_user = user_is_active or user.is_anonymous
- return allow_user
+ return allow_user and self.config.checkout_on_ecommerce_service
def payment_page_url(self):
""" Return the URL for the checkout page.
diff --git a/lms/djangoapps/course_home_api/outline/tests/test_view.py b/lms/djangoapps/course_home_api/outline/tests/test_view.py
index 7324ce7c9733..c37788e9eec6 100644
--- a/lms/djangoapps/course_home_api/outline/tests/test_view.py
+++ b/lms/djangoapps/course_home_api/outline/tests/test_view.py
@@ -19,7 +19,6 @@
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.roles import CourseInstructorRole
from common.djangoapps.student.tests.factories import UserFactory
-from lms.djangoapps.commerce.tests.test_utils import update_commerce_config
from lms.djangoapps.course_home_api.tests.utils import BaseCourseHomeTests
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.content.learning_sequences.api import replace_course_outline
@@ -350,7 +349,6 @@ def test_can_show_upgrade_sock(self, sock_enabled):
assert response.data['can_show_upgrade_sock'] == sock_enabled
def test_verified_mode(self):
- update_commerce_config()
enrollment = CourseEnrollment.enroll(self.user, self.course.id)
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
@@ -361,7 +359,7 @@ def test_verified_mode(self):
'currency_symbol': '$',
'price': 149,
'sku': 'ABCD1234',
- 'upgrade_url': 'http://payment-mfe?sku=ABCD1234'
+ 'upgrade_url': '/dashboard'
}
def test_hide_learning_sequences(self):
diff --git a/lms/djangoapps/course_home_api/progress/tests/test_views.py b/lms/djangoapps/course_home_api/progress/tests/test_views.py
index 8aaebafa9922..4c330c7cc306 100644
--- a/lms/djangoapps/course_home_api/progress/tests/test_views.py
+++ b/lms/djangoapps/course_home_api/progress/tests/test_views.py
@@ -17,7 +17,6 @@
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.tests.factories import UserFactory
-from lms.djangoapps.commerce.tests.test_utils import update_commerce_config
from lms.djangoapps.course_home_api.tests.utils import BaseCourseHomeTests
from lms.djangoapps.course_home_api.models import DisableProgressPageStackedConfig
from lms.djangoapps.course_home_api.toggles import COURSE_HOME_MICROFRONTEND_PROGRESS_TAB
@@ -147,20 +146,14 @@ def test_user_has_passing_grade(self):
assert response.json()['user_has_passing_grade']
def test_verified_mode(self):
- update_commerce_config()
enrollment = CourseEnrollment.enroll(self.user, self.course.id)
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
response = self.client.get(self.url)
assert response.status_code == 200
- assert response.data['verified_mode'] == {
- 'access_expiration_date': (enrollment.created + MIN_DURATION),
- 'currency': 'USD',
- 'currency_symbol': '$',
- 'price': 149,
- 'sku': 'ABCD1234',
- 'upgrade_url': 'http://payment-mfe?sku=ABCD1234'
- }
+ assert response.data['verified_mode'] == {'access_expiration_date': (enrollment.created + MIN_DURATION),
+ 'currency': 'USD', 'currency_symbol': '$', 'price': 149,
+ 'sku': 'ABCD1234', 'upgrade_url': '/dashboard'}
def test_page_respects_stacked_config(self):
CourseEnrollment.enroll(self.user, self.course.id)
diff --git a/lms/djangoapps/courseware/tests/test_about.py b/lms/djangoapps/courseware/tests/test_about.py
index 218d0c5207f3..244a5fd2b390 100644
--- a/lms/djangoapps/courseware/tests/test_about.py
+++ b/lms/djangoapps/courseware/tests/test_about.py
@@ -279,7 +279,7 @@ def test_enrollment_cap(self):
self.setup_user()
url = reverse('about_course', args=[str(self.course.id)])
resp = self.client.get(url)
- self.assertContains(resp, '')
+ self.assertContains(resp, '')
self.enroll(self.course, verify=True)
diff --git a/lms/djangoapps/courseware/tests/test_date_summary.py b/lms/djangoapps/courseware/tests/test_date_summary.py
index 4c198494d080..741a02bea425 100644
--- a/lms/djangoapps/courseware/tests/test_date_summary.py
+++ b/lms/djangoapps/courseware/tests/test_date_summary.py
@@ -465,7 +465,7 @@ def test_course_end_date_self_paced(self, days_till_end):
def test_ecommerce_checkout_redirect(self):
"""Verify the block link redirects to ecommerce checkout if it's enabled."""
sku = 'TESTSKU'
- configuration = CommerceConfiguration.objects.create()
+ configuration = CommerceConfiguration.objects.create(checkout_on_ecommerce_service=True)
course = create_course_run()
user = create_user()
course_mode = CourseMode.objects.get(course_id=course.id, mode_slug=CourseMode.VERIFIED)
diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py
index a8ef63c7c6dc..b84f51b3a445 100644
--- a/lms/djangoapps/courseware/tests/test_views.py
+++ b/lms/djangoapps/courseware/tests/test_views.py
@@ -16,6 +16,7 @@
from completion.test_utils import CompletionWaffleTestMixin
from crum import set_current_request
from django.conf import settings
+from django.contrib.auth.models import AnonymousUser
from django.http import Http404, HttpResponse, HttpResponseBadRequest
from django.http.request import QueryDict
from django.test import RequestFactory, TestCase
@@ -65,6 +66,7 @@
GeneratedCertificateFactory
)
from lms.djangoapps.commerce.models import CommerceConfiguration
+from lms.djangoapps.commerce.utils import EcommerceService
from lms.djangoapps.courseware.access_utils import check_course_open_for_learner
from lms.djangoapps.courseware.model_data import FieldDataCache, set_score
from lms.djangoapps.courseware.module_render import get_module, handle_xblock_callback
@@ -645,7 +647,7 @@ def assert_enrollment_link_present(self, is_anonymous):
_id(bool): Tell the method to either expect an id in the href or not.
"""
sku = 'TEST123'
- configuration = CommerceConfiguration.objects.create()
+ configuration = CommerceConfiguration.objects.create(checkout_on_ecommerce_service=True)
course = CourseFactory.create()
CourseModeFactory(mode_slug=CourseMode.PROFESSIONAL, course_id=course.id, sku=sku, min_price=1)
@@ -668,7 +670,10 @@ def assert_enrollment_link_present(self, is_anonymous):
@ddt.data(True, False)
def test_ecommerce_checkout(self, is_anonymous):
- self.assert_enrollment_link_present(is_anonymous=is_anonymous)
+ if not is_anonymous:
+ self.assert_enrollment_link_present(is_anonymous=is_anonymous)
+ else:
+ assert EcommerceService().is_enabled(AnonymousUser()) is False
def test_user_groups(self):
# deprecated function
diff --git a/lms/djangoapps/verify_student/tests/test_views.py b/lms/djangoapps/verify_student/tests/test_views.py
index 6d306e60545a..77a6cac497a5 100644
--- a/lms/djangoapps/verify_student/tests/test_views.py
+++ b/lms/djangoapps/verify_student/tests/test_views.py
@@ -31,7 +31,7 @@
from common.djangoapps.util.testing import UrlResetMixin
from common.test.utils import MockS3BotoMixin, XssTestMixin
from lms.djangoapps.commerce.models import CommerceConfiguration
-from lms.djangoapps.commerce.tests import TEST_API_URL, TEST_PAYMENT_DATA, TEST_PUBLIC_URL_ROOT
+from lms.djangoapps.commerce.tests import TEST_API_URL, TEST_PAYMENT_DATA
from lms.djangoapps.commerce.tests.mocks import mock_payment_processors
from lms.djangoapps.commerce.utils import EcommerceService
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification, VerificationDeadline
@@ -187,8 +187,8 @@ def test_start_flow_with_ecommerce(self):
sku = 'TESTSKU'
# When passing a SKU ecommerce api gets called.
_mock_payment_processors()
- checkout_page = 'http://localhost:1998'
- CommerceConfiguration.objects.create(basket_checkout_page=checkout_page)
+ checkout_page = 'http://payment-mfe'
+ CommerceConfiguration.objects.create(checkout_on_ecommerce_service=True, basket_checkout_page=checkout_page)
checkout_page += "?utm_source=test"
httpretty.register_uri(httpretty.GET, f"{checkout_page}")
@@ -196,10 +196,10 @@ def test_start_flow_with_ecommerce(self):
self._enroll(course.id)
# Verify that utm params are included in the url used for redirect
- url_with_utm = 'http://localhost:1998/?utm_source=test&sku=TESTSKU'
+ url_with_utm = 'http://payment-mfe?utm_source=test&sku=TESTSKU'
with mock.patch.object(EcommerceService, 'get_checkout_page_url', return_value=url_with_utm):
response = self._get_page('verify_student_start_flow', course.id, expected_status_code=302)
- expected_page = f'{TEST_PUBLIC_URL_ROOT}{checkout_page}&sku={sku}'
+ expected_page = f'{checkout_page}&sku={sku}'
self.assertRedirects(response, expected_page, fetch_redirect_response=False)
@ddt.data(
diff --git a/openedx/core/djangoapps/programs/tests/test_utils.py b/openedx/core/djangoapps/programs/tests/test_utils.py
index e2395ad10b8e..63da11ae04f3 100644
--- a/openedx/core/djangoapps/programs/tests/test_utils.py
+++ b/openedx/core/djangoapps/programs/tests/test_utils.py
@@ -928,7 +928,7 @@ def test_is_enrollment_open(self, days_offset):
def test_student_enrollment_status(self, is_enrolled, enrolled_mode, is_upgrade_required, mock_get_mode):
"""Verify that program data is supplemented with the student's enrollment status."""
expected_upgrade_url = f'{self.checkout_path}?sku={self.sku}'
- update_commerce_config(checkout_page=self.checkout_path)
+ update_commerce_config(enabled=True, checkout_page=self.checkout_path)
mock_mode = mock.Mock()
mock_mode.sku = self.sku
@@ -950,7 +950,7 @@ def test_inactive_enrollment_no_upgrade(self, enrolled_mode):
"""
Verify that a student with an inactive enrollment isn't encouraged to upgrade.
"""
- update_commerce_config(checkout_page=self.checkout_path)
+ update_commerce_config(enabled=True, checkout_page=self.checkout_path)
CourseEnrollmentFactory(
user=self.user,
@@ -963,6 +963,23 @@ def test_inactive_enrollment_no_upgrade(self, enrolled_mode):
self._assert_supplemented(data)
+ @mock.patch(UTILS_MODULE + '.CourseMode.mode_for_course')
+ def test_ecommerce_disabled(self, mock_get_mode):
+ """
+ Verify that the utility can operate when the ecommerce service is disabled.
+ """
+ update_commerce_config(enabled=False, checkout_page=self.checkout_path)
+
+ mock_mode = mock.Mock()
+ mock_mode.sku = self.sku
+ mock_get_mode.return_value = mock_mode
+
+ CourseEnrollmentFactory(user=self.user, course_id=self.course.id, mode=MODES.audit)
+
+ data = ProgramDataExtender(self.program, self.user).extend()
+
+ self._assert_supplemented(data, is_enrolled=True, upgrade_url=None)
+
@ddt.data(
(1, 1, False),
(1, -1, True),
diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/send_email_base.py b/openedx/core/djangoapps/schedules/management/commands/tests/send_email_base.py
index 1a99435588d4..774f1f418124 100644
--- a/openedx/core/djangoapps/schedules/management/commands/tests/send_email_base.py
+++ b/openedx/core/djangoapps/schedules/management/commands/tests/send_email_base.py
@@ -99,7 +99,7 @@ def setUp(self):
ScheduleConfigFactory.create(site=self.site_config.site)
DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True)
- CommerceConfiguration.objects.create()
+ CommerceConfiguration.objects.create(checkout_on_ecommerce_service=True)
self._courses_with_verified_modes = set()
diff --git a/openedx/core/djangoapps/theming/management/commands/create_sites_and_configurations.py b/openedx/core/djangoapps/theming/management/commands/create_sites_and_configurations.py
index e14e5ede4733..9f9e7661395b 100644
--- a/openedx/core/djangoapps/theming/management/commands/create_sites_and_configurations.py
+++ b/openedx/core/djangoapps/theming/management/commands/create_sites_and_configurations.py
@@ -137,7 +137,8 @@ def _enable_commerce_configuration(self):
Enable the commerce configuration.
"""
CommerceConfiguration.objects.get_or_create(
- enabled=True
+ enabled=True,
+ checkout_on_ecommerce_service=True
)
def _update_default_clients(self):
diff --git a/openedx/features/course_duration_limits/tests/test_access.py b/openedx/features/course_duration_limits/tests/test_access.py
index 93e8c07560c2..558afec22ab3 100644
--- a/openedx/features/course_duration_limits/tests/test_access.py
+++ b/openedx/features/course_duration_limits/tests/test_access.py
@@ -15,7 +15,6 @@
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
from common.djangoapps.util.date_utils import strftime_localized
-from lms.djangoapps.commerce.tests.test_utils import update_commerce_config
from lms.djangoapps.courseware.models import DynamicUpgradeDeadlineConfiguration
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
from openedx.core.djangoapps.schedules.models import Schedule
@@ -47,7 +46,6 @@ def assertDateInMessage(self, date, message): # lint-amnesty, pylint: disable=m
assert 'data-datetime="%s"' % date.isoformat() in message
def test_get_access_expiration_data(self):
- update_commerce_config()
enrollment = CourseEnrollmentFactory()
overview = enrollment.course
user = enrollment.user
@@ -73,7 +71,7 @@ def test_get_access_expiration_data(self):
'expiration_date': expiration_date,
'masquerading_expired_course': False,
'upgrade_deadline': upgrade_deadline,
- 'upgrade_url': 'http://payment-mfe?sku=None'
+ 'upgrade_url': '/dashboard'
}
@ddt.data(
diff --git a/openedx/features/course_experience/tests/views/test_course_sock.py b/openedx/features/course_experience/tests/views/test_course_sock.py
index 0e42475380fb..cdc80168ab1c 100644
--- a/openedx/features/course_experience/tests/views/test_course_sock.py
+++ b/openedx/features/course_experience/tests/views/test_course_sock.py
@@ -57,7 +57,7 @@ def setUp(self):
user=self.user, course_id=self.verified_course_already_enrolled.id, mode=CourseMode.VERIFIED
)
- CommerceConfiguration.objects.create(enabled=True)
+ CommerceConfiguration.objects.create(enabled=True, checkout_on_ecommerce_service=True)
# Log the user in
self.client.login(username=self.user.username, password=TEST_PASSWORD)
diff --git a/openedx/features/discounts/tests/test_utils.py b/openedx/features/discounts/tests/test_utils.py
index 802681662e82..1ff305831d74 100644
--- a/openedx/features/discounts/tests/test_utils.py
+++ b/openedx/features/discounts/tests/test_utils.py
@@ -1,7 +1,7 @@
"""
Tests of the openedx.features.discounts.utils module.
"""
-from unittest.mock import Mock, patch
+from unittest.mock import patch, Mock
import ddt
from django.contrib.auth.models import AnonymousUser
@@ -13,7 +13,6 @@
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.tests.factories import UserFactory
-from lms.djangoapps.commerce.tests.test_utils import update_commerce_config
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
from openedx.features.discounts.applicability import DISCOUNT_APPLICABILITY_FLAG, get_discount_expiration_date
@@ -72,14 +71,13 @@ def setUp(self):
CourseEnrollment.enroll(self.user, self.overview.id, CourseMode.AUDIT)
def test_happy_path(self):
- update_commerce_config()
assert utils.generate_offer_data(self.user, self.overview) == {
'code': 'EDXWELCOME',
'expiration_date': get_discount_expiration_date(self.user, self.overview),
'original_price': '$149',
'discounted_price': '$126.65',
'percentage': 15,
- 'upgrade_url': 'http://payment-mfe?sku=None'
+ 'upgrade_url': '/dashboard'
}
def test_spanish_code(self):