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
9e3a9029
Commit
9e3a9029
authored
Feb 20, 2017
by
zubair-arbi
Committed by
Zubair Afzal
Feb 23, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
catalog coupons add course seat types selection
ENT-199
parent
f9f6ab8a
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
88 additions
and
29 deletions
+88
-29
ecommerce/extensions/api/v2/tests/views/test_coupons.py
+43
-7
ecommerce/extensions/api/v2/views/coupons.py
+6
-4
ecommerce/static/js/models/coupon_model.js
+3
-1
ecommerce/static/js/test/specs/models/coupon_model_spec.js
+7
-1
ecommerce/static/js/test/specs/views/coupon_detail_view_spec.js
+7
-1
ecommerce/static/js/views/coupon_detail_view.js
+10
-8
ecommerce/static/js/views/coupon_form_view.js
+7
-2
ecommerce/static/templates/coupon_form.html
+5
-5
No files found.
ecommerce/extensions/api/v2/tests/views/test_coupons.py
View file @
9e3a9029
...
@@ -16,6 +16,7 @@ from oscar.apps.catalogue.categories import create_from_breadcrumbs
...
@@ -16,6 +16,7 @@ from oscar.apps.catalogue.categories import create_from_breadcrumbs
from
oscar.core.loading
import
get_class
,
get_model
from
oscar.core.loading
import
get_class
,
get_model
from
oscar.test
import
factories
from
oscar.test
import
factories
from
rest_framework
import
status
from
rest_framework
import
status
from
testfixtures
import
LogCapture
from
ecommerce.core.tests.decorators
import
mock_course_catalog_api_client
from
ecommerce.core.tests.decorators
import
mock_course_catalog_api_client
from
ecommerce.coupons.tests.mixins
import
CourseCatalogMockMixin
,
CouponMixin
from
ecommerce.coupons.tests.mixins
import
CourseCatalogMockMixin
,
CouponMixin
...
@@ -28,6 +29,7 @@ from ecommerce.tests.factories import ProductFactory, SiteConfigurationFactory
...
@@ -28,6 +29,7 @@ from ecommerce.tests.factories import ProductFactory, SiteConfigurationFactory
from
ecommerce.tests.mixins
import
ThrottlingMixin
from
ecommerce.tests.mixins
import
ThrottlingMixin
from
ecommerce.tests.testcases
import
TestCase
from
ecommerce.tests.testcases
import
TestCase
Applicator
=
get_class
(
'offer.utils'
,
'Applicator'
)
Applicator
=
get_class
(
'offer.utils'
,
'Applicator'
)
Basket
=
get_model
(
'basket'
,
'Basket'
)
Basket
=
get_model
(
'basket'
,
'Basket'
)
Benefit
=
get_model
(
'offer'
,
'Benefit'
)
Benefit
=
get_model
(
'offer'
,
'Benefit'
)
...
@@ -636,16 +638,20 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat
...
@@ -636,16 +638,20 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat
self
.
assertEqual
(
voucher_range
.
catalog_query
,
data
[
'catalog_query'
])
self
.
assertEqual
(
voucher_range
.
catalog_query
,
data
[
'catalog_query'
])
self
.
assertEqual
(
voucher_range
.
course_seat_types
,
data
[
'course_seat_types'
][
0
])
self
.
assertEqual
(
voucher_range
.
course_seat_types
,
data
[
'course_seat_types'
][
0
])
def
test_update_course_catalog
(
self
):
def
test_update_course_catalog
_coupon
(
self
):
"""
"""
Test updating course catalog range value deletes catalog,
Test that on updating a coupon as course catalog coupon with course
catalog_query and course_seat_types from that voucher range.
seats, deletes values for fields "catalog" and "catalog_query" from its
related voucher range.
"""
"""
path
=
reverse
(
'api:v2:coupons-detail'
,
kwargs
=
{
'pk'
:
self
.
coupon
.
id
})
path
=
reverse
(
'api:v2:coupons-detail'
,
kwargs
=
{
'pk'
:
self
.
coupon
.
id
})
course_catalog_id
=
{
'id'
:
1
,
'name'
:
'Test catalog'
}
course_catalog
=
{
'id'
:
1
,
'name'
:
'Test catalog'
}
course_seat_types
=
[
'verified'
]
data
=
{
data
=
{
'id'
:
self
.
coupon
.
id
,
'id'
:
self
.
coupon
.
id
,
'course_catalog'
:
course_catalog_id
'course_catalog'
:
course_catalog
,
'course_seat_types'
:
course_seat_types
,
}
}
self
.
client
.
put
(
path
,
json
.
dumps
(
data
),
'application/json'
)
self
.
client
.
put
(
path
,
json
.
dumps
(
data
),
'application/json'
)
...
@@ -653,10 +659,40 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat
...
@@ -653,10 +659,40 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat
vouchers
=
updated_coupon
.
attr
.
coupon_vouchers
.
vouchers
vouchers
=
updated_coupon
.
attr
.
coupon_vouchers
.
vouchers
voucher_range
=
vouchers
.
first
()
.
offers
.
first
()
.
benefit
.
range
voucher_range
=
vouchers
.
first
()
.
offers
.
first
()
.
benefit
.
range
expected_course_seat_types
=
','
.
join
(
course_seat_types
)
self
.
assertEqual
(
voucher_range
.
catalog
,
None
)
self
.
assertEqual
(
voucher_range
.
catalog
,
None
)
self
.
assertEqual
(
voucher_range
.
catalog_query
,
None
)
self
.
assertEqual
(
voucher_range
.
catalog_query
,
None
)
self
.
assertEqual
(
voucher_range
.
course_seat_types
,
None
)
self
.
assertEqual
(
voucher_range
.
course_seat_types
,
expected_course_seat_types
)
self
.
assertEqual
(
voucher_range
.
course_catalog
,
course_catalog_id
[
'id'
])
self
.
assertEqual
(
voucher_range
.
course_catalog
,
course_catalog
[
'id'
])
def
test_update_course_catalog_coupon_without_seat_types
(
self
):
"""
Test that on updating a coupon as a course catalog coupon without any
course seat types, a validation error message is logged along with a
400 http response.
"""
path
=
reverse
(
'api:v2:coupons-detail'
,
kwargs
=
{
'pk'
:
self
.
coupon
.
id
})
data
=
{
'id'
:
self
.
coupon
.
id
,
'course_catalog'
:
{
'id'
:
1
,
'name'
:
'Test catalog'
},
'course_seat_types'
:
[],
}
logger_name
=
'ecommerce.core.utils'
expected_logger_message
=
'Failed to create Range. Either catalog_query or course_catalog must be given '
\
'but not both and course_seat_types fields must be set.'
with
LogCapture
(
logger_name
)
as
logger
:
response
=
self
.
client
.
put
(
path
,
json
.
dumps
(
data
),
'application/json'
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_400_BAD_REQUEST
)
logger
.
check
(
(
logger_name
,
'ERROR'
,
expected_logger_message
)
)
def
test_update_coupon_benefit_value
(
self
):
def
test_update_coupon_benefit_value
(
self
):
vouchers
=
self
.
coupon
.
attr
.
coupon_vouchers
.
vouchers
.
all
()
vouchers
=
self
.
coupon
.
attr
.
coupon_vouchers
.
vouchers
.
all
()
...
...
ecommerce/extensions/api/v2/views/coupons.py
View file @
9e3a9029
...
@@ -286,10 +286,9 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet):
...
@@ -286,10 +286,9 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet):
course_catalog
=
course_catalog_data
.
get
(
'id'
)
course_catalog
=
course_catalog_data
.
get
(
'id'
)
range_data
[
'course_catalog'
]
=
course_catalog
range_data
[
'course_catalog'
]
=
course_catalog
# Remove catalog_query
and course_seat_types, switching from
# Remove catalog_query
, switching from the dynamic query coupon to
#
dynamic query to course catalog
#
course catalog coupon
range_data
[
'catalog_query'
]
=
None
range_data
[
'catalog_query'
]
=
None
range_data
[
'course_seat_types'
]
=
None
else
:
else
:
range_data
[
'course_catalog'
]
=
None
range_data
[
'course_catalog'
]
=
None
...
@@ -299,7 +298,10 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet):
...
@@ -299,7 +298,10 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet):
else
:
else
:
range_data
[
'enterprise_customer'
]
=
None
range_data
[
'enterprise_customer'
]
=
None
Range
.
objects
.
filter
(
id
=
voucher_range
.
id
)
.
update
(
**
range_data
)
for
attr
,
value
in
range_data
.
iteritems
():
setattr
(
voucher_range
,
attr
,
value
)
voucher_range
.
save
()
def
update
(
self
,
request
,
*
args
,
**
kwargs
):
def
update
(
self
,
request
,
*
args
,
**
kwargs
):
"""Update coupon depending on request data sent."""
"""Update coupon depending on request data sent."""
...
...
ecommerce/static/js/models/coupon_model.js
View file @
9e3a9029
...
@@ -89,7 +89,9 @@ define([
...
@@ -89,7 +89,9 @@ define([
}
}
},
},
course_seat_types
:
function
(
val
)
{
course_seat_types
:
function
(
val
)
{
if
(
this
.
get
(
'catalog_type'
)
===
CATALOG_TYPES
.
multiple_courses
&&
val
.
length
===
0
)
{
// add validation only for dynamic coupons, e.g. dynamic query coupon or catalog coupon
var
dynamicCoupons
=
[
CATALOG_TYPES
.
multiple_courses
,
CATALOG_TYPES
.
catalog
];
if
(
dynamicCoupons
.
indexOf
(
this
.
get
(
'catalog_type'
))
!==
-
1
&&
val
.
length
===
0
)
{
return
Backbone
.
Validation
.
messages
.
seat_types
;
return
Backbone
.
Validation
.
messages
.
seat_types
;
}
}
},
},
...
...
ecommerce/static/js/test/specs/models/coupon_model_spec.js
View file @
9e3a9029
...
@@ -92,7 +92,7 @@ define([
...
@@ -92,7 +92,7 @@ define([
expect
(
model
.
isValid
()).
toBeTruthy
();
expect
(
model
.
isValid
()).
toBeTruthy
();
});
});
it
(
'should validate course catalog for type Catalog'
,
function
()
{
it
(
'should validate course catalog
and course seat types
for type Catalog'
,
function
()
{
model
.
set
(
'catalog_type'
,
'Catalog'
);
model
.
set
(
'catalog_type'
,
'Catalog'
);
model
.
set
(
'course_catalog'
,
''
);
model
.
set
(
'course_catalog'
,
''
);
model
.
validate
();
model
.
validate
();
...
@@ -103,6 +103,12 @@ define([
...
@@ -103,6 +103,12 @@ define([
expect
(
model
.
isValid
()).
toBe
(
false
);
expect
(
model
.
isValid
()).
toBe
(
false
);
model
.
set
(
'course_catalog'
,
'1'
);
model
.
set
(
'course_catalog'
,
'1'
);
model
.
set
(
'course_seat_types'
,
[]);
model
.
validate
();
expect
(
model
.
isValid
()).
toBe
(
false
);
model
.
set
(
'course_catalog'
,
'1'
);
model
.
set
(
'course_seat_types'
,
[
'verified'
]);
model
.
validate
();
model
.
validate
();
expect
(
model
.
isValid
()).
toBe
(
true
);
expect
(
model
.
isValid
()).
toBe
(
true
);
});
});
...
...
ecommerce/static/js/test/specs/views/coupon_detail_view_spec.js
View file @
9e3a9029
...
@@ -140,14 +140,20 @@ define([
...
@@ -140,14 +140,20 @@ define([
expect
(
view
.
$
(
'.invoice-discount-type > .value'
).
text
()).
toEqual
(
''
);
expect
(
view
.
$
(
'.invoice-discount-type > .value'
).
text
()).
toEqual
(
''
);
});
});
it
(
'should display course catalog name on render.'
,
function
()
{
it
(
'should display course catalog name
and course seats without preview button
on render.'
,
function
()
{
ecommerce
.
coupons
.
catalogs
=
new
CatalogCollection
([{
id
:
1
,
name
:
'Test Catalog'
}]);
ecommerce
.
coupons
.
catalogs
=
new
CatalogCollection
([{
id
:
1
,
name
:
'Test Catalog'
}]);
data
.
course_catalog
=
1
;
data
.
course_catalog
=
1
;
data
.
course_seat_types
=
[
'verified'
];
model
=
Coupon
.
findOrCreate
(
data
,
{
parse
:
true
,
create
:
true
});
model
=
Coupon
.
findOrCreate
(
data
,
{
parse
:
true
,
create
:
true
});
view
=
new
CouponDetailView
({
model
:
model
});
view
=
new
CouponDetailView
({
model
:
model
});
view
.
render
();
view
.
render
();
expect
(
view
.
$
(
'.catalog-name > .value'
).
text
()).
toEqual
(
'Test Catalog'
);
expect
(
view
.
$
(
'.catalog-name > .value'
).
text
()).
toEqual
(
'Test Catalog'
);
expect
(
view
.
$
(
'.seat-types .value'
).
text
()).
toEqual
(
data
.
course_seat_types
.
join
(
', '
)
);
expect
(
view
.
$
(
'.catalog_buttons'
).
text
()).
toEqual
(
''
);
});
});
it
(
'should format seat types.'
,
function
()
{
it
(
'should format seat types.'
,
function
()
{
...
...
ecommerce/static/js/views/coupon_detail_view.js
View file @
9e3a9029
...
@@ -153,14 +153,16 @@ define([
...
@@ -153,14 +153,16 @@ define([
this
.
renderCourseData
();
this
.
renderCourseData
();
this
.
delegateEvents
();
this
.
delegateEvents
();
this
.
dynamic_catalog_view
=
new
DynamicCatalogView
({
if
(
this
.
model
.
get
(
'catalog_type'
)
===
this
.
model
.
catalogTypes
.
multiple_courses
)
{
'query'
:
this
.
model
.
get
(
'catalog_query'
),
this
.
dynamic_catalog_view
=
new
DynamicCatalogView
({
'seat_types'
:
this
.
model
.
get
(
'course_seat_types'
)
'query'
:
this
.
model
.
get
(
'catalog_query'
),
});
'seat_types'
:
this
.
model
.
get
(
'course_seat_types'
)
});
this
.
dynamic_catalog_view
.
$el
=
this
.
$
(
'.catalog_buttons'
);
this
.
dynamic_catalog_view
.
render
();
this
.
dynamic_catalog_view
.
$el
=
this
.
$
(
'.catalog_buttons'
);
this
.
dynamic_catalog_view
.
delegateEvents
();
this
.
dynamic_catalog_view
.
render
();
this
.
dynamic_catalog_view
.
delegateEvents
();
}
this
.
$
(
'.coupon-information'
).
before
(
AlertDivTemplate
);
this
.
$
(
'.coupon-information'
).
before
(
AlertDivTemplate
);
this
.
$alerts
=
this
.
$el
.
find
(
'.alerts'
);
this
.
$alerts
=
this
.
$el
.
find
(
'.alerts'
);
...
...
ecommerce/static/js/views/coupon_form_view.js
View file @
9e3a9029
...
@@ -480,15 +480,19 @@ define([
...
@@ -480,15 +480,19 @@ define([
this
.
model
.
unset
(
'seat_type'
);
this
.
model
.
unset
(
'seat_type'
);
this
.
model
.
unset
(
'stock_record_ids'
);
this
.
model
.
unset
(
'stock_record_ids'
);
this
.
model
.
unset
(
'catalog_query'
);
this
.
model
.
unset
(
'catalog_query'
);
this
.
model
.
unset
(
'course_seat_types'
);
this
.
formGroup
(
'[name=enterprise_customer]'
).
addClass
(
this
.
hiddenClass
);
this
.
formGroup
(
'[name=enterprise_customer]'
).
addClass
(
this
.
hiddenClass
);
this
.
model
.
unset
(
'enterprise_customer'
);
this
.
model
.
unset
(
'enterprise_customer'
);
this
.
formGroup
(
'[name=catalog_query]'
).
addClass
(
this
.
hiddenClass
);
this
.
formGroup
(
'[name=catalog_query]'
).
addClass
(
this
.
hiddenClass
);
this
.
formGroup
(
'[name=course_seat_types]'
).
add
Class
(
this
.
hiddenClass
);
this
.
formGroup
(
'[name=course_seat_types]'
).
remove
Class
(
this
.
hiddenClass
);
this
.
formGroup
(
'[name=course_id]'
).
addClass
(
this
.
hiddenClass
);
this
.
formGroup
(
'[name=course_id]'
).
addClass
(
this
.
hiddenClass
);
this
.
formGroup
(
'[name=seat_type]'
).
addClass
(
this
.
hiddenClass
);
this
.
formGroup
(
'[name=seat_type]'
).
addClass
(
this
.
hiddenClass
);
this
.
formGroup
(
'[name=course_catalog]'
).
removeClass
(
this
.
hiddenClass
);
this
.
formGroup
(
'[name=course_catalog]'
).
removeClass
(
this
.
hiddenClass
);
this
.
formGroup
(
'[name=seat_type] option'
).
remove
();
this
.
formGroup
(
'[name=seat_type] option'
).
remove
();
this
.
$
(
'.catalog_buttons'
).
addClass
(
this
.
hiddenClass
);
if
(
!
this
.
model
.
get
(
'course_seat_types'
))
{
this
.
model
.
set
(
'course_seat_types'
,
[]);
}
}
else
{
}
else
{
this
.
formGroup
(
'[name=catalog_query]'
).
removeClass
(
this
.
hiddenClass
);
this
.
formGroup
(
'[name=catalog_query]'
).
removeClass
(
this
.
hiddenClass
);
this
.
formGroup
(
'[name=course_seat_types]'
).
removeClass
(
this
.
hiddenClass
);
this
.
formGroup
(
'[name=course_seat_types]'
).
removeClass
(
this
.
hiddenClass
);
...
@@ -502,6 +506,7 @@ define([
...
@@ -502,6 +506,7 @@ define([
this
.
model
.
unset
(
'stock_record_ids'
);
this
.
model
.
unset
(
'stock_record_ids'
);
this
.
model
.
set
(
'course_catalog'
,
this
.
model
.
defaults
.
course_catalog
);
this
.
model
.
set
(
'course_catalog'
,
this
.
model
.
defaults
.
course_catalog
);
this
.
$
(
'.catalog_buttons'
).
removeClass
(
this
.
hiddenClass
);
if
(
!
this
.
model
.
get
(
'course_seat_types'
))
{
if
(
!
this
.
model
.
get
(
'course_seat_types'
))
{
this
.
model
.
set
(
'course_seat_types'
,
[]);
this
.
model
.
set
(
'course_seat_types'
,
[]);
}
}
...
...
ecommerce/static/templates/coupon_form.html
View file @
9e3a9029
...
@@ -185,6 +185,11 @@
...
@@ -185,6 +185,11 @@
<span>
Query length:
<span
class=
"query_length"
>
0
</span></span>
<span>
Query length:
<span
class=
"query_length"
>
0
</span></span>
<p
class=
"help-block"
></p>
<p
class=
"help-block"
></p>
</div>
</div>
<div
class=
"form-group course-catalog"
>
<label
for=
"course-catalog"
><
%=
gettext
('
Select
from
course
catalogs:
')
%
>
*
</label>
<select
id=
"course-catalog"
class=
"form-control"
name=
"course_catalog"
></select>
<p
class=
"help-block"
></p>
</div>
<div
class=
"form-group course-seat-types"
>
<div
class=
"form-group course-seat-types"
>
<label
for=
"course-seat-types"
><
%=
gettext
('
Seat
Types:
')
%
>
*
</label>
<label
for=
"course-seat-types"
><
%=
gettext
('
Seat
Types:
')
%
>
*
</label>
<div
class=
"checkboxes"
>
<div
class=
"checkboxes"
>
...
@@ -206,11 +211,6 @@
...
@@ -206,11 +211,6 @@
</div>
</div>
<div
class=
"catalog_buttons"
></div>
<div
class=
"catalog_buttons"
></div>
</div>
</div>
<div
class=
"form-group course-catalog"
>
<label
for=
"course-catalog"
><
%=
gettext
('
Select
from
course
catalogs:
')
%
>
*
</label>
<select
id=
"course-catalog"
class=
"form-control"
name=
"course_catalog"
></select>
<p
class=
"help-block"
></p>
</div>
<div
class=
"form-group enterprise-customer"
>
<div
class=
"form-group enterprise-customer"
>
<label
for=
"enterprise-customer"
><
%=
gettext
('
Enterprise
Customer:
')
%
></label>
<label
for=
"enterprise-customer"
><
%=
gettext
('
Enterprise
Customer:
')
%
></label>
<select
id=
"enterprise-customer"
class=
"form-control"
name=
"enterprise_customer"
></select>
<select
id=
"enterprise-customer"
class=
"form-control"
name=
"enterprise_customer"
></select>
...
...
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