Commit 91a47962 by chrisndodge

Merge pull request #6196 from edx/muhhshoaib/WL-168

WL-168 When redeeming a Registration Code in the shopping cart, the user should be redirect to the Registration Code Redemption page
parents 4589a976 5a437b39
...@@ -289,9 +289,15 @@ class DashboardTest(ModuleStoreTestCase): ...@@ -289,9 +289,15 @@ class DashboardTest(ModuleStoreTestCase):
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': course_reg_code.code}) resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': course_reg_code.code})
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
# freely enroll the user into course redeem_url = reverse('register_code_redemption', args=[course_reg_code.code])
resp = self.client.get(reverse('shoppingcart.views.register_courses')) response = self.client.get(redeem_url)
self.assertIn('success', resp.content) 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')) response = self.client.get(reverse('dashboard'))
self.assertIn('You can no longer access this course because payment has not yet been received', response.content) 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 ...@@ -1862,30 +1862,6 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa
for res in res_json['sale']: for res in res_json['sale']:
self.validate_sale_records_response(res, course_registration_code, self.sale_invoice_1, 0) 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): 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 Test that the response from get_sale_records is in json format for multiple invoices
...@@ -3038,7 +3014,8 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase): ...@@ -3038,7 +3014,8 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
for i in range(5): for i in range(5):
i += 1 i += 1
registration_code_redemption = RegistrationCodeRedemption( 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() registration_code_redemption.save()
...@@ -3220,7 +3197,8 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase): ...@@ -3220,7 +3197,8 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
for i in range(9): for i in range(9):
i += 13 i += 13
registration_code_redemption = RegistrationCodeRedemption( 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() registration_code_redemption.save()
......
...@@ -798,8 +798,6 @@ def get_sale_order_records(request, course_id): # pylint: disable=unused-argume ...@@ -798,8 +798,6 @@ def get_sale_order_records(request, course_id): # pylint: disable=unused-argume
('company_contact_name', 'Company Contact Name'), ('company_contact_name', 'Company Contact Name'),
('company_contact_email', 'Company Contact Email'), ('company_contact_email', 'Company Contact Email'),
('total_amount', 'Total Amount'), ('total_amount', 'Total Amount'),
('total_codes', 'Total Codes'),
('total_used_codes', 'Total Used Codes'),
('logged_in_username', 'Login Username'), ('logged_in_username', 'Login Username'),
('logged_in_email', 'Login User Email'), ('logged_in_email', 'Login User Email'),
('purchase_time', 'Date of Sale'), ('purchase_time', 'Date of Sale'),
...@@ -817,8 +815,6 @@ def get_sale_order_records(request, course_id): # pylint: disable=unused-argume ...@@ -817,8 +815,6 @@ def get_sale_order_records(request, course_id): # pylint: disable=unused-argume
('coupon_code', 'Coupon Code'), ('coupon_code', 'Coupon Code'),
('unit_cost', 'Unit Price'), ('unit_cost', 'Unit Price'),
('list_price', 'List Price'), ('list_price', 'List Price'),
('codes', 'Registration Codes'),
('course_id', 'Course Id')
] ]
db_columns = [x[0] for x in query_features] db_columns = [x[0] for x in query_features]
...@@ -1156,7 +1152,7 @@ def generate_registration_codes(request, course_id): ...@@ -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) generated_registration_code = save_registration_code(request.user, course_id, sale_invoice, order=None)
registration_codes.append(generated_registration_code) 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 = get_course_by_id(course_id, depth=None)
course_honor_mode = CourseMode.mode_for_course(course_id, 'honor') course_honor_mode = CourseMode.mode_for_course(course_id, 'honor')
course_price = course_honor_mode.min_price course_price = course_honor_mode.min_price
......
...@@ -60,7 +60,6 @@ def sale_order_record_features(course_id, features): ...@@ -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] 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] order_item_features = [x for x in ORDER_ITEM_FEATURES if x in features]
# Extracting order information # Extracting order information
...@@ -74,9 +73,6 @@ def sale_order_record_features(course_id, features): ...@@ -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_username": purchased_course.order.user.username})
sale_order_dict.update({"logged_in_email": purchased_course.order.user.email}) 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 # Extracting OrderItem information of unit_cost, list_price and status
order_item_dict = dict((feature, getattr(purchased_course, feature, None)) order_item_dict = dict((feature, getattr(purchased_course, feature, None))
for feature in order_item_features) for feature in order_item_features)
...@@ -89,22 +85,6 @@ def sale_order_record_features(course_id, features): ...@@ -89,22 +85,6 @@ def sale_order_record_features(course_id, features):
order_item_dict.update({'coupon_code': ", ".join(coupon_codes)}) order_item_dict.update({'coupon_code': ", ".join(coupon_codes)})
sale_order_dict.update(dict(order_item_dict.items())) 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 return sale_order_dict
......
...@@ -238,8 +238,6 @@ class TestCourseSaleRecordsAnalyticsBasic(ModuleStoreTestCase): ...@@ -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_name'], order.company_contact_name)
self.assertEqual(sale_order_record['company_contact_email'], order.company_contact_email) 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['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['unit_cost'], item.unit_cost)
self.assertEqual(sale_order_record['list_price'], item.list_price) self.assertEqual(sale_order_record['list_price'], item.list_price)
self.assertEqual(sale_order_record['status'], item.status) self.assertEqual(sale_order_record['status'], item.status)
...@@ -281,7 +279,7 @@ class TestCourseRegistrationCodeAnalyticsBasic(ModuleStoreTestCase): ...@@ -281,7 +279,7 @@ class TestCourseRegistrationCodeAnalyticsBasic(ModuleStoreTestCase):
order.save() order.save()
registration_code_redemption = RegistrationCodeRedemption( 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_code_redemption.save()
registration_codes = CourseRegistrationCode.objects.all() registration_codes = CourseRegistrationCode.objects.all()
......
...@@ -37,18 +37,6 @@ class MultipleCouponsNotAllowedException(InvalidCartItem): ...@@ -37,18 +37,6 @@ class MultipleCouponsNotAllowedException(InvalidCartItem):
pass pass
class RegCodeAlreadyExistException(InvalidCartItem):
pass
class ItemNotAllowedToRedeemRegCodeException(InvalidCartItem):
pass
class ItemDoesNotExistAgainstRegCodeException(InvalidCartItem):
pass
class ReportException(Exception): class ReportException(Exception):
pass pass
...@@ -63,3 +51,7 @@ class InvalidStatusToRetire(Exception): ...@@ -63,3 +51,7 @@ class InvalidStatusToRetire(Exception):
class UnexpectedOrderItemStatus(Exception): class UnexpectedOrderItemStatus(Exception):
pass pass
class ItemNotFoundInCartException(Exception):
pass
...@@ -44,11 +44,9 @@ from .exceptions import ( ...@@ -44,11 +44,9 @@ from .exceptions import (
AlreadyEnrolledInCourseException, AlreadyEnrolledInCourseException,
CourseDoesNotExistException, CourseDoesNotExistException,
MultipleCouponsNotAllowedException, MultipleCouponsNotAllowedException,
RegCodeAlreadyExistException,
ItemDoesNotExistAgainstRegCodeException,
ItemNotAllowedToRedeemRegCodeException,
InvalidStatusToRetire, InvalidStatusToRetire,
UnexpectedOrderItemStatus, UnexpectedOrderItemStatus,
ItemNotFoundInCartException
) )
from microsite_configuration import microsite from microsite_configuration import microsite
...@@ -552,6 +550,23 @@ class Order(models.Model): ...@@ -552,6 +550,23 @@ class Order(models.Model):
for item in self.orderitem_set.all(): # pylint: disable=no-member for item in self.orderitem_set.all(): # pylint: disable=no-member
item.retire() 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): class OrderItem(TimeStampedModel):
""" """
...@@ -730,25 +745,6 @@ class CourseRegistrationCode(models.Model): ...@@ -730,25 +745,6 @@ class CourseRegistrationCode(models.Model):
order = models.ForeignKey(Order, db_index=True, null=True, related_name="purchase_order") order = models.ForeignKey(Order, db_index=True, null=True, related_name="purchase_order")
invoice = models.ForeignKey(Invoice, null=True) 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): class RegistrationCodeRedemption(models.Model):
""" """
...@@ -761,47 +757,12 @@ class RegistrationCodeRedemption(models.Model): ...@@ -761,47 +757,12 @@ class RegistrationCodeRedemption(models.Model):
course_enrollment = models.ForeignKey(CourseEnrollment, null=True) course_enrollment = models.ForeignKey(CourseEnrollment, null=True)
@classmethod @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) return cls.objects.filter(registration_code=course_reg_code).exists()
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
@classmethod @classmethod
def create_invoice_generated_registration_redemption(cls, course_reg_code, user): def create_invoice_generated_registration_redemption(cls, course_reg_code, user):
......
...@@ -17,7 +17,6 @@ urlpatterns = patterns('shoppingcart.views', # nopep8 ...@@ -17,7 +17,6 @@ urlpatterns = patterns('shoppingcart.views', # nopep8
url(r'^reset_code_redemption/$', 'reset_code_redemption'), url(r'^reset_code_redemption/$', 'reset_code_redemption'),
url(r'^billing_details/$', 'billing_details', name='billing_details'), url(r'^billing_details/$', 'billing_details', name='billing_details'),
url(r'^verify_cart/$', 'verify_cart'), url(r'^verify_cart/$', 'verify_cart'),
url(r'^register_courses/$', 'register_courses'),
) )
if settings.FEATURES.get('ENABLE_PAYMENT_FAKE'): if settings.FEATURES.get('ENABLE_PAYMENT_FAKE'):
......
...@@ -117,6 +117,14 @@ var edx = edx || {}; ...@@ -117,6 +117,14 @@ var edx = edx || {};
$(document).ready(function() { $(document).ready(function() {
// (click on the payment submit button). // (click on the payment submit button).
$('.cart-view form input[type="submit"]').click(function(event) { $('.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 container = $('.confirm-enrollment.cart-view form');
var view = new edx.shoppingcart.showcart.CartView({ var view = new edx.shoppingcart.showcart.CartView({
el:container el:container
......
...@@ -236,7 +236,6 @@ ...@@ -236,7 +236,6 @@
} }
a.course-link-bg-color { a.course-link-bg-color {
background-color: #00A1E5; background-color: #00A1E5;
background-image: linear-gradient(#00A1E5, #00A1E5);
border: 16px solid #00A1E5; border: 16px solid #00A1E5;
box-shadow: 0 1px 0 0 #00A1E5 inset; box-shadow: 0 1px 0 0 #00A1E5 inset;
text-shadow: 0 1px 0 #00A1E5; text-shadow: 0 1px 0 #00A1E5;
...@@ -254,6 +253,11 @@ ...@@ -254,6 +253,11 @@
text-decoration: none; text-decoration: none;
font-size: 24px; font-size: 24px;
text-align: center; 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"] { input[type="submit"] {
text-transform: none; text-transform: none;
...@@ -272,6 +276,10 @@ ...@@ -272,6 +276,10 @@
text-decoration: none; text-decoration: none;
text-shadow: 0 1px 0 #00A1E5; text-shadow: 0 1px 0 #00A1E5;
font-size: 24px; 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 ...@@ -65,8 +65,8 @@ from courseware.courses import course_image_url, get_course_about_section
</div> </div>
% if not reg_code_already_redeemed: % if not reg_code_already_redeemed:
%if redemption_success: %if redemption_success:
<% course_url = reverse('info', args=[course.id.to_deprecated_string()]) %> <% dashboard_url = reverse('dashboard') %>
<a href="${course_url}" class="link-button course-link-bg-color">${_("View Course &nbsp; &nbsp; &#x25b8;")}</a> <a href="${dashboard_url}" class="link-button course-link-bg-color">${_("View Dashboard &nbsp; &nbsp; &#x25b8;")}</a>
%elif not registered_for_course: %elif not registered_for_course:
<form method="post"> <form method="post">
<input type="hidden" name="csrfmiddlewaretoken" value="${ csrf_token }"> <input type="hidden" name="csrfmiddlewaretoken" value="${ csrf_token }">
......
...@@ -100,9 +100,7 @@ from django.utils.translation import ugettext as _ ...@@ -100,9 +100,7 @@ from django.utils.translation import ugettext as _
</div> </div>
<div class="col-two"> <div class="col-two">
<div class="col-2 relative"> <div class="col-2 relative">
% if amount == 0: % if order_type == 'business':
<input type="submit" value = "Register" id="register" >
% elif order_type == 'business':
<div name="billing"> <div name="billing">
<input type="submit" value = "Billing Details" name="billing-details"><i class="icon-caret-right"></i> <input type="submit" value = "Billing Details" name="billing-details"><i class="icon-caret-right"></i>
<p> <p>
...@@ -172,8 +170,14 @@ from django.utils.translation import ugettext as _ ...@@ -172,8 +170,14 @@ from django.utils.translation import ugettext as _
} }
) )
.success(function(data) { .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) { .error(function(data,status) {
if(status=="parsererror"){ if(status=="parsererror"){
location.reload(true); location.reload(true);
...@@ -199,23 +203,15 @@ from django.utils.translation import ugettext as _ ...@@ -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){ $("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(); event.preventDefault();
location.href = "${reverse('shoppingcart.views.billing_details')}"; 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