Commit 5a437b39 by Muhammad Shoaib

WL-168 When redeeming a Registration Code in the shopping cart, the user should…

WL-168 When redeeming a Registration Code in the shopping cart, the user should be redirect to the Registration Code Redemption page

improvements suggested by william
parent 20c356bd
......@@ -289,9 +289,15 @@ class DashboardTest(ModuleStoreTestCase):
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': course_reg_code.code})
self.assertEqual(resp.status_code, 200)
# freely enroll the user into course
resp = self.client.get(reverse('shoppingcart.views.register_courses'))
self.assertIn('success', resp.content)
redeem_url = reverse('register_code_redemption', args=[course_reg_code.code])
response = self.client.get(redeem_url)
self.assertEquals(response.status_code, 200)
# check button text
self.assertTrue('Activate Course Enrollment' in response.content)
#now activate the user by enrolling him/her to the course
response = self.client.post(redeem_url)
self.assertEquals(response.status_code, 200)
response = self.client.get(reverse('dashboard'))
self.assertIn('You can no longer access this course because payment has not yet been received', response.content)
......
......@@ -1862,30 +1862,6 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa
for res in res_json['sale']:
self.validate_sale_records_response(res, course_registration_code, self.sale_invoice_1, 0)
def test_get_sale_records_features_with_used_code(self):
"""
Test that the response from get_sale_records is in json format and using one of the registration codes.
"""
for i in range(5):
course_registration_code = CourseRegistrationCode(
code='qwerty{}'.format(i), course_id=self.course.id.to_deprecated_string(),
created_by=self.instructor, invoice=self.sale_invoice_1
)
course_registration_code.save()
PaidCourseRegistration.add_to_order(self.cart, self.course.id)
# now using registration code
self.client.post(reverse('shoppingcart.views.use_code'), {'code': 'qwerty0'})
url = reverse('get_sale_records', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {})
res_json = json.loads(response.content)
self.assertIn('sale', res_json)
for res in res_json['sale']:
self.validate_sale_records_response(res, course_registration_code, self.sale_invoice_1, 1)
def test_get_sale_records_features_with_multiple_invoices(self):
"""
Test that the response from get_sale_records is in json format for multiple invoices
......@@ -3038,7 +3014,8 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
for i in range(5):
i += 1
registration_code_redemption = RegistrationCodeRedemption(
order_id=i, registration_code_id=i, redeemed_by=self.instructor
registration_code_id=i,
redeemed_by=self.instructor
)
registration_code_redemption.save()
......@@ -3220,7 +3197,8 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
for i in range(9):
i += 13
registration_code_redemption = RegistrationCodeRedemption(
order_id=i, registration_code_id=i, redeemed_by=self.instructor
registration_code_id=i,
redeemed_by=self.instructor
)
registration_code_redemption.save()
......
......@@ -798,8 +798,6 @@ def get_sale_order_records(request, course_id): # pylint: disable=unused-argume
('company_contact_name', 'Company Contact Name'),
('company_contact_email', 'Company Contact Email'),
('total_amount', 'Total Amount'),
('total_codes', 'Total Codes'),
('total_used_codes', 'Total Used Codes'),
('logged_in_username', 'Login Username'),
('logged_in_email', 'Login User Email'),
('purchase_time', 'Date of Sale'),
......@@ -817,8 +815,6 @@ def get_sale_order_records(request, course_id): # pylint: disable=unused-argume
('coupon_code', 'Coupon Code'),
('unit_cost', 'Unit Price'),
('list_price', 'List Price'),
('codes', 'Registration Codes'),
('course_id', 'Course Id')
]
db_columns = [x[0] for x in query_features]
......@@ -1156,7 +1152,7 @@ def generate_registration_codes(request, course_id):
generated_registration_code = save_registration_code(request.user, course_id, sale_invoice, order=None)
registration_codes.append(generated_registration_code)
site_name = microsite.get_value('SITE_NAME', 'localhost')
site_name = microsite.get_value('SITE_NAME', settings.SITE_NAME)
course = get_course_by_id(course_id, depth=None)
course_honor_mode = CourseMode.mode_for_course(course_id, 'honor')
course_price = course_honor_mode.min_price
......
......@@ -60,7 +60,6 @@ def sale_order_record_features(course_id, features):
"""
sale_order_features = [x for x in SALE_ORDER_FEATURES if x in features]
course_reg_features = [x for x in COURSE_REGISTRATION_FEATURES if x in features]
order_item_features = [x for x in ORDER_ITEM_FEATURES if x in features]
# Extracting order information
......@@ -74,9 +73,6 @@ def sale_order_record_features(course_id, features):
sale_order_dict.update({"logged_in_username": purchased_course.order.user.username})
sale_order_dict.update({"logged_in_email": purchased_course.order.user.email})
sale_order_dict.update({"total_codes": 'N/A'})
sale_order_dict.update({'total_used_codes': 'N/A'})
# Extracting OrderItem information of unit_cost, list_price and status
order_item_dict = dict((feature, getattr(purchased_course, feature, None))
for feature in order_item_features)
......@@ -89,22 +85,6 @@ def sale_order_record_features(course_id, features):
order_item_dict.update({'coupon_code': ", ".join(coupon_codes)})
sale_order_dict.update(dict(order_item_dict.items()))
if getattr(purchased_course.order, 'order_type') == OrderTypes.BUSINESS:
registration_codes = CourseRegistrationCode.objects.filter(order=purchased_course.order, course_id=course_id)
sale_order_dict.update({"total_codes": registration_codes.count()})
total_used_codes = RegistrationCodeRedemption.objects.filter(registration_code__in=registration_codes).count()
sale_order_dict.update({'total_used_codes': total_used_codes})
codes = [reg_code.code for reg_code in registration_codes]
# Extracting registration code information
obj_course_reg_code = registration_codes.all()[:1].get()
course_reg_dict = dict((feature, getattr(obj_course_reg_code, feature))
for feature in course_reg_features)
course_reg_dict['course_id'] = course_id.to_deprecated_string()
course_reg_dict.update({'codes': ", ".join(codes)})
sale_order_dict.update(dict(course_reg_dict.items()))
return sale_order_dict
......
......@@ -238,8 +238,6 @@ class TestCourseSaleRecordsAnalyticsBasic(ModuleStoreTestCase):
self.assertEqual(sale_order_record['company_contact_name'], order.company_contact_name)
self.assertEqual(sale_order_record['company_contact_email'], order.company_contact_email)
self.assertEqual(sale_order_record['customer_reference_number'], order.customer_reference_number)
self.assertEqual(sale_order_record['total_used_codes'], order.registrationcoderedemption_set.all().count())
self.assertEqual(sale_order_record['total_codes'], len(CourseRegistrationCode.objects.filter(order=order)))
self.assertEqual(sale_order_record['unit_cost'], item.unit_cost)
self.assertEqual(sale_order_record['list_price'], item.list_price)
self.assertEqual(sale_order_record['status'], item.status)
......@@ -281,7 +279,7 @@ class TestCourseRegistrationCodeAnalyticsBasic(ModuleStoreTestCase):
order.save()
registration_code_redemption = RegistrationCodeRedemption(
order=order, registration_code_id=1, redeemed_by=self.instructor
registration_code_id=1, redeemed_by=self.instructor
)
registration_code_redemption.save()
registration_codes = CourseRegistrationCode.objects.all()
......
......@@ -37,18 +37,6 @@ class MultipleCouponsNotAllowedException(InvalidCartItem):
pass
class RegCodeAlreadyExistException(InvalidCartItem):
pass
class ItemNotAllowedToRedeemRegCodeException(InvalidCartItem):
pass
class ItemDoesNotExistAgainstRegCodeException(InvalidCartItem):
pass
class ReportException(Exception):
pass
......@@ -63,3 +51,7 @@ class InvalidStatusToRetire(Exception):
class UnexpectedOrderItemStatus(Exception):
pass
class ItemNotFoundInCartException(Exception):
pass
......@@ -44,11 +44,9 @@ from .exceptions import (
AlreadyEnrolledInCourseException,
CourseDoesNotExistException,
MultipleCouponsNotAllowedException,
RegCodeAlreadyExistException,
ItemDoesNotExistAgainstRegCodeException,
ItemNotAllowedToRedeemRegCodeException,
InvalidStatusToRetire,
UnexpectedOrderItemStatus,
ItemNotFoundInCartException
)
from microsite_configuration import microsite
......@@ -552,6 +550,23 @@ class Order(models.Model):
for item in self.orderitem_set.all(): # pylint: disable=no-member
item.retire()
def find_item_by_course_id(self, course_id):
"""
course_id: Course id of the item to find
Returns OrderItem from the Order given a course_id
Raises exception ItemNotFoundException when the item
having the given course_id is not present in the cart
"""
cart_items = OrderItem.objects.filter(order=self).select_subclasses()
found_items = []
for item in cart_items:
if getattr(item, 'course_id', None):
if item.course_id == course_id:
found_items.append(item)
if not found_items:
raise ItemNotFoundInCartException
return found_items
class OrderItem(TimeStampedModel):
"""
......@@ -731,25 +746,6 @@ class CourseRegistrationCode(models.Model):
order = models.ForeignKey(Order, db_index=True, null=True, related_name="purchase_order")
invoice = models.ForeignKey(Invoice, null=True)
@classmethod
@transaction.commit_on_success
def free_user_enrollment(cls, cart):
"""
Here we enroll the user free for all courses available in shopping cart
"""
cart_items = cart.orderitem_set.all().select_subclasses()
if cart_items:
for item in cart_items:
CourseEnrollment.enroll(cart.user, item.course_id)
log.info("Enrolled '{0}' in free course '{1}'"
.format(cart.user.email, item.course_id)) # pylint: disable=no-member
item.status = 'purchased'
item.save()
cart.status = 'purchased'
cart.purchase_time = datetime.now(pytz.utc)
cart.save()
class RegistrationCodeRedemption(models.Model):
"""
......@@ -762,47 +758,12 @@ class RegistrationCodeRedemption(models.Model):
course_enrollment = models.ForeignKey(CourseEnrollment, null=True)
@classmethod
def delete_registration_redemption(cls, user, cart):
def is_registration_code_redeemed(cls, course_reg_code):
"""
This method delete registration redemption
Checks the existence of the registration code
in the RegistrationCodeRedemption
"""
reg_code_redemption = cls.objects.filter(redeemed_by=user, order=cart)
if reg_code_redemption:
reg_code_redemption.delete()
log.info('Registration code redemption entry removed for user {0} for order {1}'.format(user, cart.id))
@classmethod
def add_reg_code_redemption(cls, course_reg_code, order):
"""
add course registration code info into RegistrationCodeRedemption model
"""
cart_items = order.orderitem_set.all().select_subclasses()
for item in cart_items:
if getattr(item, 'course_id'):
if item.course_id == course_reg_code.course_id:
# If the item qty is greater than 1 then the registration code should not be allowed to
# redeem
if item.qty > 1:
raise ItemNotAllowedToRedeemRegCodeException
# If another account tries to use a existing registration code before the student checks out, an
# error message will appear.The reg code is un-reusable.
code_redemption = cls.objects.filter(registration_code=course_reg_code)
if code_redemption:
log.exception("Registration code '{0}' already used".format(course_reg_code.code))
raise RegCodeAlreadyExistException
code_redemption = RegistrationCodeRedemption(registration_code=course_reg_code, order=order, redeemed_by=order.user)
code_redemption.save()
item.list_price = item.unit_cost
item.unit_cost = 0
item.save()
log.info("Code '{0}' is used by user {1} against order id '{2}' "
.format(course_reg_code.code, order.user.username, order.id))
return course_reg_code
log.warning("Course item does not exist against registration code '{0}'".format(course_reg_code.code))
raise ItemDoesNotExistAgainstRegCodeException
return cls.objects.filter(registration_code=course_reg_code).exists()
@classmethod
def create_invoice_generated_registration_redemption(cls, course_reg_code, user):
......
......@@ -491,12 +491,18 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.reg_code})
self.assertEqual(resp.status_code, 200)
# unit price should be updated to 0 for that course
item = self.cart.orderitem_set.all().select_subclasses()[0]
self.assertEquals(item.unit_cost, 0)
self.assertEqual(self.cart.total_cost, 0)
redeem_url = reverse('register_code_redemption', args=[self.reg_code])
response = self.client.get(redeem_url)
self.assertEquals(response.status_code, 200)
# check button text
self.assertTrue('Activate Course Enrollment' in response.content)
#now activate the user by enrolling him/her to the course
response = self.client.post(redeem_url)
self.assertEquals(response.status_code, 200)
# now testing registration code already used scenario, reusing the same code
# the item has been removed when using the registration code for the first time
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.reg_code})
self.assertEqual(resp.status_code, 400)
self.assertIn("Oops! The code '{0}' you entered is either invalid or expired".format(self.reg_code), resp.content)
......@@ -546,38 +552,6 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
'Coupon redemption entry removed for user {0} for order {1}'.format(self.user, reg_item.id))
@patch('shoppingcart.views.log.info')
def test_reset_redemption_for_registration_code(self, info_log):
self.add_reg_code(self.course_key)
reg_item = self.add_course_to_user_cart(self.course_key)
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.reg_code})
self.assertEqual(resp.status_code, 200)
resp = self.client.post(reverse('shoppingcart.views.reset_code_redemption', args=[]))
self.assertEqual(resp.status_code, 200)
info_log.assert_called_with(
'Registration code redemption entry removed for user {0} for order {1}'.format(self.user, reg_item.id))
@patch('shoppingcart.views.log.info')
def test_existing_reg_code_redemption_on_removing_item(self, info_log):
self.add_reg_code(self.course_key)
reg_item = self.add_course_to_user_cart(self.course_key)
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.reg_code})
self.assertEqual(resp.status_code, 200)
resp = self.client.post(reverse('shoppingcart.views.remove_item', args=[]),
{'id': reg_item.id})
self.assertEqual(resp.status_code, 200)
self.assertEquals(self.cart.orderitem_set.count(), 0)
info_log.assert_called_with(
'Registration code "{0}" redemption entry removed for user "{1}" for order item "{2}"'.format(self.reg_code, self.user, reg_item.id))
@patch('shoppingcart.views.log.info')
def test_coupon_discount_for_multiple_courses_in_cart(self, info_log):
reg_item = self.add_course_to_user_cart(self.course_key)
......@@ -596,7 +570,8 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
elif item.id == cert_item.id:
self.assertEquals(item.list_price, None)
# Delete the discounted item, corresponding coupon redemption should be removed for that particular discounted item
# Delete the discounted item, corresponding coupon redemption should
# be removed for that particular discounted item
resp = self.client.post(reverse('shoppingcart.views.remove_item', args=[]),
{'id': reg_item.id})
......@@ -606,34 +581,6 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
'Coupon "{0}" redemption entry removed for user "{1}" for order item "{2}"'.format(self.coupon_code, self.user, reg_item.id))
@patch('shoppingcart.views.log.info')
def test_reg_code_free_discount_with_multiple_courses_in_cart(self, info_log):
reg_item = self.add_course_to_user_cart(self.course_key)
self.add_reg_code(self.course_key)
cert_item = CertificateItem.add_to_order(self.cart, self.verified_course_key, self.cost, 'honor')
self.assertEquals(self.cart.orderitem_set.count(), 2)
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.reg_code})
self.assertEqual(resp.status_code, 200)
# unit_cost should be 0 for that particular course for which registration code is registered
items = self.cart.orderitem_set.all().select_subclasses()
for item in items:
if item.id == reg_item.id:
self.assertEquals(item.unit_cost, 0)
elif item.id == cert_item.id:
self.assertEquals(item.list_price, None)
# Delete the discounted item, corresponding reg code redemption should be removed for that particular item
resp = self.client.post(reverse('shoppingcart.views.remove_item', args=[]),
{'id': reg_item.id})
self.assertEqual(resp.status_code, 200)
self.assertEquals(self.cart.orderitem_set.count(), 1)
info_log.assert_called_with(
'Registration code "{0}" redemption entry removed for user "{1}" for order item "{2}"'.format(self.reg_code, self.user, reg_item.id))
@patch('shoppingcart.views.log.info')
def test_delete_certificate_item(self, info_log):
self.add_course_to_user_cart(self.course_key)
......@@ -667,24 +614,6 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
info_log.assert_called_with(
'Coupon redemption entry removed for user {0} for order {1}'.format(self.user, reg_item.id))
@patch('shoppingcart.views.log.info')
def test_remove_registration_code_redemption_on_clear_cart(self, info_log):
reg_item = self.add_course_to_user_cart(self.course_key)
CertificateItem.add_to_order(self.cart, self.verified_course_key, self.cost, 'honor')
self.assertEquals(self.cart.orderitem_set.count(), 2)
self.add_reg_code(self.course_key)
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.reg_code})
self.assertEqual(resp.status_code, 200)
resp = self.client.post(reverse('shoppingcart.views.clear_cart', args=[]))
self.assertEqual(resp.status_code, 200)
self.assertEquals(self.cart.orderitem_set.count(), 0)
info_log.assert_called_with(
'Registration code redemption entry removed for user {0} for order {1}'.format(self.user, reg_item.id))
def test_add_course_to_cart_already_registered(self):
CourseEnrollment.enroll(self.user, self.course_key)
self.login_user()
......@@ -964,12 +893,16 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.reg_code})
self.assertEqual(resp.status_code, 200)
resp = self.client.get(reverse('shoppingcart.views.show_cart', args=[]))
self.assertIn('Register', resp.content)
# freely enroll the user into course
resp = self.client.get(reverse('shoppingcart.views.register_courses'))
self.assertIn('success', resp.content)
redeem_url = reverse('register_code_redemption', args=[self.reg_code])
response = self.client.get(redeem_url)
self.assertEquals(response.status_code, 200)
# check button text
self.assertTrue('Activate Course Enrollment' in response.content)
#now activate the user by enrolling him/her to the course
response = self.client.post(redeem_url)
self.assertEquals(response.status_code, 200)
@patch('shoppingcart.views.render_to_response', render_mock)
def test_reg_code_with_multiple_courses_and_checkout_scenario(self):
......@@ -984,6 +917,16 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.reg_code})
self.assertEqual(resp.status_code, 200)
redeem_url = reverse('register_code_redemption', args=[self.reg_code])
resp = self.client.get(redeem_url)
self.assertEquals(resp.status_code, 200)
# check button text
self.assertTrue('Activate Course Enrollment' in resp.content)
#now activate the user by enrolling him/her to the course
resp = self.client.post(redeem_url)
self.assertEquals(resp.status_code, 200)
resp = self.client.get(reverse('shoppingcart.views.show_cart', args=[]))
self.assertIn('Payment', resp.content)
self.cart.purchase(first='FirstNameTesting123', street1='StreetTesting123')
......@@ -1001,10 +944,8 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
# make sure the enrollment_ids were stored in the PaidCourseRegistration items
# refetch them first since they are updated
item1 = PaidCourseRegistration.objects.get(id=item1.id)
self.assertIsNotNone(item1.course_enrollment)
self.assertEqual(item1.course_enrollment.course_id, self.course_key)
# item1 has been deleted from the the cart.
# User has been enrolled for the item1
item2 = PaidCourseRegistration.objects.get(id=item2.id)
self.assertIsNotNone(item2.course_enrollment)
self.assertEqual(item2.course_enrollment.course_id, self.testing_course.id)
......@@ -1123,9 +1064,9 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
redeem_url = reverse('register_code_redemption', args=[context['reg_code_info_list'][0]['code']])
#now activate the user by enrolling him/her to the course
response = self.client.post(redeem_url, **{'HTTP_HOST': 'localhost'})
response = self.client.post(redeem_url)
self.assertEquals(response.status_code, 200)
self.assertTrue('View Course' in response.content)
self.assertTrue('View Dashboard' in response.content)
# now view the receipt page again to see if any registration codes
# has been expired or not
......@@ -1240,7 +1181,6 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
self._assert_404(reverse('shoppingcart.views.update_user_cart', args=[]))
self._assert_404(reverse('shoppingcart.views.reset_code_redemption', args=[]), use_post=True)
self._assert_404(reverse('shoppingcart.views.billing_details', args=[]))
self._assert_404(reverse('shoppingcart.views.register_courses', args=[]))
# TODO (ECOM-188): Once we complete the A/B test of separate
......@@ -1451,7 +1391,7 @@ class RegistrationCodeRedemptionCourseEnrollment(ModuleStoreTestCase):
url = reverse('register_code_redemption', args=['asdasd'])
self.login_user()
for i in xrange(30): # pylint: disable=unused-variable
response = self.client.post(url, **{'HTTP_HOST': 'localhost'})
response = self.client.post(url)
self.assertEquals(response.status_code, 404)
# then the rate limiter should kick in and give a HttpForbidden response
......@@ -1461,7 +1401,7 @@ class RegistrationCodeRedemptionCourseEnrollment(ModuleStoreTestCase):
# now reset the time to 5 mins from now in future in order to unblock
reset_time = datetime.now(UTC) + timedelta(seconds=300)
with freeze_time(reset_time):
response = self.client.post(url, **{'HTTP_HOST': 'localhost'})
response = self.client.post(url)
self.assertEquals(response.status_code, 404)
cache.clear()
......@@ -1475,7 +1415,7 @@ class RegistrationCodeRedemptionCourseEnrollment(ModuleStoreTestCase):
url = reverse('register_code_redemption', args=['asdasd'])
self.login_user()
for i in xrange(30): # pylint: disable=unused-variable
response = self.client.get(url, **{'HTTP_HOST': 'localhost'})
response = self.client.get(url)
self.assertEquals(response.status_code, 404)
# then the rate limiter should kick in and give a HttpForbidden response
......@@ -1485,7 +1425,7 @@ class RegistrationCodeRedemptionCourseEnrollment(ModuleStoreTestCase):
# now reset the time to 5 mins from now in future in order to unblock
reset_time = datetime.now(UTC) + timedelta(seconds=300)
with freeze_time(reset_time):
response = self.client.get(url, **{'HTTP_HOST': 'localhost'})
response = self.client.get(url)
self.assertEquals(response.status_code, 404)
cache.clear()
......@@ -1508,33 +1448,31 @@ class RegistrationCodeRedemptionCourseEnrollment(ModuleStoreTestCase):
'customer_reference_number': '123A23F', 'internal_reference': '', 'invoice': ''
}
response = self.client.post(url, data, **{'HTTP_HOST': 'localhost'})
response = self.client.post(url, data)
self.assertEquals(response.status_code, 200)
# get the first registration from the newly created registration codes
registration_code = CourseRegistrationCode.objects.all()[0].code
redeem_url = reverse('register_code_redemption', args=[registration_code])
self.login_user()
response = self.client.get(redeem_url, **{'HTTP_HOST': 'localhost'})
response = self.client.get(redeem_url)
self.assertEquals(response.status_code, 200)
# check button text
self.assertTrue('Activate Course Enrollment' in response.content)
#now activate the user by enrolling him/her to the course
response = self.client.post(redeem_url, **{'HTTP_HOST': 'localhost'})
response = self.client.post(redeem_url)
self.assertEquals(response.status_code, 200)
self.assertTrue('View Course' in response.content)
self.assertTrue('View Dashboard' in response.content)
#now check that the registration code has already been redeemed and user is already registered in the course
redemption = RegistrationCodeRedemption.objects.get(registration_code__code=registration_code)
self.assertIsNotNone(redemption.course_enrollment)
response = self.client.get(redeem_url, **{'HTTP_HOST': 'localhost'})
RegistrationCodeRedemption.objects.filter(registration_code__code=registration_code)
response = self.client.get(redeem_url)
self.assertEquals(len(RegistrationCodeRedemption.objects.filter(registration_code__code=registration_code)), 1)
self.assertTrue("You've clicked a link for an enrollment code that has already been used." in response.content)
#now check that the registration code has already been redeemed
response = self.client.post(redeem_url, **{'HTTP_HOST': 'localhost'})
response = self.client.post(redeem_url)
self.assertTrue("You've clicked a link for an enrollment code that has already been used." in response.content)
#now check the response of the dashboard page
......
......@@ -17,7 +17,6 @@ urlpatterns = patterns('shoppingcart.views', # nopep8
url(r'^reset_code_redemption/$', 'reset_code_redemption'),
url(r'^billing_details/$', 'billing_details', name='billing_details'),
url(r'^verify_cart/$', 'verify_cart'),
url(r'^register_courses/$', 'register_courses'),
)
if settings.FEATURES.get('ENABLE_PAYMENT_FAKE'):
......
......@@ -29,15 +29,14 @@ from student.models import CourseEnrollment
from .exceptions import (
ItemAlreadyInCartException, AlreadyEnrolledInCourseException,
CourseDoesNotExistException, ReportTypeDoesNotExistException,
RegCodeAlreadyExistException, ItemDoesNotExistAgainstRegCodeException,
MultipleCouponsNotAllowedException, InvalidCartItem,
ItemNotAllowedToRedeemRegCodeException
ItemNotFoundInCartException
)
from .models import (
Order, OrderTypes,
PaidCourseRegistration, OrderItem, Coupon,
CouponRedemption, CourseRegistrationCode, RegistrationCodeRedemption,
Donation, DonationConfiguration
CourseRegCodeItem, Donation, DonationConfiguration
)
from .processors import (
process_postpay_callback, render_purchase_form_html,
......@@ -90,9 +89,10 @@ def add_course_to_cart(request, course_id):
except CourseDoesNotExistException:
return HttpResponseNotFound(_('The course you requested does not exist.'))
except ItemAlreadyInCartException:
return HttpResponseBadRequest(_('The course {0} is already in your cart.'.format(course_id)))
return HttpResponseBadRequest(_('The course {course_id} is already in your cart.'.format(course_id=course_id)))
except AlreadyEnrolledInCourseException:
return HttpResponseBadRequest(_('You are already registered in course {0}.'.format(course_id)))
return HttpResponseBadRequest(
_('You are already registered in course {course_id}.'.format(course_id=course_id)))
else:
# in case a coupon redemption code has been applied, new items should also get a discount if applicable.
order = paid_course_item.order
......@@ -129,7 +129,7 @@ def update_user_cart(request):
try:
item = OrderItem.objects.get(id=item_id, status='cart')
except OrderItem.DoesNotExist:
log.exception('Cart OrderItem id={0} DoesNotExist'.format(item_id))
log.exception('Cart OrderItem id={item_id} DoesNotExist'.format(item_id=item_id))
return HttpResponseNotFound('Order item does not exist.')
item.qty = qty
......@@ -186,12 +186,8 @@ def clear_cart(request):
coupon_redemption = CouponRedemption.objects.filter(user=request.user, order=cart.id)
if coupon_redemption:
coupon_redemption.delete()
log.info('Coupon redemption entry removed for user {0} for order {1}'.format(request.user, cart.id))
reg_code_redemption = RegistrationCodeRedemption.objects.filter(redeemed_by=request.user, order=cart.id)
if reg_code_redemption:
reg_code_redemption.delete()
log.info('Registration code redemption entry removed for user {0} for order {1}'.format(request.user, cart.id))
log.info('Coupon redemption entry removed for user {user} for order {order_id}'.format(user=request.user,
order_id=cart.id))
return HttpResponse('Cleared')
......@@ -207,13 +203,14 @@ def remove_item(request):
items = OrderItem.objects.filter(id=item_id, status='cart').select_subclasses()
if not len(items):
log.exception('Cannot remove cart OrderItem id={0}. DoesNotExist or item is already purchased'.format(item_id))
log.exception('Cannot remove cart OrderItem id={item_id}. DoesNotExist or item is already purchased'.format(
item_id=item_id))
else:
item = items[0]
if item.user == request.user:
order_item_course_id = getattr(item, 'course_id')
item.delete()
log.info('order item {0} removed for user {1}'.format(item_id, request.user))
log.info('order item {item_id} removed for user {user}'.format(item_id=item_id, user=request.user))
remove_code_redemption(order_item_course_id, item_id, item, request.user)
item.order.update_order_type()
......@@ -223,7 +220,7 @@ def remove_item(request):
def remove_code_redemption(order_item_course_id, item_id, item, user):
"""
If an item removed from shopping cart then we will remove
the corresponding redemption info of coupon/registration code.
the corresponding redemption info of coupon code
"""
try:
# Try to remove redemption information of coupon code, If exist.
......@@ -233,21 +230,10 @@ def remove_code_redemption(order_item_course_id, item_id, item, user):
order=item.order_id
)
coupon_redemption.delete()
log.info('Coupon "{0}" redemption entry removed for user "{1}" for order item "{2}"'
.format(coupon_redemption.coupon.code, user, item_id))
log.info('Coupon "{code}" redemption entry removed for user "{user}" for order item "{item_id}"'
.format(code=coupon_redemption.coupon.code, user=user, item_id=item_id))
except CouponRedemption.DoesNotExist:
pass
try:
# Try to remove redemption information of registration code, If exist.
reg_code_redemption = RegistrationCodeRedemption.objects.get(redeemed_by=user, order=item.order_id)
except RegistrationCodeRedemption.DoesNotExist:
log.debug('Code redemption does not exist for order item id={0}.'.format(item_id))
else:
if order_item_course_id == reg_code_redemption.registration_code.course_id:
reg_code_redemption.delete()
log.info('Registration code "{0}" redemption entry removed for user "{1}" for order item "{2}"'
.format(reg_code_redemption.registration_code.code, user, item_id))
log.debug('Code redemption does not exist for order item id={item_id}.'.format(item_id=item_id))
@login_required
......@@ -259,7 +245,6 @@ def reset_code_redemption(request):
cart = Order.get_cart_for_user(request.user)
cart.reset_cart_items_prices()
CouponRedemption.delete_coupon_redemption(request.user, cart)
RegistrationCodeRedemption.delete_registration_redemption(request.user, cart)
return HttpResponse('reset')
......@@ -267,20 +252,19 @@ def reset_code_redemption(request):
@enforce_shopping_cart_enabled
def use_code(request):
"""
This method may generate the discount against valid coupon code
and save its entry into coupon redemption table
OR
Make the cart item free of cost against valid registration code.
Valid Code can be either coupon or registration code.
Valid Code can be either Coupon or Registration code.
For a valid Coupon Code, this applies the coupon code and generates a discount against all applicable items.
For a valid Registration code, it deletes the item from the shopping cart and redirects to the
Registration Code Redemption page.
"""
code = request.POST["code"]
coupons = Coupon.objects.filter(code=code, is_active=True)
if not coupons:
# If not coupon code then we check that code against course registration code
# If no coupons then we check that code against course registration code
try:
course_reg = CourseRegistrationCode.objects.get(code=code)
except CourseRegistrationCode.DoesNotExist:
return HttpResponseNotFound(_("Discount does not exist against code '{0}'.".format(code)))
return HttpResponseNotFound(_("Discount does not exist against code '{code}'.".format(code=code)))
return use_registration_code(course_reg, request.user)
......@@ -331,7 +315,7 @@ def register_code_redemption(request, registration_code):
AUDIT_LOG.warning("Rate limit exceeded in registration code redemption.")
return HttpResponseForbidden()
template_to_render = 'shoppingcart/registration_code_receipt.html'
template_to_render = 'shoppingcart/registration_code_redemption.html'
if request.method == "GET":
reg_code_is_valid, reg_code_already_redeemed, course_registration = get_reg_code_validity(registration_code,
request, limiter)
......@@ -351,6 +335,18 @@ def register_code_redemption(request, registration_code):
course = get_course_by_id(getattr(course_registration, 'course_id'), depth=0)
if reg_code_is_valid and not reg_code_already_redeemed:
# remove the course from the cart if it was added there.
cart = Order.get_cart_for_user(request.user)
try:
cart_items = cart.find_item_by_course_id(course_registration.course_id)
except ItemNotFoundInCartException:
pass
else:
for cart_item in cart_items:
if isinstance(cart_item, PaidCourseRegistration) or isinstance(cart_item, CourseRegCodeItem):
cart_item.delete()
#now redeem the reg code.
redemption = RegistrationCodeRedemption.create_invoice_generated_registration_redemption(course_registration, request.user)
redemption.course_enrollment = CourseEnrollment.enroll(request.user, course.id)
......@@ -375,19 +371,41 @@ def register_code_redemption(request, registration_code):
def use_registration_code(course_reg, user):
"""
This method utilize course registration code
This method utilize course registration code.
If the registration code is already redeemed, it returns an error.
Else, it identifies and removes the applicable OrderItem from the Order
and redirects the user to the Registration code redemption page.
"""
if RegistrationCodeRedemption.is_registration_code_redeemed(course_reg):
log.warning("Registration code '{registration_code}' already used".format(registration_code=course_reg.code))
return HttpResponseBadRequest(_(
"Oops! The code '{registration_code}' you entered is either invalid or expired".format(
registration_code=course_reg.code)))
try:
cart = Order.get_cart_for_user(user)
RegistrationCodeRedemption.add_reg_code_redemption(course_reg, cart)
except RegCodeAlreadyExistException:
return HttpResponseBadRequest(_("Oops! The code '{0}' you entered is either invalid or expired".format(course_reg.code)))
except ItemDoesNotExistAgainstRegCodeException:
return HttpResponseNotFound(_("Code '{0}' is not valid for any course in the shopping cart.".format(course_reg.code)))
except ItemNotAllowedToRedeemRegCodeException:
return HttpResponseNotFound(_("Cart item quantity should not be greater than 1 when applying activation code"))
return HttpResponse(json.dumps({'response': 'success'}), content_type="application/json")
cart_items = cart.find_item_by_course_id(course_reg.course_id)
except ItemNotFoundInCartException:
log.warning("Course item does not exist against registration code '{registration_code}'".format(
registration_code=course_reg.code))
return HttpResponseNotFound(_(
"Code '{registration_code}' is not valid for any course in the shopping cart.".format(
registration_code=course_reg.code)))
else:
applicable_cart_items = [
cart_item for cart_item in cart_items
if (
(isinstance(cart_item, PaidCourseRegistration) or isinstance(cart_item, CourseRegCodeItem))and cart_item.qty == 1
)
]
if not applicable_cart_items:
return HttpResponseNotFound(
_("Cart item quantity should not be greater than 1 when applying activation code"))
redemption_url = reverse('register_code_redemption', kwargs={'registration_code': course_reg.code})
return HttpResponse(
json.dumps({'response': 'success', 'coupon_code_applied': False, 'redemption_url': redemption_url}),
content_type="application/json"
)
def use_coupon_code(coupons, user):
......@@ -405,22 +423,14 @@ def use_coupon_code(coupons, user):
return HttpResponseBadRequest(_("Only one coupon redemption is allowed against an order"))
if not is_redemption_applied:
log.warning("Course item does not exist for coupon '{0}'".format(coupons[0].code))
return HttpResponseNotFound(_("Coupon '{0}' is not valid for any course in the shopping cart.".format(coupons[0].code)))
return HttpResponse(json.dumps({'response': 'success'}), content_type="application/json")
log.warning("Course item does not exist for coupon '{code}'".format(code=coupons[0].code))
return HttpResponseNotFound(
_("Coupon '{code}' is not valid for any course in the shopping cart.".format(code=coupons[0].code)))
@login_required
@enforce_shopping_cart_enabled
def register_courses(request):
"""
This method enroll the user for available course(s)
in cart on which valid registration code is applied
"""
cart = Order.get_cart_for_user(request.user)
CourseRegistrationCode.free_user_enrollment(cart)
return HttpResponse(json.dumps({'response': 'success'}), content_type="application/json")
return HttpResponse(
json.dumps({'response': 'success', 'coupon_code_applied': True}),
content_type="application/json"
)
@require_config(DonationConfiguration)
......
......@@ -117,6 +117,14 @@ var edx = edx || {};
$(document).ready(function() {
// (click on the payment submit button).
$('.cart-view form input[type="submit"]').click(function(event) {
// check if there is code exists in the inout_code field
// before going to make payment
// if exists then trigger click event of the apply code button
var code = $('div.code-input input#input_code').val();
if (typeof(code) != 'undefined' && code != ''){
$('div.code-input #submit-code').trigger('click');
return false;
}
var container = $('.confirm-enrollment.cart-view form');
var view = new edx.shoppingcart.showcart.CartView({
el:container
......
......@@ -236,7 +236,6 @@
}
a.course-link-bg-color {
background-color: #00A1E5;
background-image: linear-gradient(#00A1E5, #00A1E5);
border: 16px solid #00A1E5;
box-shadow: 0 1px 0 0 #00A1E5 inset;
text-shadow: 0 1px 0 #00A1E5;
......@@ -254,6 +253,11 @@
text-decoration: none;
font-size: 24px;
text-align: center;
&:hover {
background: $m-blue-d2;
border: 16px solid $m-blue-d2;
box-shadow: 0 1px 0 0 $m-blue-d2 inset;
}
}
input[type="submit"] {
text-transform: none;
......@@ -272,6 +276,10 @@
text-decoration: none;
text-shadow: 0 1px 0 #00A1E5;
font-size: 24px;
&:hover {
background: $m-blue-d2;
box-shadow: none;
}
}
}
......
......@@ -65,8 +65,8 @@ from courseware.courses import course_image_url, get_course_about_section
</div>
% if not reg_code_already_redeemed:
%if redemption_success:
<% course_url = reverse('info', args=[course.id.to_deprecated_string()]) %>
<a href="${course_url}" class="link-button course-link-bg-color">${_("View Course &nbsp; &nbsp; &#x25b8;")}</a>
<% dashboard_url = reverse('dashboard') %>
<a href="${dashboard_url}" class="link-button course-link-bg-color">${_("View Dashboard &nbsp; &nbsp; &#x25b8;")}</a>
%elif not registered_for_course:
<form method="post">
<input type="hidden" name="csrfmiddlewaretoken" value="${ csrf_token }">
......
......@@ -100,9 +100,7 @@ from django.utils.translation import ugettext as _
</div>
<div class="col-two">
<div class="col-2 relative">
% if amount == 0:
<input type="submit" value = "Register" id="register" >
% elif order_type == 'business':
% if order_type == 'business':
<div name="billing">
<input type="submit" value = "Billing Details" name="billing-details"><i class="icon-caret-right"></i>
<p>
......@@ -172,7 +170,13 @@ from django.utils.translation import ugettext as _
}
)
.success(function(data) {
location.reload(true);
if (data.coupon_code_applied) {
location.reload(true); // Reload the page if the coupon code was applied.
}
else {
// Redirect to the redemption URL if the Registration code was applied.
location.href = data.redemption_url;
}
})
.error(function(data,status) {
if(status=="parsererror"){
......@@ -199,23 +203,15 @@ from django.utils.translation import ugettext as _
})
});
$('#register').click(function(event){
event.preventDefault();
var post_url = "${reverse('shoppingcart.views.register_courses')}";
$.post(post_url)
.success(function(data) {
window.location.href = "${reverse('dashboard')}";
})
.error(function(data,status) {
if(status=="parsererror"){
location.reload(true);
}else{
showErrorMsgs(data.responseText)
}
})
});
$("input[name='billing-details']").click(function(event){
// check if there is code exists in the inout_code field
// before going to billing details page
// if exists then trigger click event of the apply code button
var code = $('div.code-input input#input_code').val();
if (code!= ''){
$('div.code-input #submit-code').trigger('click');
return false;
}
event.preventDefault();
location.href = "${reverse('shoppingcart.views.billing_details')}";
});
......
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