added course price set/view/edit functionality
added CoursmodeArchive model to save the old prices history analytics -> instructor_analytics analytics -> instructor_analytics
This commit is contained in:
committed by
Chris Dodge
parent
dc46170f36
commit
33d5b49e81
@@ -0,0 +1,64 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding model 'CourseModesArchive'
|
||||
db.create_table('course_modes_coursemodesarchive', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('course_id', self.gf('xmodule_django.models.CourseKeyField')(max_length=255, db_index=True)),
|
||||
('mode_slug', self.gf('django.db.models.fields.CharField')(max_length=100)),
|
||||
('mode_display_name', self.gf('django.db.models.fields.CharField')(max_length=255)),
|
||||
('min_price', self.gf('django.db.models.fields.IntegerField')(default=0)),
|
||||
('suggested_prices', self.gf('django.db.models.fields.CommaSeparatedIntegerField')(default='', max_length=255, blank=True)),
|
||||
('currency', self.gf('django.db.models.fields.CharField')(default='usd', max_length=8)),
|
||||
('expiration_date', self.gf('django.db.models.fields.DateField')(default=None, null=True, blank=True)),
|
||||
('expiration_datetime', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True, blank=True)),
|
||||
))
|
||||
db.send_create_signal('course_modes', ['CourseModesArchive'])
|
||||
|
||||
|
||||
# Changing field 'CourseMode.course_id'
|
||||
db.alter_column('course_modes_coursemode', 'course_id', self.gf('xmodule_django.models.CourseKeyField')(max_length=255))
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'CourseModesArchive'
|
||||
db.delete_table('course_modes_coursemodesarchive')
|
||||
|
||||
|
||||
# Changing field 'CourseMode.course_id'
|
||||
db.alter_column('course_modes_coursemode', 'course_id', self.gf('django.db.models.fields.CharField')(max_length=255))
|
||||
|
||||
models = {
|
||||
'course_modes.coursemode': {
|
||||
'Meta': {'unique_together': "(('course_id', 'mode_slug', 'currency'),)", 'object_name': 'CourseMode'},
|
||||
'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}),
|
||||
'expiration_date': ('django.db.models.fields.DateField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
||||
'expiration_datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'min_price': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'mode_display_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'mode_slug': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'suggested_prices': ('django.db.models.fields.CommaSeparatedIntegerField', [], {'default': "''", 'max_length': '255', 'blank': 'True'})
|
||||
},
|
||||
'course_modes.coursemodesarchive': {
|
||||
'Meta': {'object_name': 'CourseModesArchive'},
|
||||
'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}),
|
||||
'expiration_date': ('django.db.models.fields.DateField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
||||
'expiration_datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'min_price': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'mode_display_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'mode_slug': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'suggested_prices': ('django.db.models.fields.CommaSeparatedIntegerField', [], {'default': "''", 'max_length': '255', 'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['course_modes']
|
||||
@@ -128,3 +128,33 @@ class CourseMode(models.Model):
|
||||
return u"{} : {}, min={}, prices={}".format(
|
||||
self.course_id.to_deprecated_string(), self.mode_slug, self.min_price, self.suggested_prices
|
||||
)
|
||||
|
||||
|
||||
class CourseModesArchive(models.Model):
|
||||
"""
|
||||
Store the CourseModesArchives in this model
|
||||
|
||||
"""
|
||||
# the course that this mode is attached to
|
||||
course_id = CourseKeyField(max_length=255, db_index=True)
|
||||
|
||||
# the reference to this mode that can be used by Enrollments to generate
|
||||
# similar behavior for the same slug across courses
|
||||
mode_slug = models.CharField(max_length=100)
|
||||
|
||||
# The 'pretty' name that can be translated and displayed
|
||||
mode_display_name = models.CharField(max_length=255)
|
||||
|
||||
# minimum price in USD that we would like to charge for this mode of the course
|
||||
min_price = models.IntegerField(default=0)
|
||||
|
||||
# the suggested prices for this mode
|
||||
suggested_prices = models.CommaSeparatedIntegerField(max_length=255, blank=True, default='')
|
||||
|
||||
# the currency these prices are in, using lower case ISO currency codes
|
||||
currency = models.CharField(default="usd", max_length=8)
|
||||
|
||||
# turn this mode off after the given expiration date
|
||||
expiration_date = models.DateField(default=None, null=True, blank=True)
|
||||
|
||||
expiration_datetime = models.DateTimeField(default=None, null=True, blank=True)
|
||||
|
||||
@@ -16,12 +16,10 @@ from mock import patch
|
||||
from student.roles import CourseFinanceAdminRole
|
||||
|
||||
|
||||
# pylint: disable=E1101
|
||||
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
|
||||
class TestECommerceDashboardViews(ModuleStoreTestCase):
|
||||
"""
|
||||
Check for email view on the new instructor dashboard
|
||||
for Mongo-backed courses
|
||||
Check for E-commerce view on the new instructor dashboard
|
||||
"""
|
||||
def setUp(self):
|
||||
self.course = CourseFactory.create()
|
||||
@@ -71,6 +69,75 @@ class TestECommerceDashboardViews(ModuleStoreTestCase):
|
||||
self.assertFalse('Download All e-Commerce Purchase' in response.content)
|
||||
self.assertFalse('<span>Total Amount: <span>$' + str(total_amount) + '</span></span>' in response.content)
|
||||
|
||||
def test_user_view_course_price(self):
|
||||
"""
|
||||
test to check if the user views the set price button and price in
|
||||
the instructor dashboard
|
||||
"""
|
||||
response = self.client.get(self.url)
|
||||
self.assertTrue(self.e_commerce_link in response.content)
|
||||
|
||||
# Total amount html should render in e-commerce page, total amount will be 0
|
||||
course_honor_mode = CourseMode.mode_for_course(self.course.id, 'honor')
|
||||
|
||||
price = course_honor_mode.min_price
|
||||
self.assertTrue('Course Price: <span>$' + str(price) + '</span>' in response.content)
|
||||
self.assertFalse('+ Set Price</a></span>' in response.content)
|
||||
|
||||
# removing the course finance_admin role of login user
|
||||
CourseFinanceAdminRole(self.course.id).remove_users(self.instructor)
|
||||
|
||||
# total amount should not be visible in e-commerce page if the user is not finance admin
|
||||
url = reverse('instructor_dashboard', kwargs={'course_id': self.course.id.to_deprecated_string()})
|
||||
response = self.client.get(url)
|
||||
self.assertFalse('+ Set Price</a></span>' in response.content)
|
||||
|
||||
def test_update_course_price_check(self):
|
||||
price = 200
|
||||
# course B
|
||||
course2 = CourseFactory.create(org='EDX', display_name='test_course', number='100')
|
||||
mode = CourseMode(
|
||||
course_id=course2.id.to_deprecated_string(), mode_slug='honor',
|
||||
mode_display_name='honor', min_price=30, currency='usd'
|
||||
)
|
||||
mode.save()
|
||||
# course A update
|
||||
CourseMode.objects.filter(course_id=self.course.id).update(min_price=price)
|
||||
|
||||
set_course_price_url = reverse('set_course_mode_price', kwargs={'course_id': self.course.id.to_deprecated_string()})
|
||||
data = {'course_price': price, 'currency': 'usd'}
|
||||
response = self.client.post(set_course_price_url, data)
|
||||
self.assertTrue('CourseMode price updated successfully' in response.content)
|
||||
|
||||
# Course A updated total amount should be visible in e-commerce page if the user is finance admin
|
||||
url = reverse('instructor_dashboard', kwargs={'course_id': self.course.id.to_deprecated_string()})
|
||||
response = self.client.get(url)
|
||||
|
||||
self.assertTrue('Course Price: <span>$' + str(price) + '</span>' in response.content)
|
||||
|
||||
def test_user_admin_set_course_price(self):
|
||||
"""
|
||||
test to set the course price related functionality.
|
||||
test al the scenarios for setting a new course price
|
||||
"""
|
||||
set_course_price_url = reverse('set_course_mode_price', kwargs={'course_id': self.course.id.to_deprecated_string()})
|
||||
data = {'course_price': '12%', 'currency': 'usd'}
|
||||
|
||||
# Value Error course price should be a numeric value
|
||||
response = self.client.post(set_course_price_url, data)
|
||||
self.assertTrue("Please Enter the numeric value for the course price" in response.content)
|
||||
|
||||
# validation check passes and course price is successfully added
|
||||
data['course_price'] = 100
|
||||
response = self.client.post(set_course_price_url, data)
|
||||
self.assertTrue("CourseMode price updated successfully" in response.content)
|
||||
|
||||
course_honor_mode = CourseMode.objects.get(mode_slug='honor')
|
||||
course_honor_mode.delete()
|
||||
# Course Mode not exist with mode slug honor
|
||||
response = self.client.post(set_course_price_url, data)
|
||||
self.assertTrue("CourseMode with the mode slug({mode_slug}) DoesNotExist".format(mode_slug='honor') in response.content)
|
||||
|
||||
def test_add_coupon(self):
|
||||
"""
|
||||
Test Add Coupon Scenarios. Handle all the HttpResponses return by add_coupon view
|
||||
@@ -221,9 +288,9 @@ class TestECommerceDashboardViews(ModuleStoreTestCase):
|
||||
percentage_discount=20, created_by=self.instructor
|
||||
)
|
||||
coupon1.save()
|
||||
data = {'coupon_id': coupon.id, 'code': '11111', 'discount': '12'}
|
||||
data = {'coupon_id': coupon.id, 'code': '11111', 'discount': '12'} # pylint: disable=E1101
|
||||
response = self.client.post(update_coupon_url, data=data)
|
||||
self.assertTrue('coupon with the coupon id ({coupon_id}) already exist'.format(coupon_id=coupon.id) in response.content)
|
||||
self.assertTrue('coupon with the coupon id ({coupon_id}) already exist'.format(coupon_id=coupon.id) in response.content) # pylint: disable=E1101
|
||||
|
||||
course_registration = CourseRegistrationCode(
|
||||
code='Vs23Ws4j', course_id=self.course.id.to_deprecated_string(),
|
||||
@@ -231,8 +298,8 @@ class TestECommerceDashboardViews(ModuleStoreTestCase):
|
||||
)
|
||||
course_registration.save()
|
||||
|
||||
data = {'coupon_id': coupon.id, 'code': 'Vs23Ws4j',
|
||||
'discount': '6', 'course_id': coupon.course_id.to_deprecated_string()}
|
||||
data = {'coupon_id': coupon.id, 'code': 'Vs23Ws4j', # pylint: disable=E1101
|
||||
'discount': '6', 'course_id': coupon.course_id.to_deprecated_string()} # pylint: disable=E1101
|
||||
response = self.client.post(update_coupon_url, data=data)
|
||||
self.assertTrue("The code ({code}) that you have tried to define is already in use as a registration code".
|
||||
format(code=data['code']) in response.content)
|
||||
|
||||
@@ -576,8 +576,8 @@ def get_purchase_transaction(request, course_id, csv=False): # pylint: disable=
|
||||
}
|
||||
return JsonResponse(response_payload)
|
||||
else:
|
||||
header, datarows = analytics.csvs.format_dictlist(student_data, query_features)
|
||||
return analytics.csvs.create_csv_response("e-commerce_purchase_transactions.csv", header, datarows)
|
||||
header, datarows = instructor_analytics.csvs.format_dictlist(student_data, query_features)
|
||||
return instructor_analytics.csvs.create_csv_response("e-commerce_purchase_transactions.csv", header, datarows)
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@@ -671,7 +671,7 @@ def registration_codes_csv(file_name, codes_list, csv_type=None):
|
||||
|
||||
registration_codes = instructor_analytics.basic.course_registration_features(query_features, codes_list, csv_type)
|
||||
header, data_rows = instructor_analytics.csvs.format_dictlist(registration_codes, query_features)
|
||||
return analytics.csvs.create_csv_response(file_name, header, data_rows)
|
||||
return instructor_analytics.csvs.create_csv_response(file_name, header, data_rows)
|
||||
|
||||
|
||||
def random_code_generator():
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
"""
|
||||
Instructor Dashboard Views
|
||||
"""
|
||||
from django.views.decorators.http import require_POST
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
import logging
|
||||
|
||||
import datetime
|
||||
import pytz
|
||||
from django.utils.translation import ugettext as _
|
||||
from django_future.csrf import ensure_csrf_cookie
|
||||
from django.views.decorators.cache import cache_control
|
||||
@@ -11,7 +14,7 @@ from edxmako.shortcuts import render_to_response
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.html import escape
|
||||
from django.http import Http404
|
||||
from django.http import Http404, HttpResponse, HttpResponseNotFound
|
||||
from django.conf import settings
|
||||
|
||||
from lms.lib.xblock.runtime import quote_slashes
|
||||
@@ -27,7 +30,7 @@ from django_comment_client.utils import has_forum_access
|
||||
from django_comment_common.models import FORUM_ROLE_ADMINISTRATOR
|
||||
from student.models import CourseEnrollment
|
||||
from shoppingcart.models import Coupon, PaidCourseRegistration
|
||||
from course_modes.models import CourseMode
|
||||
from course_modes.models import CourseMode, CourseModesArchive
|
||||
from student.roles import CourseFinanceAdminRole
|
||||
|
||||
from bulk_email.models import CourseAuthorization
|
||||
@@ -130,6 +133,10 @@ def _section_e_commerce(course_key, access):
|
||||
""" Provide data for the corresponding dashboard section """
|
||||
coupons = Coupon.objects.filter(course_id=course_key).order_by('-is_active')
|
||||
total_amount = None
|
||||
course_price = None
|
||||
course_honor_mode = CourseMode.mode_for_course(course_key, 'honor')
|
||||
if course_honor_mode and course_honor_mode.min_price > 0:
|
||||
course_price = course_honor_mode.min_price
|
||||
if access['finance_admin']:
|
||||
total_amount = PaidCourseRegistration.get_total_amount_of_purchased_item(course_key)
|
||||
|
||||
@@ -148,12 +155,46 @@ def _section_e_commerce(course_key, access):
|
||||
'generate_registration_code_csv_url': reverse('generate_registration_codes', kwargs={'course_id': course_key.to_deprecated_string()}),
|
||||
'active_registration_code_csv_url': reverse('active_registration_codes', kwargs={'course_id': course_key.to_deprecated_string()}),
|
||||
'spent_registration_code_csv_url': reverse('spent_registration_codes', kwargs={'course_id': course_key.to_deprecated_string()}),
|
||||
'set_course_mode_url': reverse('set_course_mode_price', kwargs={'course_id': course_key.to_deprecated_string()}),
|
||||
'coupons': coupons,
|
||||
'total_amount': total_amount,
|
||||
'course_price': course_price
|
||||
}
|
||||
return section_data
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
|
||||
@require_POST
|
||||
@login_required
|
||||
def set_course_mode_price(request, course_id):
|
||||
"""
|
||||
set the new course price and add new entry in the CourseModesArchive Table
|
||||
"""
|
||||
try:
|
||||
course_price = int(request.POST['course_price'])
|
||||
except ValueError:
|
||||
return HttpResponseNotFound(_("Please Enter the numeric value for the course price"))
|
||||
currency = request.POST['currency']
|
||||
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
|
||||
|
||||
course_honor_mode = CourseMode.objects.filter(mode_slug='honor', course_id=course_key)
|
||||
if not course_honor_mode:
|
||||
return HttpResponseNotFound(
|
||||
_("CourseMode with the mode slug({mode_slug}) DoesNotExist").format(mode_slug='honor')
|
||||
)
|
||||
CourseModesArchive.objects.create(
|
||||
course_id=course_id, mode_slug='honor', mode_display_name='Honor Code Certificate',
|
||||
min_price=getattr(course_honor_mode[0], 'min_price'), currency=getattr(course_honor_mode[0], 'currency'),
|
||||
expiration_datetime=datetime.datetime.now(pytz.utc), expiration_date=datetime.date.today()
|
||||
)
|
||||
course_honor_mode.update(
|
||||
min_price=course_price,
|
||||
currency=currency
|
||||
)
|
||||
return HttpResponse(_("CourseMode price updated successfully"))
|
||||
|
||||
|
||||
def _section_course_info(course_key, access):
|
||||
""" Provide data for the corresponding dashboard section """
|
||||
course = get_course_by_id(course_key, depth=None)
|
||||
|
||||
@@ -11,10 +11,10 @@ class ECommerce
|
||||
# gather elements
|
||||
@$list_purchase_csv_btn = @$section.find("input[name='list-purchase-transaction-csv']'")
|
||||
@$transaction_group_name = @$section.find("input[name='transaction_group_name']'")
|
||||
@$transaction_group_name = @$section.find("input[name='transaction_group_name']'")
|
||||
@$download_transaction_group_name = @$section.find("input[name='transaction_group_name']'")
|
||||
@$active_transaction_group_name = @$section.find("input[name='transaction_group_name']'")
|
||||
@$spent_transaction_group_name = @$section.find('input[name="course_registration_code_number"]')
|
||||
@$course_registration_number = @$section.find("input[name='course_registration_code_number']'")
|
||||
@$download_transaction_group_name = @$section.find("input[name='download_transaction_group_name']'")
|
||||
@$active_transaction_group_name = @$section.find("input[name='active_transaction_group_name']'")
|
||||
@$spent_transaction_group_name = @$section.find('input[name="spent_transaction_group_name"]')
|
||||
|
||||
@$generate_registration_code_form = @$section.find("form#course_codes_number")
|
||||
@$download_registration_codes_form = @$section.find("form#download_registration_codes")
|
||||
|
||||
@@ -945,7 +945,7 @@ input[name="subject"] {
|
||||
}
|
||||
|
||||
// coupon edit and add modals
|
||||
#add-coupon-modal, #edit-coupon-modal{
|
||||
#add-coupon-modal, #edit-coupon-modal, #set-course-mode-price-modal{
|
||||
.inner-wrapper {
|
||||
background: #fff;
|
||||
}
|
||||
@@ -961,6 +961,10 @@ input[name="subject"] {
|
||||
@include button(simple, $blue);
|
||||
@extend .button-reset;
|
||||
}
|
||||
input[type="submit"]#set_course_button{
|
||||
@include button(simple, $blue);
|
||||
@extend .button-reset;
|
||||
}
|
||||
.modal-form-error {
|
||||
box-shadow: inset 0 -1px 2px 0 #f3d9db;
|
||||
-webkit-box-sizing: border-box;
|
||||
@@ -997,9 +1001,21 @@ input[name="subject"] {
|
||||
li:last-child{
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
|
||||
}
|
||||
#coupon-content {
|
||||
li#set-course-mode-modal-field-price{
|
||||
width: 100%;
|
||||
label.required:after {
|
||||
content: "*";
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
li#set-course-mode-modal-field-currency{
|
||||
margin-left: 0px !important;
|
||||
select {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
#coupon-content, #course-content {
|
||||
padding: 20px;
|
||||
header {
|
||||
margin: 0;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<%page args="section_data"/>
|
||||
<%include file="add_coupon_modal.html" args="section_data=section_data" />
|
||||
<%include file="edit_coupon_modal.html" args="section_data=section_data" />
|
||||
<%include file="set_course_mode_price_modal.html" args="section_data=section_data" />
|
||||
|
||||
<div class="ecommerce-wrapper">
|
||||
<h3 class="coupon-errors" id="code-error"></h3>
|
||||
@@ -43,18 +44,24 @@
|
||||
</form>
|
||||
</p>
|
||||
<hr>
|
||||
<h2>${_("Course Price")}</h2>
|
||||
<span class="tip">${_("Course Price: ")}<span>$${section_data['course_price']}</span>
|
||||
%if section_data['access']['finance_admin'] is True:
|
||||
<a id="course_price_link" href="#set-course-mode-price-modal" rel="leanModal" class="add blue-button">+ Set Price</a>
|
||||
%endif
|
||||
</span>
|
||||
<hr>
|
||||
%if section_data['access']['finance_admin'] is True:
|
||||
<h2>${_("Transactions")}</h2>
|
||||
%if section_data['total_amount'] is not None:
|
||||
<span>${_("Total Amount: ")}<span>$${section_data['total_amount']}</span></span>
|
||||
%endif
|
||||
|
||||
<h2>${_("Transactions")}</h2>
|
||||
%if section_data['total_amount'] is not None:
|
||||
<span>${_("Total Amount: ")}<span>$${section_data['total_amount']}</span></span>
|
||||
<p>${_("Click to generate a CSV file for all purchase transactions in this course")}</p>
|
||||
|
||||
<p><input type="button" name="list-purchase-transaction-csv" value="${_("Download All e-Commerce Purchases")}" data-endpoint="${ section_data['get_purchase_transaction_url'] }" data-csv="true"></p>
|
||||
%endif
|
||||
|
||||
<p>${_("Click to generate a CSV file for all purchase transactions in this course")}</p>
|
||||
|
||||
<p><input type="button" name="list-purchase-transaction-csv" value="${_("Download All e-Commerce Purchases")}" data-endpoint="${ section_data['get_purchase_transaction_url'] }" data-csv="true"></p>
|
||||
%endif
|
||||
|
||||
<h2>${_("Coupons List")}</h2>
|
||||
|
||||
<h3 class="coupon-errors" id="coupon-error"></h3>
|
||||
@@ -202,9 +209,38 @@
|
||||
return false;
|
||||
}
|
||||
});
|
||||
$('#course_price_link').click(function () {
|
||||
reset_input_fields();
|
||||
});
|
||||
$('#add_coupon_link').click(function () {
|
||||
reset_input_fields();
|
||||
});
|
||||
$('#set_price_form').submit(function () {
|
||||
$("#set_course_button").attr('disabled', true);
|
||||
// Get the Code and Discount value and trim it
|
||||
var course_price = $.trim($('#mode_price').val());
|
||||
var currency = $.trim($('#course_mode_currency').val());
|
||||
|
||||
// Check if empty of not
|
||||
if (course_price === '') {
|
||||
$('#set_price_form #course_form_error').attr('style', 'display: block !important');
|
||||
$('#set_price_form #course_form_error').text("${_('Please Enter the Course Price')}");
|
||||
$("#set_course_button").removeAttr('disabled');
|
||||
return false;
|
||||
}
|
||||
if (!$.isNumeric(course_price)) {
|
||||
$("#set_course_button").removeAttr('disabled');
|
||||
$('#set_price_form #course_form_error').attr('style', 'display: block !important');
|
||||
$('#set_price_form #course_form_error').text("${_('Please Enter the Numeric value for Discount')}");
|
||||
return false;
|
||||
}
|
||||
if (currency == '') {
|
||||
$('#set_price_form #course_form_error').attr('style', 'display: block !important');
|
||||
$('#set_price_form #course_form_error').text("${_('Please Select the Currency')}");
|
||||
$("#set_course_button").removeAttr('disabled');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
$('#add_coupon_form').submit(function () {
|
||||
$("#add_coupon_button").attr('disabled', true);
|
||||
// Get the Code and Discount value and trim it
|
||||
@@ -238,6 +274,16 @@
|
||||
}
|
||||
});
|
||||
|
||||
$('#set_price_form').on('ajax:complete', function (event, xhr) {
|
||||
if (xhr.status == 200) {
|
||||
location.reload(true);
|
||||
} else {
|
||||
$("#set_course_button").removeAttr('disabled');
|
||||
$('#set_price_form #course_form_error').attr('style', 'display: block !important');
|
||||
$('#set_price_form #course_form_error').text(xhr.responseText);
|
||||
}
|
||||
});
|
||||
|
||||
$('#add_coupon_form').on('ajax:complete', function (event, xhr) {
|
||||
if (xhr.status == 200) {
|
||||
location.reload(true);
|
||||
@@ -261,6 +307,7 @@
|
||||
$('.close-modal').click(function (e) {
|
||||
$("#update_coupon_button").removeAttr('disabled');
|
||||
$("#add_coupon_button").removeAttr('disabled');
|
||||
$("#set_course_button").removeAttr('disabled');
|
||||
reset_input_fields();
|
||||
e.preventDefault();
|
||||
});
|
||||
@@ -270,7 +317,9 @@
|
||||
$(".remove_coupon").focus();
|
||||
$("#edit-coupon-modal").attr("aria-hidden", "true");
|
||||
$(".edit-right").focus();
|
||||
$("#set-course-mode-price-modal").attr("aria-hidden", "true");
|
||||
$("#add_coupon_button").removeAttr('disabled');
|
||||
$("#set_course_button").removeAttr('disabled');
|
||||
$("#update_coupon_button").removeAttr('disabled');
|
||||
reset_input_fields();
|
||||
};
|
||||
@@ -288,16 +337,17 @@
|
||||
|
||||
$("#add-coupon-modal .close-modal").click(onModalClose);
|
||||
$("#edit-coupon-modal .close-modal").click(onModalClose);
|
||||
$("#add-coupon-modal .close-modal").click(reset_input_fields);
|
||||
$("#set-course-mode-price-modal .close-modal").click(reset_input_fields);
|
||||
|
||||
|
||||
// Hitting the ESC key will exit the modal
|
||||
$("#add-coupon-modal, #edit-coupon-modal").on("keydown", function (e) {
|
||||
$("#add-coupon-modal, #edit-coupon-modal, #set-course-mode-price-modal").on("keydown", function (e) {
|
||||
var keyCode = e.keyCode || e.which;
|
||||
// 27 is the ESC key
|
||||
if (keyCode === 27) {
|
||||
e.preventDefault();
|
||||
$("#add-coupon-modal .close-modal").click();
|
||||
$("#set-course-mode-price-modal .close-modal").click();
|
||||
$("#edit-coupon-modal .close-modal").click();
|
||||
}
|
||||
});
|
||||
@@ -306,7 +356,9 @@
|
||||
$('#coupon-error').val('');
|
||||
$('#coupon-error').attr('style', 'display: none');
|
||||
$('#add_coupon_form #coupon_form_error').attr('style', 'display: none');
|
||||
$('#set_price_form #course_form_error').attr('style', 'display: none');
|
||||
$('#add_coupon_form #coupon_form_error').text();
|
||||
$('input#mode_price').val('');
|
||||
$('input#coupon_code').val('');
|
||||
$('input#coupon_discount').val('');
|
||||
$('textarea#coupon_description').val('');
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
<%! from django.core.urlresolvers import reverse %>
|
||||
<%page args="section_data"/>
|
||||
<section id="set-course-mode-price-modal" class="modal" role="dialog" tabindex="-1" aria-label="${_('Set Course Mode Price')}">
|
||||
<div class="inner-wrapper">
|
||||
<button class="close-modal">
|
||||
<i class="icon-remove"></i>
|
||||
<span class="sr">
|
||||
## Translators: this is a control to allow users to exit out of this modal interface (a menu or piece of UI that takes the full focus of the screen)
|
||||
${_('Close')}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div id="course-content">
|
||||
<header>
|
||||
<h2>${_("Set Course Mode Price")}</h2>
|
||||
</header>
|
||||
|
||||
<div class="instructions">
|
||||
<p>
|
||||
${_("Please enter Course Mode detail below")}</p>
|
||||
</div>
|
||||
|
||||
<form id="set_price_form" action="${section_data['set_course_mode_url']}" method="post" data-remote="true">
|
||||
<div id="course_form_error" class="modal-form-error"></div>
|
||||
<fieldset class="group group-form group-form-requiredinformation">
|
||||
<legend class="is-hidden">${_("Required Information")}</legend>
|
||||
|
||||
<ol class="list-input">
|
||||
<li class="field required text" id="set-course-mode-modal-field-price">
|
||||
<label for="mode_price" class="required">${_("Course Price")}</label>
|
||||
<input class="field" id="mode_price" type="text" name="course_price" placeholder="${section_data['course_price']}" aria-required="true">
|
||||
</li>
|
||||
<li class="field required text" id="set-course-mode-modal-field-currency">
|
||||
<label for="course_mode_currency" class="required text">${_("Currency")}</label>
|
||||
<select class="field required" id="course_mode_currency" name="currency">
|
||||
<option value="usd">USD</option>
|
||||
</select>
|
||||
</li>
|
||||
</ol>
|
||||
</fieldset>
|
||||
<div class="submit">
|
||||
<input name="submit" type="submit" id="set_course_button" value="${_('Set Price')}"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -280,6 +280,8 @@ if settings.COURSEWARE_ENABLED:
|
||||
# For the instructor
|
||||
url(r'^courses/{}/instructor$'.format(settings.COURSE_ID_PATTERN),
|
||||
'instructor.views.instructor_dashboard.instructor_dashboard_2', name="instructor_dashboard"),
|
||||
url(r'^courses/{}/set_course_mode_price$'.format(settings.COURSE_ID_PATTERN),
|
||||
'instructor.views.instructor_dashboard.set_course_mode_price', name="set_course_mode_price"),
|
||||
url(r'^courses/{}/instructor/api/'.format(settings.COURSE_ID_PATTERN),
|
||||
include('instructor.views.api_urls')),
|
||||
url(r'^courses/{}/remove_coupon$'.format(settings.COURSE_ID_PATTERN),
|
||||
|
||||
Reference in New Issue
Block a user