Commit dc46170f by asadiqbal08 Committed by Chris Dodge

registration code checkout flow

Redirecting to dashboard after free user enrollment.
parent 878eaa9f
......@@ -1368,7 +1368,7 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa
url = reverse('get_purchase_transaction', kwargs={'course_id': self.course.id.to_deprecated_string()})
# using coupon code
resp = self.client.post(reverse('shoppingcart.views.use_coupon'), {'coupon_code': self.coupon_code})
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.coupon_code})
self.assertEqual(resp.status_code, 200)
self.cart.purchase(first='FirstNameTesting123', street1='StreetTesting123')
response = self.client.get(url, {})
......
......@@ -40,6 +40,14 @@ class ItemDoesNotExistAgainstCouponException(InvalidCartItem):
pass
class RegCodeAlreadyExistException(InvalidCartItem):
pass
class ItemDoesNotExistAgainstRegCodeException(InvalidCartItem):
pass
class ReportException(Exception):
pass
......
......@@ -31,7 +31,9 @@ from xmodule_django.models import CourseKeyField
from verify_student.models import SoftwareSecurePhotoVerification
from .exceptions import (InvalidCartItem, PurchasedCallbackException, ItemAlreadyInCartException,
AlreadyEnrolledInCourseException, CourseDoesNotExistException, CouponAlreadyExistException, ItemDoesNotExistAgainstCouponException)
AlreadyEnrolledInCourseException, CourseDoesNotExistException,
CouponAlreadyExistException, ItemDoesNotExistAgainstCouponException,
RegCodeAlreadyExistException, ItemDoesNotExistAgainstRegCodeException)
from microsite_configuration import microsite
......@@ -326,6 +328,25 @@ class CourseRegistrationCode(models.Model):
created_by = models.ForeignKey(User, related_name='created_by_user')
created_at = models.DateTimeField(default=datetime.now(pytz.utc))
@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=E1101
item.status = 'purchased'
item.save()
cart.status = 'purchased'
cart.purchase_time = datetime.now(pytz.utc)
cart.save()
class RegistrationCodeRedemption(models.Model):
"""
......@@ -336,6 +357,35 @@ class RegistrationCodeRedemption(models.Model):
redeemed_by = models.ForeignKey(User, db_index=True)
redeemed_at = models.DateTimeField(default=datetime.now(pytz.utc), null=True)
@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 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
class SoftDeleteCouponManager(models.Manager):
""" Use this manager to get objects that have a is_active=True """
......
......@@ -17,7 +17,7 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE
from shoppingcart.views import _can_download_report, _get_date_from_str
from shoppingcart.models import Order, CertificateItem, PaidCourseRegistration, Coupon
from shoppingcart.models import Order, CertificateItem, PaidCourseRegistration, Coupon, CourseRegistrationCode
from student.tests.factories import UserFactory, AdminFactory
from student.models import CourseEnrollment
from course_modes.models import CourseMode
......@@ -53,6 +53,7 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
self.instructor = AdminFactory.create()
self.cost = 40
self.coupon_code = 'abcde'
self.reg_code = 'qwerty'
self.percentage_discount = 10
self.course = CourseFactory.create(org='MITx', number='999', display_name='Robot Super Course')
self.course_key = self.course.id
......@@ -61,6 +62,16 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
mode_display_name="honor cert",
min_price=self.cost)
self.course_mode.save()
#Saving another testing course mode
self.testing_cost = 20
self.testing_course = CourseFactory.create(org='edX', number='888', display_name='Testing Super Course')
self.testing_course_mode = CourseMode(course_id=self.testing_course.id,
mode_slug="honor",
mode_display_name="testing honor cert",
min_price=self.testing_cost)
self.testing_course_mode.save()
verified_course = CourseFactory.create(org='org', number='test', display_name='Test Course')
self.verified_course_key = verified_course.id
self.cart = Order.get_cart_for_user(self.user)
......@@ -81,6 +92,14 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
percentage_discount=self.percentage_discount, created_by=self.user, is_active=is_active)
coupon.save()
def add_reg_code(self, course_key):
"""
add dummy registration code into models
"""
course_reg_code = CourseRegistrationCode(code=self.reg_code, course_id=course_key,
transaction_group_name='A', created_by=self.user)
course_reg_code.save()
def add_course_to_user_cart(self):
"""
adding course to user cart
......@@ -107,14 +126,22 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
self.add_coupon(self.course_key, True)
self.add_course_to_user_cart()
non_existing_code = "non_existing_code"
resp = self.client.post(reverse('shoppingcart.views.use_coupon'), {'coupon_code': non_existing_code})
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': non_existing_code})
self.assertEqual(resp.status_code, 404)
self.assertIn("Discount does not exist against code '{0}'.".format(non_existing_code), resp.content)
def test_course_discount_invalid_reg_code(self):
self.add_reg_code(self.course_key)
self.add_course_to_user_cart()
non_existing_code = "non_existing_code"
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': non_existing_code})
self.assertEqual(resp.status_code, 404)
self.assertIn("Discount does not exist against coupon '{0}'.".format(non_existing_code), resp.content)
self.assertIn("Discount does not exist against code '{0}'.".format(non_existing_code), resp.content)
def test_course_discount_inactive_coupon(self):
self.add_coupon(self.course_key, False)
self.add_course_to_user_cart()
resp = self.client.post(reverse('shoppingcart.views.use_coupon'), {'coupon_code': self.coupon_code})
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.coupon_code})
self.assertEqual(resp.status_code, 400)
self.assertIn("Coupon '{0}' is inactive.".format(self.coupon_code), resp.content)
......@@ -123,16 +150,25 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
self.add_coupon(course_key, True)
self.add_course_to_user_cart()
resp = self.client.post(reverse('shoppingcart.views.use_coupon'), {'coupon_code': self.coupon_code})
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.coupon_code})
self.assertEqual(resp.status_code, 404)
self.assertIn("Coupon '{0}' is not valid for any course in the shopping cart.".format(self.coupon_code), resp.content)
def test_course_does_not_exist_in_cart_against_valid_reg_code(self):
course_key = self.course_key.to_deprecated_string() + 'testing'
self.add_reg_code(course_key)
self.add_course_to_user_cart()
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.reg_code})
self.assertEqual(resp.status_code, 404)
self.assertIn("Code '{0}' is not valid for any course in the shopping cart.".format(self.reg_code), resp.content)
def test_course_discount_for_valid_active_coupon_code(self):
self.add_coupon(self.course_key, True)
self.add_course_to_user_cart()
resp = self.client.post(reverse('shoppingcart.views.use_coupon'), {'coupon_code': self.coupon_code})
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.coupon_code})
self.assertEqual(resp.status_code, 200)
# unit price should be updated for that course
......@@ -143,7 +179,7 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
self.assertEqual(self.cart.total_cost, self.get_discount())
# now testing coupon code already used scenario, reusing the same coupon code
resp = self.client.post(reverse('shoppingcart.views.use_coupon'), {'coupon_code': self.coupon_code})
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.coupon_code})
self.assertEqual(resp.status_code, 400)
self.assertIn("Coupon '{0}' already used.".format(self.coupon_code), resp.content)
......@@ -180,6 +216,24 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
for coupon in test_query_set:
self.assertEqual(coupon.is_active, False)
def test_course_free_discount_for_valid_active_reg_code(self):
self.add_reg_code(self.course_key)
self.add_course_to_user_cart()
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)
# now testing registration code already used scenario, reusing the same code
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)
@patch('shoppingcart.views.log.debug')
def test_non_existing_coupon_redemption_on_removing_item(self, debug_log):
......@@ -187,7 +241,7 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
resp = self.client.post(reverse('shoppingcart.views.remove_item', args=[]),
{'id': reg_item.id})
debug_log.assert_called_with(
'Coupon redemption does not exist for order item id={0}.'.format(reg_item.id))
'Code redemption does not exist for order item id={0}.'.format(reg_item.id))
self.assertEqual(resp.status_code, 200)
self.assertEquals(self.cart.orderitem_set.count(), 0)
......@@ -198,7 +252,7 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
self.add_coupon(self.course_key, True)
reg_item = self.add_course_to_user_cart()
resp = self.client.post(reverse('shoppingcart.views.use_coupon'), {'coupon_code': self.coupon_code})
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.coupon_code})
self.assertEqual(resp.status_code, 200)
resp = self.client.post(reverse('shoppingcart.views.remove_item', args=[]),
......@@ -210,6 +264,23 @@ 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_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()
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()
......@@ -217,7 +288,7 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
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_coupon'), {'coupon_code': self.coupon_code})
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.coupon_code})
self.assertEqual(resp.status_code, 200)
# unit_cost should be updated for that particular course for which coupon code is registered
......@@ -238,6 +309,34 @@ 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.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):
reg_item = self.add_course_to_user_cart()
......@@ -261,7 +360,7 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
self.assertEquals(self.cart.orderitem_set.count(), 2)
self.add_coupon(self.course_key, True)
resp = self.client.post(reverse('shoppingcart.views.use_coupon'), {'coupon_code': self.coupon_code})
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.coupon_code})
self.assertEqual(resp.status_code, 200)
resp = self.client.post(reverse('shoppingcart.views.clear_cart', args=[]))
......@@ -271,6 +370,24 @@ 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()
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()
......@@ -391,7 +508,7 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
self.add_course_to_user_cart()
self.assertEquals(self.cart.orderitem_set.count(), 1)
self.add_coupon(self.course_key, True)
resp = self.client.post(reverse('shoppingcart.views.use_coupon'), {'coupon_code': self.coupon_code})
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.coupon_code})
self.assertEqual(resp.status_code, 200)
self.cart.purchase(first='FirstNameTesting123', street1='StreetTesting123')
......@@ -413,7 +530,7 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
self.add_course_to_user_cart()
self.add_coupon(self.course_key, True)
resp = self.client.post(reverse('shoppingcart.views.use_coupon'), {'coupon_code': self.coupon_code})
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.coupon_code})
self.assertEqual(resp.status_code, 200)
self.cart.purchase(first='FirstNameTesting123', street1='StreetTesting123')
......@@ -423,6 +540,64 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
self.assertIn(str(self.get_discount()), resp.content)
@patch('shoppingcart.views.render_to_response', render_mock)
def test_reg_code_and_course_registration_scenario(self):
self.add_reg_code(self.course_key)
# One courses in user shopping cart
self.add_course_to_user_cart()
self.assertEquals(self.cart.orderitem_set.count(), 1)
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)
@patch('shoppingcart.views.render_to_response', render_mock)
def test_reg_code_with_multiple_courses_and_checkout_scenario(self):
self.add_reg_code(self.course_key)
# Two courses in user shopping cart
self.login_user()
PaidCourseRegistration.add_to_order(self.cart, self.course_key)
PaidCourseRegistration.add_to_order(self.cart, self.testing_course.id)
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)
resp = self.client.get(reverse('shoppingcart.views.show_cart', args=[]))
self.assertIn('Check Out', resp.content)
self.cart.purchase(first='FirstNameTesting123', street1='StreetTesting123')
resp = self.client.get(reverse('shoppingcart.views.show_receipt', args=[self.cart.id]))
self.assertEqual(resp.status_code, 200)
((template, context), _) = render_mock.call_args # pylint: disable=W0621
self.assertEqual(template, 'shoppingcart/receipt.html')
self.assertEqual(context['order'], self.cart)
self.assertEqual(context['order'].total_cost, self.testing_cost)
course_enrollment = CourseEnrollment.objects.filter(user=self.user)
self.assertEqual(course_enrollment.count(), 2)
@patch('shoppingcart.views.render_to_response', render_mock)
def test_show_receipt_success_with_valid_reg_code(self):
self.add_course_to_user_cart()
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)
self.cart.purchase(first='FirstNameTesting123', street1='StreetTesting123')
resp = self.client.get(reverse('shoppingcart.views.show_receipt', args=[self.cart.id]))
self.assertEqual(resp.status_code, 200)
self.assertIn('0.00', resp.content)
@patch('shoppingcart.views.render_to_response', render_mock)
def test_show_receipt_success(self):
reg_item = PaidCourseRegistration.add_to_order(self.cart, self.course_key)
cert_item = CertificateItem.add_to_order(self.cart, self.verified_course_key, self.cost, 'honor')
......
......@@ -14,7 +14,8 @@ if settings.FEATURES['ENABLE_SHOPPING_CART']:
url(r'^clear/$', 'clear_cart'),
url(r'^remove_item/$', 'remove_item'),
url(r'^add/course/{}/$'.format(settings.COURSE_ID_PATTERN), 'add_course_to_cart', name='add_course_to_cart'),
url(r'^use_coupon/$', 'use_coupon'),
url(r'^use_code/$', 'use_code'),
url(r'^register_courses/$', 'register_courses'),
)
if settings.FEATURES.get('ENABLE_PAYMENT_FAKE'):
......
......@@ -14,8 +14,9 @@ from edxmako.shortcuts import render_to_response
from opaque_keys.edx.locations import SlashSeparatedCourseKey
from shoppingcart.reports import RefundReport, ItemizedPurchaseReport, UniversityRevenueShareReport, CertificateStatusReport
from student.models import CourseEnrollment
from .exceptions import ItemAlreadyInCartException, AlreadyEnrolledInCourseException, CourseDoesNotExistException, ReportTypeDoesNotExistException, CouponAlreadyExistException, ItemDoesNotExistAgainstCouponException
from .models import Order, PaidCourseRegistration, OrderItem, Coupon, CouponRedemption
from .exceptions import ItemAlreadyInCartException, AlreadyEnrolledInCourseException, CourseDoesNotExistException, ReportTypeDoesNotExistException, \
CouponAlreadyExistException, ItemDoesNotExistAgainstCouponException, RegCodeAlreadyExistException, ItemDoesNotExistAgainstRegCodeException
from .models import Order, PaidCourseRegistration, OrderItem, Coupon, CouponRedemption, CourseRegistrationCode, RegistrationCodeRedemption
from .processors import process_postpay_callback, render_purchase_form_html
import json
......@@ -97,6 +98,11 @@ def clear_cart(request):
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))
return HttpResponse('Cleared')
......@@ -111,43 +117,104 @@ def remove_item(request):
order_item_course_id = item.paidcourseregistration.course_id
item.delete()
log.info('order item {0} removed for user {1}'.format(item_id, request.user))
try:
coupon_redemption = CouponRedemption.objects.get(user=request.user, order=item.order_id)
if order_item_course_id == coupon_redemption.coupon.course_id:
coupon_redemption.delete()
log.info('Coupon "{0}" redemption entry removed for user "{1}" for order item "{2}"'
.format(coupon_redemption.coupon.code, request.user, item_id))
except CouponRedemption.DoesNotExist:
log.debug('Coupon redemption does not exist for order item id={0}.'.format(item_id))
remove_code_redemption(order_item_course_id, item_id, item, request.user)
except OrderItem.DoesNotExist:
log.exception('Cannot remove cart OrderItem id={0}. DoesNotExist or item is already purchased'.format(item_id))
return HttpResponse('OK')
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.
"""
try:
# Try to remove redemption information of coupon code, If exist.
coupon_redemption = CouponRedemption.objects.get(user=user, order=item.order_id)
except CouponRedemption.DoesNotExist:
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))
else:
if order_item_course_id == coupon_redemption.coupon.course_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))
@login_required
def use_coupon(request):
def use_code(request):
"""
This method generate discount against valid coupon code and save its entry into coupon redemption table
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.
"""
coupon_code = request.POST["coupon_code"]
code = request.POST["code"]
try:
coupon = Coupon.objects.get(code=coupon_code)
coupon = Coupon.objects.get(code=code)
except Coupon.DoesNotExist:
return HttpResponseNotFound(_("Discount does not exist against coupon '{0}'.".format(coupon_code)))
# If not coupon code 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 use_registration_code(course_reg, request.user)
return use_coupon_code(coupon, request.user)
def use_registration_code(course_reg, user):
"""
This method utilize course registration 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)))
return HttpResponse(json.dumps({'response': 'success'}), content_type="application/json")
def use_coupon_code(coupon, user):
"""
This method utilize course coupon code
"""
if coupon.is_active:
try:
cart = Order.get_cart_for_user(request.user)
cart = Order.get_cart_for_user(user)
CouponRedemption.add_coupon_redemption(coupon, cart)
except CouponAlreadyExistException:
return HttpResponseBadRequest(_("Coupon '{0}' already used.".format(coupon_code)))
return HttpResponseBadRequest(_("Coupon '{0}' already used.".format(coupon.code)))
except ItemDoesNotExistAgainstCouponException:
return HttpResponseNotFound(_("Coupon '{0}' is not valid for any course in the shopping cart.".format(coupon_code)))
return HttpResponseNotFound(_("Coupon '{0}' is not valid for any course in the shopping cart.".format(coupon.code)))
response = HttpResponse(json.dumps({'response': 'success'}), content_type="application/json")
return response
return HttpResponse(json.dumps({'response': 'success'}), content_type="application/json")
else:
return HttpResponseBadRequest(_("Coupon '{0}' is inactive.".format(coupon_code)))
return HttpResponseBadRequest(_("Coupon '{0}' is inactive.".format(coupon.code)))
@login_required
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")
@csrf_exempt
......
......@@ -63,10 +63,6 @@
height: 35px;
border-bottom: 1px solid #BEBEBE;
th:nth-child(5),th:first-child{
text-align: center;
width: 120px;
}
th {
text-align: left;
border-bottom: 1px solid $border-color-1;
......@@ -75,17 +71,19 @@
width: 100px;
}
&.u-pr {
width: 100px;
width: 70px;
}
&.prc {
width: 150px;
}
&.cur {
width: 100px;
text-align: center;
}
&.dsc{
width: 640px;
padding-right: 50px;
text-align: left;
}
}
}
......@@ -96,22 +94,25 @@
position: relative;
line-height: normal;
span.old-price{
left: -75px;
position: relative;
text-decoration: line-through;
color: red;
font-size: 12px;
top: -1px;
margin-left: 3px;
}
}
td:nth-child(5),td:first-child{
td:nth-child(3){
text-align: center;
}
td:last-child{
width: 50px;
text-align: center;
}
td:nth-child(2){
td:nth-child(1){
line-height: 22px;
padding-right: 50px;
text-align: left;
padding-right: 20px;
}
}
......
......@@ -13,17 +13,15 @@
<table class="cart-table">
<thead>
<tr class="cart-headings">
<th class="qty">${_("Quantity")}</th>
<th class="dsc">${_("Description")}</th>
<th class="u-pr">${_("Unit Price")}</th>
<th class="prc">${_("Price")}</th>
<th class="u-pr">${_("Price")}</th>
<th class="cur">${_("Currency")}</th>
<th>&nbsp;</th>
</tr>
</thead>
<tbody>
% for item in shoppingcart_items:
<tr class="cart-items">
<td>${item.qty}</td>
<td>${item.line_desc}</td>
<td>
${"{0:0.2f}".format(item.unit_cost)}
......@@ -31,14 +29,12 @@
<span class="old-price"> ${"{0:0.2f}".format(item.list_price)}</span>
% endif
</td>
<td>${"{0:0.2f}".format(item.line_cost)}</td>
<td>${item.currency.upper()}</td>
<td><a data-item-id="${item.id}" class='remove_line_item' href='#'>[x]</a></td>
</tr>
% endfor
<tr class="always-gray">
<td colspan="3"></td>
<td colspan="3" valign="middle" class="cart-total" align="right">
<td colspan="4" valign="middle" class="cart-total" align="right">
<b>${_("Total Amount")}: <span> ${"{0:0.2f}".format(amount)} </span> </b>
</td>
</tr>
......@@ -47,11 +43,15 @@
<tfoot>
<tr class="always-white">
<td colspan="2">
<input type="text" placeholder="Enter coupon code here" name="coupon_code" id="couponCode">
<input type="button" value="Use Coupon" id="cart-coupon">
<input type="text" placeholder="Enter code here" name="cart_code" id="code">
<input type="button" value="Apply Code" id="cart-code">
</td>
<td colspan="4" align="right">
% if amount == 0:
<input type="button" value = "Register" id="register" >
% else:
${form_html}
%endif
</td>
</tr>
......@@ -76,14 +76,14 @@
});
});
$('#cart-coupon').click(function(event){
$('#cart-code').click(function(event){
event.preventDefault();
var post_url = "${reverse('shoppingcart.views.use_coupon')}";
var post_url = "${reverse('shoppingcart.views.use_code')}";
$.post(post_url,{
"coupon_code" : $('#couponCode').val(),
"code" : $('#code').val(),
beforeSend: function(xhr, options){
if($('#couponCode').val() == "") {
showErrorMsgs('Must contain a valid coupon code')
if($('#code').val() == "") {
showErrorMsgs('Must enter a valid code')
xhr.abort();
}
}
......@@ -101,6 +101,22 @@
})
});
$('#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)
}
})
});
$('#back_input').click(function(){
history.back();
});
......@@ -111,4 +127,3 @@
}
});
</script>
\ No newline at end of file
......@@ -86,7 +86,7 @@
${_("Note: items with strikethough like <del>this</del> have been refunded.")}
</p>
% endif
% if order.total_cost > 0:
<h2>${_("Billed To:")}</h2>
<p>
${order.bill_to_cardtype} ${_("#:")} ${order.bill_to_ccnum}<br />
......@@ -96,6 +96,8 @@
${order.bill_to_city}, ${order.bill_to_state} ${order.bill_to_postalcode}<br />
${order.bill_to_country.upper()}<br />
</p>
% endif
</article>
</div>
</section>
</div>
......
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