Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
ecommerce
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
ecommerce
Commits
d383e1ed
Commit
d383e1ed
authored
Jul 05, 2016
by
Matt Drayer
Committed by
GitHub
Jul 05, 2016
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into malikshahzad228/MAYN-271
parents
9b82fcf5
af9f2d4a
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
324 additions
and
76 deletions
+324
-76
ecommerce/extensions/api/v2/tests/views/test_coupons.py
+35
-0
ecommerce/extensions/api/v2/views/coupons.py
+32
-13
ecommerce/static/js/models/coupon_model.js
+48
-11
ecommerce/static/js/test/mock_data/coupons.js
+2
-1
ecommerce/static/js/test/specs/models/coupon_model_spec.js
+31
-0
ecommerce/static/js/test/specs/views/coupon_create_view_spec.js
+12
-11
ecommerce/static/js/test/specs/views/coupon_detail_view_spec.js
+0
-0
ecommerce/static/js/test/specs/views/coupon_edit_view_spec.js
+12
-1
ecommerce/static/js/test/specs/views/coupon_form_view_spec.js
+10
-1
ecommerce/static/js/test/specs/views/form_view_spec.js
+0
-1
ecommerce/static/js/views/coupon_detail_view.js
+43
-0
ecommerce/static/js/views/coupon_form_view.js
+40
-5
ecommerce/static/js/views/form_view.js
+52
-25
ecommerce/static/templates/coupon_form.html
+7
-7
No files found.
ecommerce/extensions/api/v2/tests/views/test_coupons.py
View file @
d383e1ed
...
@@ -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."""
...
...
ecommerce/extensions/api/v2/views/coupons.py
View file @
d383e1ed
...
@@ -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
:
...
...
ecommerce/static/js/models/coupon_model.js
View file @
d383e1ed
...
@@ -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
);
}
}
});
});
}
}
...
...
ecommerce/static/js/test/mock_data/coupons.js
View file @
d383e1ed
...
@@ -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
,
...
...
ecommerce/static/js/test/specs/models/coupon_model_spec.js
View file @
d383e1ed
...
@@ -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'
);
});
});
});
});
});
...
...
ecommerce/static/js/test/specs/views/coupon_create_view_spec.js
View file @
d383e1ed
...
@@ -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
();
...
...
ecommerce/static/js/test/specs/views/coupon_detail_view_spec.js
View file @
d383e1ed
This diff is collapsed.
Click to expand it.
ecommerce/static/js/test/specs/views/coupon_edit_view_spec.js
View file @
d383e1ed
...
@@ -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
();
});
});
});
});
});
}
}
...
...
ecommerce/static/js/test/specs/views/coupon_form_view_spec.js
View file @
d383e1ed
...
@@ -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]'
,
...
...
ecommerce/static/js/test/specs/views/form_view_spec.js
View file @
d383e1ed
...
@@ -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
();
});
});
});
});
}
}
);
);
ecommerce/static/js/views/coupon_detail_view.js
View file @
d383e1ed
...
@@ -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
);
...
...
ecommerce/static/js/views/coupon_form_view.js
View file @
d383e1ed
...
@@ -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
();
...
...
ecommerce/static/js/views/form_view.js
View file @
d383e1ed
...
@@ -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
;
}
}
...
...
ecommerce/static/templates/coupon_form.html
View file @
d383e1ed
...
@@ -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>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment