Commit d383e1ed by Matt Drayer Committed by GitHub

Merge branch 'master' into malikshahzad228/MAYN-271

parents 9b82fcf5 af9f2d4a
...@@ -80,6 +80,27 @@ class CouponViewSetTest(CouponMixin, CourseCatalogTestMixin, TestCase): ...@@ -80,6 +80,27 @@ class CouponViewSetTest(CouponMixin, CourseCatalogTestMixin, TestCase):
site.siteconfiguration = site_configuration site.siteconfiguration = site_configuration
return site return site
def test_retrieve_invoice_data(self):
request_data = {
'invoice_discount_type': Invoice.PERCENTAGE,
'invoice_discount_value': 50,
'invoice_number': 'INV-00055',
'invoice_payment_date': datetime.datetime(2016, 1, 1, tzinfo=pytz.UTC).isoformat(),
'invoice_type': Invoice.PREPAID,
'tax_deducted_source': None
}
invoice_data = CouponViewSet().retrieve_invoice_data(request_data)
self.assertDictEqual(invoice_data, {
'discount_type': request_data['invoice_discount_type'],
'discount_value': request_data['invoice_discount_value'],
'number': request_data['invoice_number'],
'payment_date': request_data['invoice_payment_date'],
'type': request_data['invoice_type'],
'tax_deducted_source': request_data['tax_deducted_source']
})
@ddt.data( @ddt.data(
(Voucher.ONCE_PER_CUSTOMER, 2, 2), (Voucher.ONCE_PER_CUSTOMER, 2, 2),
(Voucher.SINGLE_USE, 2, None) (Voucher.SINGLE_USE, 2, None)
...@@ -526,6 +547,20 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CatalogPr ...@@ -526,6 +547,20 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CatalogPr
baskets = Basket.objects.filter(lines__product_id=coupon.id) baskets = Basket.objects.filter(lines__product_id=coupon.id)
self.assertEqual(baskets.first().owner.username, 'Test Client Username') self.assertEqual(baskets.first().owner.username, 'Test Client Username')
def test_update_invoice_data(self):
coupon = Product.objects.get(title='Test coupon')
invoice = Invoice.objects.get(order__basket__lines__product=coupon)
self.assertEqual(invoice.discount_type, Invoice.PERCENTAGE)
CouponViewSet().update_invoice_data(
coupon=coupon,
data={
'invoice_discount_type': Invoice.FIXED
}
)
invoice = Invoice.objects.get(order__basket__lines__product=coupon)
self.assertEqual(invoice.discount_type, Invoice.FIXED)
@ddt.data('audit', 'honor') @ddt.data('audit', 'honor')
def test_restricted_course_mode(self, mode): def test_restricted_course_mode(self, mode):
"""Test that an exception is raised when a black-listed course mode is used.""" """Test that an exception is raised when a black-listed course mode is used."""
......
...@@ -43,6 +43,21 @@ Voucher = get_model('voucher', 'Voucher') ...@@ -43,6 +43,21 @@ Voucher = get_model('voucher', 'Voucher')
CATALOG_QUERY = 'catalog_query' CATALOG_QUERY = 'catalog_query'
CLIENT = 'client' CLIENT = 'client'
COURSE_SEAT_TYPES = 'course_seat_types' COURSE_SEAT_TYPES = 'course_seat_types'
INVOICE_DISCOUNT_TYPE = 'invoice_discount_type'
INVOICE_DISCOUNT_VALUE = 'invoice_discount_value'
INVOICE_NUMBER = 'invoice_number'
INVOICE_PAYMENT_DATE = 'invoice_payment_date'
INVOICE_TYPE = 'invoice_type'
TAX_DEDUCTED_SOURCE = 'tax_deducted_source'
UPDATEABLE_INVOICE_FIELDS = [
INVOICE_DISCOUNT_TYPE,
INVOICE_DISCOUNT_VALUE,
INVOICE_NUMBER,
INVOICE_PAYMENT_DATE,
INVOICE_TYPE,
TAX_DEDUCTED_SOURCE,
]
UPDATABLE_RANGE_FIELDS = [ UPDATABLE_RANGE_FIELDS = [
CATALOG_QUERY, CATALOG_QUERY,
...@@ -64,14 +79,17 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet): ...@@ -64,14 +79,17 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet):
def retrieve_invoice_data(self, request_data): def retrieve_invoice_data(self, request_data):
""" Retrieve the invoice information from the request data. """ """ Retrieve the invoice information from the request data. """
return { invoice_data = {}
'number': request_data.get('invoice_number'),
'type': request_data.get('invoice_type'), for field in UPDATEABLE_INVOICE_FIELDS:
'payment_date': request_data.get('invoice_payment_date'), self.create_update_data_dict(
'discount_type': request_data.get('invoice_discount_type'), request_data=request_data,
'discount_value': request_data.get('invoice_discount_value'), request_data_key=field,
'tax_deducted_source': request_data.get('tax_deducted_source'), update_dict=invoice_data,
} update_dict_key=field.replace('invoice_', '')
)
return invoice_data
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
"""Adds coupon to the user's basket. """Adds coupon to the user's basket.
...@@ -373,8 +391,8 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet): ...@@ -373,8 +391,8 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet):
update_dict (dict): Dictionary containing the coupon update data update_dict (dict): Dictionary containing the coupon update data
update_dict_key (str): Update data dictionary key update_dict_key (str): Update data dictionary key
""" """
value = request_data.get(request_data_key, '') if request_data_key in request_data:
if value: value = request_data.get(request_data_key)
update_dict[update_dict_key] = prepare_course_seat_types(value) \ update_dict[update_dict_key] = prepare_course_seat_types(value) \
if update_dict_key == COURSE_SEAT_TYPES else value if update_dict_key == COURSE_SEAT_TYPES else value
...@@ -442,9 +460,10 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet): ...@@ -442,9 +460,10 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet):
data (dict): The request's data from which the invoice data is retrieved data (dict): The request's data from which the invoice data is retrieved
and used for the updated. and used for the updated.
""" """
invoice = Invoice.objects.filter(order__basket__lines__product=coupon) invoice_data = self.retrieve_invoice_data(data)
update_data = self.retrieve_invoice_data(data)
invoice.update(**update_data) if invoice_data:
Invoice.objects.filter(order__basket__lines__product=coupon).update(**invoice_data)
def destroy(self, request, pk): # pylint: disable=unused-argument def destroy(self, request, pk): # pylint: disable=unused-argument
try: try:
......
...@@ -62,13 +62,29 @@ define([ ...@@ -62,13 +62,29 @@ define([
} }
}, },
quantity: {pattern: 'number'}, quantity: {pattern: 'number'},
price: {pattern: 'number'},
benefit_value: { benefit_value: {
pattern: 'number', pattern: 'number',
required: function () { required: function () {
return this.get('coupon_type') === 'Discount code'; return this.get('coupon_type') === 'Discount code';
} }
}, },
invoice_type: {required: true},
invoice_number: {
required: function() {
return this.isPrepaidInvoiceType();
}
},
price: {
pattern: 'number',
required: function() {
return this.isPrepaidInvoiceType();
}
},
invoice_payment_date: {
required: function() {
return this.isPrepaidInvoiceType();
}
},
invoice_discount_value: { invoice_discount_value: {
pattern: 'number', pattern: 'number',
required: function () { required: function () {
...@@ -123,6 +139,7 @@ define([ ...@@ -123,6 +139,7 @@ define([
}, },
initialize: function () { initialize: function () {
this.on('change:categories', this.updateCategory, this);
this.on('change:voucher_type', this.changeVoucherType, this); this.on('change:voucher_type', this.changeVoucherType, this);
this.on('change:vouchers', this.updateVoucherData); this.on('change:vouchers', this.updateVoucherData);
this.on('change:seats', this.updateSeatData); this.on('change:seats', this.updateSeatData);
...@@ -130,6 +147,10 @@ define([ ...@@ -130,6 +147,10 @@ define([
this.on('change:payment_information', this.updatePaymentInformation); this.on('change:payment_information', this.updatePaymentInformation);
}, },
isPrepaidInvoiceType: function() {
return this.get('invoice_type') === 'Prepaid';
},
/** /**
* When user selects the 'Single use' limitation option set quantity to '1'. * When user selects the 'Single use' limitation option set quantity to '1'.
*/ */
...@@ -158,6 +179,12 @@ define([ ...@@ -158,6 +179,12 @@ define([
return course_id ? course_id.value : ''; return course_id ? course_id.value : '';
}, },
updateCategory: function() {
var categoryID = this.get('categories')[0].id;
this.set('category', categoryID);
this.set('category_ids', [categoryID]);
},
updateSeatData: function () { updateSeatData: function () {
var seat_data, var seat_data,
seats = this.get('seats'); seats = this.get('seats');
...@@ -210,27 +237,37 @@ define([ ...@@ -210,27 +237,37 @@ define([
'invoice_payment_date': invoice.payment_date, 'invoice_payment_date': invoice.payment_date,
'tax_deducted_source': invoice.tax_deducted_source, 'tax_deducted_source': invoice.tax_deducted_source,
'tax_deduction': tax_deducted, 'tax_deduction': tax_deducted,
}); });
}, },
save: function (options) { save: function (attributes, options) {
_.defaults(options || (options = {}), { _.defaults(options || (options = {}), {
// The API requires a CSRF token for all POST requests using session authentication. // The API requires a CSRF token for all POST requests using session authentication.
headers: {'X-CSRFToken': Cookies.get('ecommerce_csrftoken')}, headers: {'X-CSRFToken': Cookies.get('ecommerce_csrftoken')},
contentType: 'application/json' contentType: 'application/json'
}); });
this.set('start_date', moment.utc(this.get('start_date'))); if (!options.patch){
this.set('end_date', moment.utc(this.get('end_date'))); this.set('start_date', moment.utc(this.get('start_date')));
this.set('category_ids', [this.get('category')]); this.set('end_date', moment.utc(this.get('end_date')));
if (this.get('coupon_type') === 'Enrollment code') {
this.set('benefit_type', 'Percentage');
this.set('benefit_value', 100);
}
options.data = JSON.stringify(this.toJSON());
} else {
if (_.has(attributes, 'start_date')) {
attributes.start_date = moment.utc(attributes.start_date);
}
if (this.get('coupon_type') === 'Enrollment code') { if (_.has(attributes, 'end_date')) {
this.set('benefit_type', 'Percentage'); attributes.end_date = moment.utc(attributes.end_date);
this.set('benefit_value', 100); }
} }
options.data = JSON.stringify(this.toJSON()); return this._super(attributes, options);
return this._super(null, options);
} }
}); });
} }
......
...@@ -178,7 +178,8 @@ define([], function(){ ...@@ -178,7 +178,8 @@ define([], function(){
seat_type: 'verified', seat_type: 'verified',
course: verifiedSeat, course: verifiedSeat,
price: 100, price: 100,
category: 4 category: 4,
invoice_type: 'Not-Applicable'
}, },
enrollmentCodeCouponData = { enrollmentCodeCouponData = {
'id': 10, 'id': 10,
......
...@@ -84,6 +84,22 @@ define([ ...@@ -84,6 +84,22 @@ define([
model.validate(); model.validate();
expect(model.isValid()).toBeTruthy(); expect(model.isValid()).toBeTruthy();
}); });
it('should validate invoice data.', function() {
model.set('price', 'text');
model.validate();
expect(model.isValid()).toBeFalsy();
model.set('price', 100);
model.validate();
expect(model.isValid()).toBeTruthy();
model.set('invoice_discount_value', 'text');
model.validate();
expect(model.isValid()).toBeFalsy();
model.set('invoice_discount_value', 100);
model.validate();
expect(model.isValid()).toBeTruthy();
});
}); });
describe('test model methods', function () { describe('test model methods', function () {
...@@ -129,6 +145,21 @@ define([ ...@@ -129,6 +145,21 @@ define([
ajaxData = JSON.parse(args[0].data); ajaxData = JSON.parse(args[0].data);
expect(ajaxData.quantity).toEqual(1); expect(ajaxData.quantity).toEqual(1);
}); });
it('should format start and end date if they are patch updated', function () {
var model = Coupon.findOrCreate(discountCodeData, {parse: true});
spyOn(moment, 'utc');
model.save(
{
start_date: '2015-11-11T00:00:00Z',
end_date: '2016-11-11T00:00:00Z'
},
{patch: true}
);
expect(moment.utc).toHaveBeenCalledWith('2015-11-11T00:00:00Z');
expect(moment.utc).toHaveBeenCalledWith('2016-11-11T00:00:00Z');
});
}); });
}); });
......
...@@ -30,18 +30,19 @@ define([ ...@@ -30,18 +30,19 @@ define([
it('should throw an error if submitted with blank fields', function () { it('should throw an error if submitted with blank fields', function () {
var errorHTML = '<strong>Error!</strong> You must complete all required fields.'; var errorHTML = '<strong>Error!</strong> You must complete all required fields.';
view.formView.submit($.Event('click')); view.formView.submit($.Event('click'));
expect(view.$el.find('.alert').length).toBe(1); expect(view.$('.alert').length).toBe(1);
expect(view.$el.find('.alert').html()).toBe(errorHTML); expect(view.$('.alert').html()).toBe(errorHTML);
}); });
it('should submit form with valid fields', function () { it('should submit form with valid fields', function () {
view.$el.find('[name=title]').val('Test Enrollment').trigger('change'); view.$('[name=title]').val('Test Enrollment').trigger('change');
view.$el.find('[name=code_type]').val('enrollment').trigger('change'); view.$('[name=code_type]').val('enrollment').trigger('change');
view.$el.find('[name=client]').val('test_client').trigger('change'); view.$('[name=client]').val('test_client').trigger('change');
view.$el.find('[name=start_date]').val('2015-01-01T00:00').trigger('change'); view.$('[name=start_date]').val('2015-01-01T00:00').trigger('change');
view.$el.find('[name=end_date]').val('2016-01-01T00:00').trigger('change'); view.$('[name=end_date]').val('2016-01-01T00:00').trigger('change');
view.$el.find('[name=price]').val('100').trigger('change'); view.$('[name=price]').val('100').trigger('change');
view.$el.find('[name=category]').val('4').trigger('change'); view.$('[name=category]').val('4').trigger('change');
view.$('#not-applicable').prop('checked', true).trigger('change');
spyOn(view.formView, 'fillFromCourse').and.callFake(function () { spyOn(view.formView, 'fillFromCourse').and.callFake(function () {
var seatTypes = [$('<option></option>') var seatTypes = [$('<option></option>')
.text('Verified') .text('Verified')
...@@ -50,12 +51,12 @@ define([ ...@@ -50,12 +51,12 @@ define([
price: '100', price: '100',
stockrecords: [1] stockrecords: [1]
})]; })];
this.$el.find('[name=seat_type]') this.$('[name=seat_type]')
.html(seatTypes) .html(seatTypes)
.trigger('change'); .trigger('change');
}); });
view.formView.delegateEvents(); view.formView.delegateEvents();
view.$el.find('[name=course_id]').val('course-v1:edX+DemoX+Demo_Course').trigger('input'); view.$('[name=course_id]').val('course-v1:edX+DemoX+Demo_Course').trigger('input');
view.formView.submit($.Event('click')); view.formView.submit($.Event('click'));
expect(model.isValid()).toBe(true); expect(model.isValid()).toBe(true);
expect(model.save).toHaveBeenCalled(); expect(model.save).toHaveBeenCalled();
......
...@@ -73,7 +73,7 @@ define([ ...@@ -73,7 +73,7 @@ define([
expect(view.$el.find('[name=code]').val()).toEqual(model.get('code')); expect(view.$el.find('[name=code]').val()).toEqual(model.get('code'));
}); });
}); });
describe('Coupon with invoice data', function() { describe('Coupon with invoice data', function() {
beforeEach(function() { beforeEach(function() {
model = Coupon.findOrCreate(invoice_coupon_data, {parse: true}); model = Coupon.findOrCreate(invoice_coupon_data, {parse: true});
...@@ -96,6 +96,17 @@ define([ ...@@ -96,6 +96,17 @@ define([
expect(view.$el.find('[name=tax_deducted_source_value]').val()) expect(view.$el.find('[name=tax_deducted_source_value]').val())
.toEqual(model.get('tax_deducted_source')); .toEqual(model.get('tax_deducted_source'));
}); });
it('should patch save the model when form is in editing mode and has editable attributes', function () {
var formView = view.formView;
spyOn(formView.model, 'save');
spyOn(formView.model, 'isValid').and.returnValue(true);
expect(formView.modelServerState).toEqual(model.pick(formView.editableAttributes));
formView.model.set('title', 'Test Title');
formView.submit($.Event('click'));
expect(model.save).toHaveBeenCalled();
});
}); });
}); });
} }
......
...@@ -86,7 +86,6 @@ define([ ...@@ -86,7 +86,6 @@ define([
}); });
}); });
describe('enrollment code', function () { describe('enrollment code', function () {
beforeEach(function () { beforeEach(function () {
view.$el.find('[name=code_type]').val('enrollment').trigger('change'); view.$el.find('[name=code_type]').val('enrollment').trigger('change');
...@@ -102,6 +101,16 @@ define([ ...@@ -102,6 +101,16 @@ define([
}); });
}); });
describe('routing', function() {
it('should route to external link.', function() {
var href = 'http://www.google.com/';
spyOn(window, 'open');
view.$el.append('<a href="' + href + '" class="test external-link">Google</a>');
view.$('.test.external-link').click();
expect(window.open).toHaveBeenCalledWith(href);
});
});
describe('discount code', function () { describe('discount code', function () {
var prepaid_invoice_fields = [ var prepaid_invoice_fields = [
'[name=invoice_number]', '[name=invoice_number]',
......
...@@ -139,7 +139,6 @@ define([ ...@@ -139,7 +139,6 @@ define([
errorObj = { responseJSON: { error: 'An error occurred while saving the data.' }}; errorObj = { responseJSON: { error: 'An error occurred while saving the data.' }};
testErrorResponse(); testErrorResponse();
}); });
}); });
} }
); );
...@@ -149,6 +149,7 @@ define([ ...@@ -149,6 +149,7 @@ define([
this.$el.html(html); this.$el.html(html);
this.renderVoucherTable(); this.renderVoucherTable();
this.renderCourseData(); this.renderCourseData();
this.renderInvoiceData();
this.delegateEvents(); this.delegateEvents();
this.dynamic_catalog_view = new DynamicCatalogView({ this.dynamic_catalog_view = new DynamicCatalogView({
...@@ -204,6 +205,48 @@ define([ ...@@ -204,6 +205,48 @@ define([
return this; return this;
}, },
renderInvoiceData: function() {
var invoice_type = this.model.get('invoice_type'),
tax_deducted = this.model.get('tax_deduction'),
prepaid_fields = [
'.invoice-number',
'.invoiced-amount',
'.invoice-payment-date'
],
postpaid_fields = [
'.invoice-discount-type',
'.invoice-discount-value'
];
if (tax_deducted === 'Yes') {
this.$('.tax-deducted-source-value').removeClass('hidden');
} else if (tax_deducted === 'No') {
this.$('.tax-deducted-source-value').addClass('hidden');
}
if (invoice_type === 'Prepaid') {
_.each(prepaid_fields, function(field) {
this.$(field).removeClass('hidden');
}, this);
_.each(postpaid_fields, function(field) {
this.$(field).addClass('hidden');
}, this);
} else if (invoice_type === 'Postpaid') {
_.each(prepaid_fields, function(field) {
this.$(field).addClass('hidden');
}, this);
_.each(postpaid_fields, function(field) {
this.$(field).removeClass('hidden');
}, this);
} else if (invoice_type === 'Not-Applicable') {
_.each(prepaid_fields, function(field) {
this.$(field).addClass('hidden');
}, this);
_.each(postpaid_fields, function(field) {
this.$(field).addClass('hidden');
}, this);
}
},
downloadCouponReport: function (event) { downloadCouponReport: function (event) {
var url = _s.sprintf('/api/v2/coupons/coupon_reports/%d', this.model.id); var url = _s.sprintf('/api/v2/coupons/coupon_reports/%d', this.model.id);
......
...@@ -79,6 +79,10 @@ define([ ...@@ -79,6 +79,10 @@ define([
}, },
setOptions: { setOptions: {
validate: true validate: true
},
onSet: function(val) {
this.model.set('category_ids', [val]);
return val;
} }
}, },
'input[name=title]': { 'input[name=title]': {
...@@ -217,7 +221,8 @@ define([ ...@@ -217,7 +221,8 @@ define([
'change [name=benefit_type]': 'changeLimitForBenefitValue', 'change [name=benefit_type]': 'changeLimitForBenefitValue',
'change [name=invoice_discount_type]': 'changeLimitForInvoiceDiscountValue', 'change [name=invoice_discount_type]': 'changeLimitForInvoiceDiscountValue',
'change [name=invoice_type]': 'toggleInvoiceFields', 'change [name=invoice_type]': 'toggleInvoiceFields',
'change [name=tax_deduction]': 'toggleTaxDeductedSourceField' 'change [name=tax_deduction]': 'toggleTaxDeductedSourceField',
'click .external-link': 'routeToLink'
}, },
initialize: function (options) { initialize: function (options) {
...@@ -225,6 +230,27 @@ define([ ...@@ -225,6 +230,27 @@ define([
this.editing = options.editing || false; this.editing = options.editing || false;
this.hiddenClass = 'hidden'; this.hiddenClass = 'hidden';
if (this.editing) {
this.editableAttributes = [
'benefit_value',
'catalog_query',
'category_ids',
'client',
'course_seat_types',
'end_date',
'invoice_discount_type',
'invoice_discount_value',
'invoice_number',
'invoice_payment_date',
'invoice_type',
'note',
'price',
'start_date',
'tax_deducted_source',
'title',
];
}
this.dynamic_catalog_view = new DynamicCatalogView({ this.dynamic_catalog_view = new DynamicCatalogView({
'query': this.model.get('catalog_query'), 'query': this.model.get('catalog_query'),
'seat_types': this.model.get('course_seat_types') 'seat_types': this.model.get('course_seat_types')
...@@ -302,7 +328,7 @@ define([ ...@@ -302,7 +328,7 @@ define([
this.formGroup('[name=code]').addClass(this.hiddenClass); this.formGroup('[name=code]').addClass(this.hiddenClass);
} }
}, },
toggleInvoiceFields: function () { toggleInvoiceFields: function () {
var invoice_type = this.$('[name=invoice_type]:checked').val(), var invoice_type = this.$('[name=invoice_type]:checked').val(),
prepaid_fields = [ prepaid_fields = [
...@@ -330,8 +356,9 @@ define([ ...@@ -330,8 +356,9 @@ define([
}, this); }, this);
this.hideField('[name=price]', 0); this.hideField('[name=price]', 0);
this.hideField('[name=invoice_discount_value]', null); this.hideField('[name=invoice_discount_value]', null);
this.hideField('[name=tax_deducted_value]', null); this.hideField('[name=tax_deducted_source_value]', null);
this.formGroup('[name=tax_deduction]').addClass(this.hiddenClass); this.$('#non-tax-deducted').prop('checked', true).trigger('change');
this.hideField('[name=tax_deduction]', null);
} }
}, },
...@@ -500,6 +527,15 @@ define([ ...@@ -500,6 +527,15 @@ define([
this.dynamic_catalog_view.seat_types = this.model.get('course_seat_types'); this.dynamic_catalog_view.seat_types = this.model.get('course_seat_types');
}, },
/* Open external links in a new tab.
* Works only for anchor elements that contain 'external-link' class.
*/
routeToLink: function(e) {
e.preventDefault();
e.stopPropagation();
window.open(e.currentTarget.href);
},
render: function () { render: function () {
// Render the parent form/template // Render the parent form/template
this.$el.html(this.template(this.model.attributes)); this.$el.html(this.template(this.model.attributes));
...@@ -512,7 +548,6 @@ define([ ...@@ -512,7 +548,6 @@ define([
this.$alerts = this.$('.alerts'); this.$alerts = this.$('.alerts');
if (this.editing) { if (this.editing) {
this.$('select[name=category]').val(this.model.get('categories')[0].id).trigger('change');
this.disableNonEditableFields(); this.disableNonEditableFields();
this.toggleCouponTypeField(); this.toggleCouponTypeField();
this.toggleVoucherTypeField(); this.toggleVoucherTypeField();
......
...@@ -28,6 +28,10 @@ define([ ...@@ -28,6 +28,10 @@ define([
initialize: function () { initialize: function () {
this.alertViews = []; this.alertViews = [];
if (this.editing && _.has(this, 'editableAttributes')) {
this.modelServerState = this.model.pick(this.editableAttributes);
}
// Enable validation // Enable validation
Utils.bindValidation(this); Utils.bindValidation(this);
}, },
...@@ -149,7 +153,9 @@ define([ ...@@ -149,7 +153,9 @@ define([
self = this, self = this,
courseId = $('input[name=id]').val(), courseId = $('input[name=id]').val(),
btnSavingContent = '<i class="fa fa-spinner fa-spin" aria-hidden="true"></i> ' + btnSavingContent = '<i class="fa fa-spinner fa-spin" aria-hidden="true"></i> ' +
gettext('Saving...'); gettext('Saving...'),
onSaveComplete,
onSaveError;
e.preventDefault(); e.preventDefault();
...@@ -174,33 +180,54 @@ define([ ...@@ -174,33 +180,54 @@ define([
// Disable all buttons by setting the attribute (for <button>) and class (for <a>) // Disable all buttons by setting the attribute (for <button>) and class (for <a>)
$buttons.attr('disabled', 'disabled').addClass('disabled'); $buttons.attr('disabled', 'disabled').addClass('disabled');
this.model.save({ onSaveComplete = function () {
complete: function () { // Restore the button text
// Restore the button text $submitButton.text(btnDefaultText);
$submitButton.text(btnDefaultText);
// Re-enable the buttons // Re-enable the buttons
$buttons.removeAttr('disabled').removeClass('disabled'); $buttons.removeAttr('disabled').removeClass('disabled');
}, };
success: this.saveSuccess.bind(this),
error: function (model, response) { onSaveError = function (model, response) {
var message = gettext('An error occurred while saving the data.'); var message = gettext('An error occurred while saving the data.');
if (response.responseJSON && response.responseJSON.error) {
message = response.responseJSON.error;
// Log the error to the console for debugging purposes
console.error(message);
} else {
// Log the error to the console for debugging purposes
console.error(response.responseText);
}
self.clearAlerts(); if (response.responseJSON && response.responseJSON.error) {
self.renderAlert('danger', message); message = response.responseJSON.error;
self.$el.animate({scrollTop: 0}, 'slow');
// Log the error to the console for debugging purposes
console.error(message);
} else {
// Log the error to the console for debugging purposes
console.error(response.responseText);
} }
});
self.clearAlerts();
self.renderAlert('danger', message);
self.$el.animate({scrollTop: 0}, 'slow');
};
if (this.editing && _.has(this, 'editableAttributes')) {
var editableAttributes = this.model.pick(this.editableAttributes),
changedAttributes = _.omit(editableAttributes, function(value, key) {
return value === this.modelServerState[key];
}, this);
this.model.save(
changedAttributes,
{
complete: onSaveComplete,
error: onSaveError,
patch: true,
success: this.saveSuccess.bind(this)
}
);
} else {
this.model.save({
complete: onSaveComplete,
success: this.saveSuccess.bind(this),
error: onSaveError
});
}
return this; return this;
} }
......
...@@ -85,7 +85,7 @@ ...@@ -85,7 +85,7 @@
<hr> <hr>
<div class="form-group"> <div class="form-group">
<label><%= gettext('Invoice Type') %></label> <label><%= gettext('Invoice Type') %> *</label>
<div class="invoice-type row"> <div class="invoice-type row">
<div class="form-inline col-md-4"> <div class="form-inline col-md-4">
<input id="already-invoiced" type="radio" name="invoice_type" value="Prepaid"> <input id="already-invoiced" type="radio" name="invoice_type" value="Prepaid">
...@@ -103,13 +103,13 @@ ...@@ -103,13 +103,13 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="invoice-number"><%= gettext('Invoice Number') %></label> <label for="invoice-number"><%= gettext('Invoice Number') %> *</label>
<input id="invoice-number" class="form-control" type="text" name="invoice_number"> <input id="invoice-number" class="form-control" type="text" name="invoice_number">
<p class="help-block"></p> <p class="help-block"></p>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="price"><%= gettext('Invoiced Amount') %></label> <label for="price"><%= gettext('Invoiced Amount') %> *</label>
<div class="input-group"> <div class="input-group">
<div class="input-group-addon">$</div> <div class="input-group-addon">$</div>
<input id="price" type="number" step="0.01" min="0" class="form-control" name="price"> <input id="price" type="number" step="0.01" min="0" class="form-control" name="price">
...@@ -118,7 +118,7 @@ ...@@ -118,7 +118,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="invoice-payment-date"><%= gettext('Payment Date') %></label> <label for="invoice-payment-date"><%= gettext('Payment Date') %> *</label>
<div class="input-group"> <div class="input-group">
<div class="input-group-addon"><span class="fa fa-calendar" aria-hidden="true"></span></div> <div class="input-group-addon"><span class="fa fa-calendar" aria-hidden="true"></span></div>
<input id="invoice-payment-date" placeholder="<%- gettext('YYYY-MM-DDTHH:mm:ss') %>" class="form-control add-pikaday" name="invoice_payment_date"> <input id="invoice-payment-date" placeholder="<%- gettext('YYYY-MM-DDTHH:mm:ss') %>" class="form-control add-pikaday" name="invoice_payment_date">
...@@ -127,7 +127,7 @@ ...@@ -127,7 +127,7 @@
</div> </div>
<div class="form-group hidden"> <div class="form-group hidden">
<label for="invoice-discount-value"><%= gettext('Discount per Code') %></label> <label for="invoice-discount-value"><%= gettext('Discount per Code') %> *</label>
<div class="input-group"> <div class="input-group">
<div class="invoice-discount-addon input-group-addon"></div> <div class="invoice-discount-addon input-group-addon"></div>
<input id="invoice-discount-value" type="number" step="0.01" class="form-control invoice-discount-value" name="invoice_discount_value"> <input id="invoice-discount-value" type="number" step="0.01" class="form-control invoice-discount-value" name="invoice_discount_value">
...@@ -155,8 +155,8 @@ ...@@ -155,8 +155,8 @@
<div class="input-group tax-deducted-source-value"> <div class="input-group tax-deducted-source-value">
<input id="tax-deducted-source-value" type="number" step="1" min="1" max="100" class="form-control" name="tax_deducted_source_value"> <input id="tax-deducted-source-value" type="number" step="1" min="1" max="100" class="form-control" name="tax_deducted_source_value">
<div class="input-group-addon">%</div> <div class="input-group-addon">%</div>
<p class="help-block"></p>
</div> </div>
<p class="help-block"></p>
</div> </div>
</div> </div>
...@@ -181,7 +181,7 @@ ...@@ -181,7 +181,7 @@
<p class="help-block"></p> <p class="help-block"></p>
</div> </div>
<div class="form-group catalog-query"> <div class="form-group catalog-query">
<label for="catalog-query"><%= gettext('Query string:') %> * <a href="https://stage-edx-discovery.edx.org/" class="normal-font-weight">(query guidelines)</a></label> <label for="catalog-query"><%= gettext('Query string:') %> * <a href="https://stage-edx-discovery.edx.org/" class="external-link normal-font-weight">(query guidelines)</a></label>
<textarea id="catalog-query" class="form-control" name="catalog_query" rows="10"></textarea> <textarea id="catalog-query" class="form-control" name="catalog_query" rows="10"></textarea>
<p class="help-block"></p> <p class="help-block"></p>
</div> </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