Commit ab52966f by Clinton Blackburn Committed by Clinton Blackburn

Updated Enrollment API to always store enrollment attributes

Enrollment attributes are now always stored, regardless of whether an enrollment is being created or updated. This will ensure that learners who purchase paid modes, without first enrolling in a non-paid mode, can request refunds if they choose to un-enroll from a course run within the refund window.

LEARNER-1282
parent f95c30af
...@@ -142,7 +142,7 @@ def get_enrollment(user_id, course_id): ...@@ -142,7 +142,7 @@ def get_enrollment(user_id, course_id):
return _data_api().get_course_enrollment(user_id, course_id) return _data_api().get_course_enrollment(user_id, course_id)
def add_enrollment(user_id, course_id, mode=None, is_active=True): def add_enrollment(user_id, course_id, mode=None, is_active=True, enrollment_attributes=None):
"""Enrolls a user in a course. """Enrolls a user in a course.
Enrolls a user in a course. If the mode is not specified, this will default to `CourseMode.DEFAULT_MODE_SLUG`. Enrolls a user in a course. If the mode is not specified, this will default to `CourseMode.DEFAULT_MODE_SLUG`.
...@@ -150,12 +150,11 @@ def add_enrollment(user_id, course_id, mode=None, is_active=True): ...@@ -150,12 +150,11 @@ def add_enrollment(user_id, course_id, mode=None, is_active=True):
Arguments: Arguments:
user_id (str): The user to enroll. user_id (str): The user to enroll.
course_id (str): The course to enroll the user in. course_id (str): The course to enroll the user in.
Keyword Arguments:
mode (str): Optional argument for the type of enrollment to create. Ex. 'audit', 'honor', 'verified', mode (str): Optional argument for the type of enrollment to create. Ex. 'audit', 'honor', 'verified',
'professional'. If not specified, this defaults to the default course mode. 'professional'. If not specified, this defaults to the default course mode.
is_active (boolean): Optional argument for making the new enrollment inactive. If not specified, is_active is_active (boolean): Optional argument for making the new enrollment inactive. If not specified, is_active
defaults to True. defaults to True.
enrollment_attributes (list): Attributes to be set the enrollment.
Returns: Returns:
A serializable dictionary of the new course enrollment. A serializable dictionary of the new course enrollment.
...@@ -194,7 +193,12 @@ def add_enrollment(user_id, course_id, mode=None, is_active=True): ...@@ -194,7 +193,12 @@ def add_enrollment(user_id, course_id, mode=None, is_active=True):
if mode is None: if mode is None:
mode = _default_course_mode(course_id) mode = _default_course_mode(course_id)
validate_course_mode(course_id, mode, is_active=is_active) validate_course_mode(course_id, mode, is_active=is_active)
return _data_api().create_course_enrollment(user_id, course_id, mode, is_active) enrollment = _data_api().create_course_enrollment(user_id, course_id, mode, is_active)
if enrollment_attributes is not None:
set_enrollment_attributes(user_id, course_id, enrollment_attributes)
return enrollment
def update_enrollment(user_id, course_id, mode=None, is_active=None, enrollment_attributes=None, include_expired=False): def update_enrollment(user_id, course_id, mode=None, is_active=None, enrollment_attributes=None, include_expired=False):
......
...@@ -19,6 +19,8 @@ from mock import patch ...@@ -19,6 +19,8 @@ from mock import patch
from nose.plugins.attrib import attr from nose.plugins.attrib import attr
from rest_framework import status from rest_framework import status
from rest_framework.test import APITestCase from rest_framework.test import APITestCase
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, check_mongo_calls_range
from course_modes.models import CourseMode from course_modes.models import CourseMode
from enrollment import api from enrollment import api
...@@ -35,8 +37,6 @@ from student.roles import CourseStaffRole ...@@ -35,8 +37,6 @@ from student.roles import CourseStaffRole
from student.tests.factories import AdminFactory, CourseModeFactory, UserFactory from student.tests.factories import AdminFactory, CourseModeFactory, UserFactory
from util.models import RateLimitConfiguration from util.models import RateLimitConfiguration
from util.testing import UrlResetMixin from util.testing import UrlResetMixin
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, check_mongo_calls_range
class EnrollmentTestMixin(object): class EnrollmentTestMixin(object):
...@@ -997,6 +997,55 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase, Ente ...@@ -997,6 +997,55 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase, Ente
'No request was made to the mocked enterprise-course-enrollment API' 'No request was made to the mocked enterprise-course-enrollment API'
) )
def test_enrollment_attributes_always_written(self):
""" Enrollment attributes should always be written, regardless of whether
the enrollment is being created or updated.
"""
course_key = self.course.id
for mode in [CourseMode.DEFAULT_MODE_SLUG, CourseMode.VERIFIED]:
CourseModeFactory.create(
course_id=course_key,
mode_slug=mode,
mode_display_name=mode,
)
# Creating a new enrollment should write attributes
order_number = 'EDX-1000'
enrollment_attributes = [{
'namespace': 'order',
'name': 'order_number',
'value': order_number,
}]
mode = CourseMode.VERIFIED
self.assert_enrollment_status(
as_server=True,
is_active=True,
mode=mode,
enrollment_attributes=enrollment_attributes
)
enrollment = CourseEnrollment.objects.get(user=self.user, course_id=course_key)
self.assertTrue(enrollment.is_active)
self.assertEqual(enrollment.mode, CourseMode.VERIFIED)
self.assertEqual(enrollment.attributes.get(namespace='order', name='order_number').value, order_number)
# Updating an enrollment should update attributes
order_number = 'EDX-2000'
enrollment_attributes = [{
'namespace': 'order',
'name': 'order_number',
'value': order_number,
}]
mode = CourseMode.DEFAULT_MODE_SLUG
self.assert_enrollment_status(
as_server=True,
mode=mode,
enrollment_attributes=enrollment_attributes
)
enrollment.refresh_from_db()
self.assertTrue(enrollment.is_active)
self.assertEqual(enrollment.mode, mode)
self.assertEqual(enrollment.attributes.get(namespace='order', name='order_number').value, order_number)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class EnrollmentEmbargoTest(EnrollmentTestMixin, UrlResetMixin, ModuleStoreTestCase): class EnrollmentEmbargoTest(EnrollmentTestMixin, UrlResetMixin, ModuleStoreTestCase):
......
...@@ -641,7 +641,13 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn): ...@@ -641,7 +641,13 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
) )
else: else:
# Will reactivate inactive enrollments. # Will reactivate inactive enrollments.
response = api.add_enrollment(username, unicode(course_id), mode=mode, is_active=is_active) response = api.add_enrollment(
username,
unicode(course_id),
mode=mode,
is_active=is_active,
enrollment_attributes=enrollment_attributes
)
email_opt_in = request.data.get('email_opt_in', None) email_opt_in = request.data.get('email_opt_in', None)
if email_opt_in is not None: if email_opt_in is not None:
......
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