diff --git a/common/djangoapps/student/tests/tests.py b/common/djangoapps/student/tests/tests.py index be13a90e8b7539f83cc8164e5b99d634432d76c2..1fc4c6c45adc04b2426e0c798e5bd7afe69bf74e 100644 --- a/common/djangoapps/student/tests/tests.py +++ b/common/djangoapps/student/tests/tests.py @@ -26,7 +26,6 @@ from six import text_type from six.moves import range from six.moves.urllib.parse import quote -import shoppingcart from bulk_email.models import Optout from course_modes.models import CourseMode from course_modes.tests.factories import CourseModeFactory @@ -390,74 +389,6 @@ class DashboardTest(ModuleStoreTestCase, TestVerificationBase): self.assertFalse(course_mode_info['show_upsell']) self.assertIsNone(course_mode_info['days_for_upsell']) - @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') - @patch('lms.djangoapps.courseware.views.index.log.warning') - @patch.dict('django.conf.settings.FEATURES', {'ENABLE_PAID_COURSE_REGISTRATION': True}) - def test_blocked_course_scenario(self, log_warning): - - self.client.login(username="jack", password="test") - - #create testing invoice 1 - sale_invoice_1 = shoppingcart.models.Invoice.objects.create( - total_amount=1234.32, company_name='Test1', company_contact_name='Testw', - company_contact_email='test1@test.com', customer_reference_number='2Fwe23S', - recipient_name='Testw_1', recipient_email='test2@test.com', internal_reference="A", - course_id=self.course.id, is_valid=False - ) - invoice_item = shoppingcart.models.CourseRegistrationCodeInvoiceItem.objects.create( - invoice=sale_invoice_1, - qty=1, - unit_price=1234.32, - course_id=self.course.id - ) - course_reg_code = shoppingcart.models.CourseRegistrationCode( - code="abcde", - course_id=self.course.id, - created_by=self.user, - invoice=sale_invoice_1, - invoice_item=invoice_item, - mode_slug=CourseMode.DEFAULT_MODE_SLUG - ) - course_reg_code.save() - - cart = shoppingcart.models.Order.get_cart_for_user(self.user) - shoppingcart.models.PaidCourseRegistration.add_to_order(cart, self.course.id) - resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': course_reg_code.code}) - self.assertEqual(resp.status_code, 200) - - redeem_url = reverse('register_code_redemption', args=[course_reg_code.code]) - response = self.client.get(redeem_url) - self.assertEqual(response.status_code, 200) - # check button text - self.assertContains(response, 'Activate Course Enrollment') - - #now activate the user by enrolling him/her to the course - response = self.client.post(redeem_url) - self.assertEqual(response.status_code, 200) - response = self.client.get(reverse('dashboard')) - self.assertContains(response, 'You can no longer access this course because payment has not yet been received') - optout_object = Optout.objects.filter(user=self.user, course_id=self.course.id) - self.assertEqual(len(optout_object), 1) - - # Direct link to course redirect to user dashboard - self.client.get(reverse('courseware', kwargs={"course_id": text_type(self.course.id)})) - log_warning.assert_called_with( - u'User %s cannot access the course %s because payment has not yet been received', - self.user, - text_type(self.course.id), - ) - - # Now re-validating the invoice - invoice = shoppingcart.models.Invoice.objects.get(id=sale_invoice_1.id) - invoice.is_valid = True - invoice.save() - - response = self.client.get(reverse('dashboard')) - self.assertNotContains( - response, - 'You can no longer access this course because payment has not yet been received', - ) - @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') def test_linked_in_add_to_profile_btn_not_appearing_without_config(self): # Without linked-in config don't show Add Certificate to LinkedIn button diff --git a/lms/djangoapps/courseware/tests/test_about.py b/lms/djangoapps/courseware/tests/test_about.py index 735a5da47e02b42e617441168700d6542e2425c8..660e1dd81ce52a0c53cac387b24717f52864fae3 100644 --- a/lms/djangoapps/courseware/tests/test_about.py +++ b/lms/djangoapps/courseware/tests/test_about.py @@ -24,7 +24,6 @@ from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag from openedx.features.course_experience import COURSE_ENABLE_UNENROLLED_ACCESS_FLAG from openedx.features.course_experience.waffle import ENABLE_COURSE_ABOUT_SIDEBAR_HTML from openedx.features.course_experience.waffle import WAFFLE_NAMESPACE as COURSE_EXPERIENCE_WAFFLE_NAMESPACE -from shoppingcart.models import Order, PaidCourseRegistration from student.models import CourseEnrollment from student.tests.factories import AdminFactory, CourseEnrollmentAllowedFactory, UserFactory from track.tests import EventTrackingTestCase @@ -464,176 +463,6 @@ class AboutSidebarHTMLTestCase(SharedModuleStoreTestCase): self.assertNotContains(resp, '<section class="about-sidebar-html">') -@patch.dict(settings.FEATURES, {'ENABLE_SHOPPING_CART': True}) -@patch.dict(settings.FEATURES, {'ENABLE_PAID_COURSE_REGISTRATION': True}) -class AboutPurchaseCourseTestCase(LoginEnrollmentTestCase, SharedModuleStoreTestCase): - """ - This test class runs through a suite of verifications regarding - purchaseable courses - """ - @classmethod - def setUpClass(cls): - super(AboutPurchaseCourseTestCase, cls).setUpClass() - cls.course = CourseFactory.create(org='MITx', number='buyme', display_name='Course To Buy') - - now = datetime.datetime.now(pytz.UTC) - tomorrow = now + datetime.timedelta(days=1) - nextday = tomorrow + datetime.timedelta(days=1) - - cls.closed_course = CourseFactory.create( - org='MITx', - number='closed', - display_name='Closed Course To Buy', - enrollment_start=tomorrow, - enrollment_end=nextday - ) - - def setUp(self): - super(AboutPurchaseCourseTestCase, self).setUp() - self._set_ecomm(self.course) - self._set_ecomm(self.closed_course) - - def _set_ecomm(self, course): - """ - Helper method to turn on ecommerce on the course - """ - course_mode = CourseMode( - course_id=course.id, - mode_slug=CourseMode.DEFAULT_MODE_SLUG, - mode_display_name=CourseMode.DEFAULT_MODE_SLUG, - min_price=10, - ) - course_mode.save() - - def test_anonymous_user(self): - """ - Make sure an anonymous user sees the purchase button - """ - url = reverse('about_course', args=[text_type(self.course.id)]) - resp = self.client.get(url) - self.assertContains(resp, "Add buyme to Cart <span>($10 USD)</span>") - - def test_logged_in(self): - """ - Make sure a logged in user sees the purchase button - """ - self.setup_user() - url = reverse('about_course', args=[text_type(self.course.id)]) - resp = self.client.get(url) - self.assertContains(resp, "Add buyme to Cart <span>($10 USD)</span>") - - def test_already_in_cart(self): - """ - This makes sure if a user has this course in the cart, that the expected message - appears - """ - self.setup_user() - cart = Order.get_cart_for_user(self.user) - PaidCourseRegistration.add_to_order(cart, self.course.id) - - url = reverse('about_course', args=[text_type(self.course.id)]) - resp = self.client.get(url) - self.assertContains(resp, "This course is in your") - self.assertNotContains(resp, "Add buyme to Cart <span>($10 USD)</span>") - - def test_already_enrolled(self): - """ - This makes sure that the already enrolled message appears for paywalled courses - """ - self.setup_user() - - # note that we can't call self.enroll here since that goes through - # the Django student views, which doesn't allow for enrollments - # for paywalled courses - CourseEnrollment.enroll(self.user, self.course.id) - - url = reverse('about_course', args=[text_type(self.course.id)]) - - resp = self.client.get(url) - self.assertContains(resp, "You are enrolled in this course") - self.assertContains(resp, "View Course") - self.assertNotContains(resp, "Add buyme to Cart <span>($10 USD)</span>") - - def test_closed_enrollment(self): - """ - This makes sure that paywalled courses also honor the registration - window - """ - self.setup_user() - - url = reverse('about_course', args=[text_type(self.closed_course.id)]) - resp = self.client.get(url) - self.assertContains(resp, "Enrollment is Closed") - self.assertNotContains(resp, "Add closed to Cart <span>($10 USD)</span>") - - # course price is visible ihe course_about page when the course - # mode is set to honor and it's price is set - self.assertContains(resp, '<span class="important-dates-item-text">$10</span>') - - def test_invitation_only(self): - """ - This makes sure that the invitation only restirction takes prescendence over - any purchase enablements - """ - course = CourseFactory.create(metadata={"invitation_only": True}) - self._set_ecomm(course) - self.setup_user() - - url = reverse('about_course', args=[text_type(course.id)]) - resp = self.client.get(url) - self.assertContains(resp, "Enrollment in this course is by invitation only") - - def test_enrollment_cap(self): - """ - Make sure that capped enrollments work even with - paywalled courses - """ - course = CourseFactory.create( - metadata={ - "max_student_enrollments_allowed": 1, - "display_coursenumber": "buyme", - } - ) - self._set_ecomm(course) - - self.setup_user() - url = reverse('about_course', args=[text_type(course.id)]) - resp = self.client.get(url) - self.assertContains(resp, "Add buyme to Cart <span>($10 USD)</span>") - - # note that we can't call self.enroll here since that goes through - # the Django student views, which doesn't allow for enrollments - # for paywalled courses - CourseEnrollment.enroll(self.user, course.id) - - # create a new account since the first account is already enrolled in the course - email = 'foo_second@test.com' - password = 'bar' - username = 'test_second' - self.create_account(username, - email, password) - self.activate_user(email) - self.login(email, password) - - # Get the about page again and make sure that the page says that the course is full - resp = self.client.get(url) - self.assertContains(resp, "Course is full") - self.assertNotContains(resp, "Add buyme to Cart ($10)") - - def test_free_course_display(self): - """ - Make sure other courses that don't have shopping cart enabled don't display the add-to-cart button - and don't display the course_price field if Cosmetic Price is disabled. - """ - course = CourseFactory.create(org='MITx', number='free', display_name='Course For Free') - self.setup_user() - url = reverse('about_course', args=[text_type(course.id)]) - - resp = self.client.get(url) - self.assertNotContains(resp, "Add free to Cart (Free)") - self.assertNotContains(resp, '<p class="important-dates-item-title">Price</p>') - - class CourseAboutTestCaseCCX(SharedModuleStoreTestCase, LoginEnrollmentTestCase): """ Test for unenrolled student tries to access ccx. diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index 5393aa840b25278e6c1dae27fd25d57f1cb4d0d3..8b02f8f2f7d27160bfa91e7c368b2763e5d8cabd 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -38,7 +38,6 @@ from xblock.core import XBlock from xblock.fields import Scope, String import lms.djangoapps.courseware.views.views as views -import shoppingcart from capa.tests.response_xml_factory import MultipleChoiceResponseXMLFactory from course_modes.models import CourseMode @@ -507,31 +506,6 @@ class ViewsTestCase(BaseViewsTestCase): self.assertEqual(response.status_code, 302) self.assertRedirects(response, '/courses/{}/about'.format(six.text_type(self.course_key))) - @unittest.skipUnless(settings.FEATURES.get('ENABLE_SHOPPING_CART'), "Shopping Cart not enabled in settings") - @patch.dict(settings.FEATURES, {'ENABLE_PAID_COURSE_REGISTRATION': True}) - def test_course_about_in_cart(self): - in_cart_span = '<span class="add-to-cart">' - # don't mock this course due to shopping cart existence checking - course = CourseFactory.create(org="new", number="unenrolled", display_name="course") - - self.client.logout() - response = self.client.get(reverse('about_course', args=[six.text_type(course.id)])) - self.assertEqual(response.status_code, 200) - self.assertNotContains(response, in_cart_span) - - # authenticated user with nothing in cart - self.assertTrue(self.client.login(username=self.user.username, password=TEST_PASSWORD)) - response = self.client.get(reverse('about_course', args=[six.text_type(course.id)])) - self.assertEqual(response.status_code, 200) - self.assertNotContains(response, in_cart_span) - - # now add the course to the cart - cart = shoppingcart.models.Order.get_cart_for_user(self.user) - shoppingcart.models.PaidCourseRegistration.add_to_order(cart, course.id) - response = self.client.get(reverse('about_course', args=[six.text_type(course.id)])) - self.assertEqual(response.status_code, 200) - self.assertContains(response, in_cart_span) - def assert_enrollment_link_present(self, is_anonymous): """ Prepare ecommerce checkout data and assert if the ecommerce link is contained in the response. @@ -568,20 +542,6 @@ class ViewsTestCase(BaseViewsTestCase): else: self.assertEqual(EcommerceService().is_enabled(AnonymousUser()), False) - @ddt.data(True, False) - @unittest.skipUnless(settings.FEATURES.get('ENABLE_SHOPPING_CART'), 'Shopping Cart not enabled in settings') - @patch.dict(settings.FEATURES, {'ENABLE_PAID_COURSE_REGISTRATION': True}) - def test_ecommerce_checkout_shopping_cart_enabled(self, is_anonymous): - """ - Two scenarios are being validated here -- authenticated/known user and unauthenticated/anonymous user - For a known user we expect the checkout link to point to Otto in a scenario where the CommerceConfiguration - is active and the course mode is PROFESSIONAL. - """ - if not is_anonymous: - self.assert_enrollment_link_present(is_anonymous=is_anonymous) - else: - self.assertEqual(EcommerceService().is_enabled(AnonymousUser()), False) - def test_user_groups(self): # deprecated function mock_user = MagicMock() diff --git a/lms/djangoapps/courseware/views/views.py b/lms/djangoapps/courseware/views/views.py index d7356e4c476b375e88b6e1a2874820a80eafaf3c..01c79bc316d1802e15c06be0b82d2ec4809061a3 100644 --- a/lms/djangoapps/courseware/views/views.py +++ b/lms/djangoapps/courseware/views/views.py @@ -47,7 +47,6 @@ from rest_framework.throttling import UserRateThrottle from six import text_type from web_fragments.fragment import Fragment -import shoppingcart import survey.views from course_modes.models import CourseMode, get_course_prices from edxmako.shortcuts import marketing_link, render_to_response, render_to_string @@ -121,7 +120,6 @@ from openedx.features.course_experience.views.course_dates import CourseDatesFra from openedx.features.course_experience.waffle import ENABLE_COURSE_ABOUT_SIDEBAR_HTML from openedx.features.course_experience.waffle import waffle as course_experience_waffle from openedx.features.enterprise_support.api import data_sharing_consent_required -from shoppingcart.utils import is_shopping_cart_enabled from student.models import CourseEnrollment, UserTestGroup from track import segment from util.cache import cache, cache_if_anonymous @@ -927,21 +925,6 @@ def course_about(request, course_id): ) or settings.FEATURES.get('ENABLE_LMS_MIGRATION') ) - # Note: this is a flow for payment for course registration, not the Verified Certificate flow. - in_cart = False - reg_then_add_to_cart_link = "" - - _is_shopping_cart_enabled = is_shopping_cart_enabled() - if _is_shopping_cart_enabled: - if request.user.is_authenticated: - cart = shoppingcart.models.Order.get_cart_for_user(request.user) - in_cart = shoppingcart.models.PaidCourseRegistration.contained_in_order(cart, course_key) or \ - shoppingcart.models.CourseRegCodeItem.contained_in_order(cart, course_key) - - reg_then_add_to_cart_link = "{reg_url}?course_id={course_id}&enrollment_action=add_to_cart".format( - reg_url=reverse('register_user'), course_id=six.moves.urllib.parse.quote(str(course_id)) - ) - # If the ecommerce checkout flow is enabled and the mode of the course is # professional or no id professional, we construct links for the enrollment # button to add the course to the ecommerce basket. @@ -964,9 +947,6 @@ def course_about(request, course_id): registration_price, course_price = get_course_prices(course) - # Determine which checkout workflow to use -- LMS shoppingcart or Otto basket - can_add_course_to_cart = _is_shopping_cart_enabled and registration_price and not ecommerce_checkout_link - # Used to provide context to message to student if enrollment not allowed can_enroll = bool(request.user.has_perm(ENROLL_IN_COURSE, course)) invitation_only = course.invitation_only @@ -1007,12 +987,10 @@ def course_about(request, course_id): 'course_target': course_target, 'is_cosmetic_price_enabled': settings.FEATURES.get('ENABLE_COSMETIC_DISPLAY_PRICE'), 'course_price': course_price, - 'in_cart': in_cart, 'ecommerce_checkout': ecommerce_checkout, 'ecommerce_checkout_link': ecommerce_checkout_link, 'ecommerce_bulk_checkout_link': ecommerce_bulk_checkout_link, 'single_paid_mode': single_paid_mode, - 'reg_then_add_to_cart_link': reg_then_add_to_cart_link, 'show_courseware_link': show_courseware_link, 'is_course_full': is_course_full, 'can_enroll': can_enroll, @@ -1022,8 +1000,6 @@ def course_about(request, course_id): # We do not want to display the internal courseware header, which is used when the course is found in the # context. This value is therefor explicitly set to render the appropriate header. 'disable_courseware_header': True, - 'can_add_course_to_cart': can_add_course_to_cart, - 'cart_link': reverse('shoppingcart.views.show_cart'), 'pre_requisite_courses': pre_requisite_courses, 'course_image_urls': overview.image_urls, 'reviews_fragment_view': reviews_fragment_view, diff --git a/lms/templates/courseware/course_about.html b/lms/templates/courseware/course_about.html index 293084de572adf8e2dea40822feed28fad14d343..8c1ebd83b7d44d2eed89e5313ab0e9d9ae9c7133 100644 --- a/lms/templates/courseware/course_about.html +++ b/lms/templates/courseware/course_about.html @@ -31,32 +31,6 @@ from six import string_types event.preventDefault(); }); - % if can_add_course_to_cart: - add_course_complete_handler = function(jqXHR, textStatus) { - if (jqXHR.status == 200) { - location.href = "${cart_link | n, js_escaped_string}"; - } - if (jqXHR.status == 400) { - $("#register_error") - .text(jqXHR.responseText ? jqXHR.responseText : "${_("An error occurred. Please try again later.") | n, js_escaped_string}") - .css("display", "block"); - } - else if (jqXHR.status == 403) { - location.href = "${reg_then_add_to_cart_link | n, js_escaped_string}"; - } - }; - - $("#add_to_cart_post").click(function(event){ - $.ajax({ - url: "${reverse('add_course_to_cart', args=[text_type(course.id)]) | n, js_escaped_string}", - type: "POST", - /* Using `complete` as promise.done did not work on this page */ - complete: add_course_complete_handler - }) - event.preventDefault(); - }); - % endif - $('#class_enroll_form').on('ajax:complete', function(event, xhr) { if (xhr.status == 200) { if (xhr.responseText == "") { @@ -112,13 +86,6 @@ from six import string_types </a> %endif - %elif in_cart: - <span class="add-to-cart"> - ${Text(_('This course is in your {start_cart_link}cart{end_cart_link}.')).format( - start_cart_link=HTML('<a href="{cart_link}">').format(cart_link=cart_link), - end_cart_link=HTML("</a>"), - )} - </span> % elif is_course_full: <span class="register disabled"> ${_("Course is full")} @@ -130,28 +97,6 @@ from six import string_types ## so that they can register and become a real user that can enroll. % elif not is_shib_course and not can_enroll: <span class="register disabled">${_("Enrollment is Closed")}</span> - %elif can_add_course_to_cart: - <% - if user.is_authenticated: - reg_href = "#" - reg_element_id = "add_to_cart_post" - else: - reg_href = reg_then_add_to_cart_link - reg_element_id = "reg_then_add_to_cart" - %> - <% if ecommerce_checkout: - reg_href = ecommerce_checkout_link - reg_element_id = "" - %> - <a href="${reg_href}" class="add-to-cart" id="${reg_element_id}"> - ${Text(_("Add {course_name} to Cart {start_span}({price} USD){end_span}")).format( - course_name=course.display_number_with_default, - price=course_price, - start_span=HTML("<span>"), - end_span=HTML("</span>"), - )} - </a> - <div id="register_error"></div> %elif allow_anonymous: %if show_courseware_link: <a href="${course_target}"> @@ -264,7 +209,7 @@ from six import string_types ##<li class="important-dates-item"><span class="icon fa fa-clock-o" aria-hidden="true"></span><p class="important-dates-item-title">${_('Course Length')}</p><span class="important-dates-item-text course-length">${_('{number} weeks').format(number=15)}</span></li> - %if course_price and (can_add_course_to_cart or is_cosmetic_price_enabled): + %if course_price and (is_cosmetic_price_enabled): <li class="important-dates-item"> <span class="icon fa fa-money" aria-hidden="true"></span> <p class="important-dates-item-title">${_("Price")}</p>