Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
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
edx-platform
Commits
fcc97a3f
Commit
fcc97a3f
authored
Jun 16, 2015
by
chrisndodge
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #8478 from edx/afzaledx/MAYN-78
MAYN-78 Extend Coupon/Discount Reports
parents
5d2ef56b
ac72e262
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
209 additions
and
43 deletions
+209
-43
lms/djangoapps/instructor/paidcourse_enrollment_report.py
+2
-6
lms/djangoapps/instructor/tests/test_api.py
+16
-8
lms/djangoapps/instructor/views/api.py
+19
-6
lms/djangoapps/instructor_analytics/basic.py
+33
-5
lms/djangoapps/instructor_analytics/tests/test_basic.py
+76
-3
lms/djangoapps/shoppingcart/models.py
+23
-11
lms/djangoapps/shoppingcart/tests/test_models.py
+34
-0
lms/djangoapps/shoppingcart/tests/test_views.py
+3
-1
lms/templates/shoppingcart/receipt.html
+2
-2
lms/templates/shoppingcart/shopping_cart.html
+1
-1
No files found.
lms/djangoapps/instructor/paidcourse_enrollment_report.py
View file @
fcc97a3f
...
@@ -94,11 +94,7 @@ class PaidCourseEnrollmentReportProvider(BaseAbstractEnrollmentReportProvider):
...
@@ -94,11 +94,7 @@ class PaidCourseEnrollmentReportProvider(BaseAbstractEnrollmentReportProvider):
coupon_codes
=
", "
.
join
(
coupon_codes
)
coupon_codes
=
", "
.
join
(
coupon_codes
)
registration_code_used
=
'N/A'
registration_code_used
=
'N/A'
if
coupon_redemption
.
exists
():
list_price
=
paid_course_reg_item
.
get_list_price
()
list_price
=
paid_course_reg_item
.
list_price
else
:
list_price
=
paid_course_reg_item
.
unit_cost
payment_amount
=
paid_course_reg_item
.
unit_cost
payment_amount
=
paid_course_reg_item
.
unit_cost
coupon_codes_used
=
coupon_codes
coupon_codes_used
=
coupon_codes
payment_status
=
paid_course_reg_item
.
status
payment_status
=
paid_course_reg_item
.
status
...
@@ -156,7 +152,7 @@ class PaidCourseEnrollmentReportProvider(BaseAbstractEnrollmentReportProvider):
...
@@ -156,7 +152,7 @@ class PaidCourseEnrollmentReportProvider(BaseAbstractEnrollmentReportProvider):
coupon_codes
=
[
redemption
.
coupon
.
code
for
redemption
in
coupon_redemption
]
coupon_codes
=
[
redemption
.
coupon
.
code
for
redemption
in
coupon_redemption
]
coupon_codes
=
", "
.
join
(
coupon_codes
)
coupon_codes
=
", "
.
join
(
coupon_codes
)
list_price
=
order_item
.
list_price
list_price
=
order_item
.
get_list_price
()
payment_amount
=
order_item
.
unit_cost
payment_amount
=
order_item
.
unit_cost
coupon_codes_used
=
coupon_codes
coupon_codes_used
=
coupon_codes
payment_status
=
order_item
.
status
payment_status
=
order_item
.
status
...
...
lms/djangoapps/instructor/tests/test_api.py
View file @
fcc97a3f
...
@@ -75,7 +75,8 @@ EXPECTED_CSV_HEADER = (
...
@@ -75,7 +75,8 @@ EXPECTED_CSV_HEADER = (
'"code","redeem_code_url","course_id","company_name","created_by","redeemed_by","invoice_id","purchaser",'
'"code","redeem_code_url","course_id","company_name","created_by","redeemed_by","invoice_id","purchaser",'
'"customer_reference_number","internal_reference"'
'"customer_reference_number","internal_reference"'
)
)
EXPECTED_COUPON_CSV_HEADER
=
'"code","course_id","percentage_discount","code_redeemed_count","description"'
EXPECTED_COUPON_CSV_HEADER
=
'"Coupon Code","Course Id","
%
Discount","Description","Expiration Date",'
\
'"Is Active","Code Redeemed Count","Total Discounted Seats","Total Discounted Amount"'
# ddt data for test cases involving reports
# ddt data for test cases involving reports
REPORTS_DATA
=
(
REPORTS_DATA
=
(
...
@@ -4246,13 +4247,20 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
...
@@ -4246,13 +4247,20 @@ class TestCourseRegistrationCodes(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
status_code
,
200
,
response
.
content
)
self
.
assertEqual
(
response
.
status_code
,
200
,
response
.
content
)
# filter all the coupons
# filter all the coupons
for
coupon
in
Coupon
.
objects
.
all
():
for
coupon
in
Coupon
.
objects
.
all
():
self
.
assertIn
(
'"{code}","{course_id}","{discount}","0","{description}","{expiration_date}","True"'
.
format
(
self
.
assertIn
(
code
=
coupon
.
code
,
'"{coupon_code}","{course_id}","{discount}","{description}","{expiration_date}","{is_active}",'
course_id
=
coupon
.
course_id
,
'"{code_redeemed_count}","{total_discounted_seats}","{total_discounted_amount}"'
.
format
(
discount
=
coupon
.
percentage_discount
,
coupon_code
=
coupon
.
code
,
description
=
coupon
.
description
,
course_id
=
coupon
.
course_id
,
expiration_date
=
coupon
.
display_expiry_date
discount
=
coupon
.
percentage_discount
,
),
response
.
content
)
description
=
coupon
.
description
,
expiration_date
=
coupon
.
display_expiry_date
,
is_active
=
coupon
.
is_active
,
code_redeemed_count
=
"0"
,
total_discounted_seats
=
"0"
,
total_discounted_amount
=
"0"
,
),
response
.
content
)
self
.
assertEqual
(
response
[
'Content-Type'
],
'text/csv'
)
self
.
assertEqual
(
response
[
'Content-Type'
],
'text/csv'
)
body
=
response
.
content
.
replace
(
'
\r
'
,
''
)
body
=
response
.
content
.
replace
(
'
\r
'
,
''
)
...
...
lms/djangoapps/instructor/views/api.py
View file @
fcc97a3f
...
@@ -951,7 +951,6 @@ def get_sale_order_records(request, course_id): # pylint: disable=unused-argume
...
@@ -951,7 +951,6 @@ def get_sale_order_records(request, course_id): # pylint: disable=unused-argume
(
'company_name'
,
'Company Name'
),
(
'company_name'
,
'Company Name'
),
(
'company_contact_name'
,
'Company Contact Name'
),
(
'company_contact_name'
,
'Company Contact Name'
),
(
'company_contact_email'
,
'Company Contact Email'
),
(
'company_contact_email'
,
'Company Contact Email'
),
(
'total_amount'
,
'Total Amount'
),
(
'logged_in_username'
,
'Login Username'
),
(
'logged_in_username'
,
'Login Username'
),
(
'logged_in_email'
,
'Login User Email'
),
(
'logged_in_email'
,
'Login User Email'
),
(
'purchase_time'
,
'Date of Sale'
),
(
'purchase_time'
,
'Date of Sale'
),
...
@@ -967,8 +966,11 @@ def get_sale_order_records(request, course_id): # pylint: disable=unused-argume
...
@@ -967,8 +966,11 @@ def get_sale_order_records(request, course_id): # pylint: disable=unused-argume
(
'order_type'
,
'Order Type'
),
(
'order_type'
,
'Order Type'
),
(
'status'
,
'Order Item Status'
),
(
'status'
,
'Order Item Status'
),
(
'coupon_code'
,
'Coupon Code'
),
(
'coupon_code'
,
'Coupon Code'
),
(
'unit_cost'
,
'Unit Price'
),
(
'list_price'
,
'List Price'
),
(
'list_price'
,
'List Price'
),
(
'unit_cost'
,
'Unit Price'
),
(
'quantity'
,
'Quantity'
),
(
'total_discount'
,
'Total Discount'
),
(
'total_amount'
,
'Total Amount Paid'
),
]
]
db_columns
=
[
x
[
0
]
for
x
in
query_features
]
db_columns
=
[
x
[
0
]
for
x
in
query_features
]
...
@@ -1198,11 +1200,22 @@ def get_coupon_codes(request, course_id): # pylint: disable=unused-argument
...
@@ -1198,11 +1200,22 @@ def get_coupon_codes(request, course_id): # pylint: disable=unused-argument
coupons
=
Coupon
.
objects
.
filter
(
course_id
=
course_id
)
coupons
=
Coupon
.
objects
.
filter
(
course_id
=
course_id
)
query_features
=
[
query_features
=
[
'code'
,
'course_id'
,
'percentage_discount'
,
'code_redeemed_count'
,
'description'
,
'expiration_date'
,
'is_active'
(
'code'
,
_
(
'Coupon Code'
)),
(
'course_id'
,
_
(
'Course Id'
)),
(
'percentage_discount'
,
_
(
'
%
Discount'
)),
(
'description'
,
_
(
'Description'
)),
(
'expiration_date'
,
_
(
'Expiration Date'
)),
(
'is_active'
,
_
(
'Is Active'
)),
(
'code_redeemed_count'
,
_
(
'Code Redeemed Count'
)),
(
'total_discounted_seats'
,
_
(
'Total Discounted Seats'
)),
(
'total_discounted_amount'
,
_
(
'Total Discounted Amount'
)),
]
]
coupons_list
=
instructor_analytics
.
basic
.
coupon_codes_features
(
query_features
,
coupons
)
db_columns
=
[
x
[
0
]
for
x
in
query_features
]
header
,
data_rows
=
instructor_analytics
.
csvs
.
format_dictlist
(
coupons_list
,
query_features
)
csv_columns
=
[
x
[
1
]
for
x
in
query_features
]
return
instructor_analytics
.
csvs
.
create_csv_response
(
'Coupons.csv'
,
header
,
data_rows
)
coupons_list
=
instructor_analytics
.
basic
.
coupon_codes_features
(
db_columns
,
coupons
,
course_id
)
__
,
data_rows
=
instructor_analytics
.
csvs
.
format_dictlist
(
coupons_list
,
db_columns
)
return
instructor_analytics
.
csvs
.
create_csv_response
(
'Coupons.csv'
,
csv_columns
,
data_rows
)
@ensure_csrf_cookie
@ensure_csrf_cookie
...
...
lms/djangoapps/instructor_analytics/basic.py
View file @
fcc97a3f
...
@@ -72,6 +72,7 @@ def sale_order_record_features(course_id, features):
...
@@ -72,6 +72,7 @@ def sale_order_record_features(course_id, features):
quantity
=
int
(
getattr
(
purchased_course
,
'qty'
))
quantity
=
int
(
getattr
(
purchased_course
,
'qty'
))
unit_cost
=
float
(
getattr
(
purchased_course
,
'unit_cost'
))
unit_cost
=
float
(
getattr
(
purchased_course
,
'unit_cost'
))
sale_order_dict
.
update
({
"quantity"
:
quantity
})
sale_order_dict
.
update
({
"total_amount"
:
quantity
*
unit_cost
})
sale_order_dict
.
update
({
"total_amount"
:
quantity
*
unit_cost
})
sale_order_dict
.
update
({
"logged_in_username"
:
purchased_course
.
order
.
user
.
username
})
sale_order_dict
.
update
({
"logged_in_username"
:
purchased_course
.
order
.
user
.
username
})
...
@@ -80,6 +81,13 @@ def sale_order_record_features(course_id, features):
...
@@ -80,6 +81,13 @@ def sale_order_record_features(course_id, features):
# Extracting OrderItem information of unit_cost, list_price and status
# Extracting OrderItem information of unit_cost, list_price and status
order_item_dict
=
dict
((
feature
,
getattr
(
purchased_course
,
feature
,
None
))
order_item_dict
=
dict
((
feature
,
getattr
(
purchased_course
,
feature
,
None
))
for
feature
in
order_item_features
)
for
feature
in
order_item_features
)
order_item_dict
[
'list_price'
]
=
purchased_course
.
get_list_price
()
sale_order_dict
.
update
(
{
"total_discount"
:
(
order_item_dict
[
'list_price'
]
-
order_item_dict
[
'unit_cost'
])
*
quantity
}
)
order_item_dict
.
update
({
"coupon_code"
:
'N/A'
})
order_item_dict
.
update
({
"coupon_code"
:
'N/A'
})
coupon_redemption
=
CouponRedemption
.
objects
.
select_related
(
'coupon'
)
.
filter
(
order_id
=
purchased_course
.
order_id
)
coupon_redemption
=
CouponRedemption
.
objects
.
select_related
(
'coupon'
)
.
filter
(
order_id
=
purchased_course
.
order_id
)
...
@@ -235,7 +243,7 @@ def list_may_enroll(course_key, features):
...
@@ -235,7 +243,7 @@ def list_may_enroll(course_key, features):
return
[
extract_student
(
student
,
features
)
for
student
in
may_enroll_and_unenrolled
]
return
[
extract_student
(
student
,
features
)
for
student
in
may_enroll_and_unenrolled
]
def
coupon_codes_features
(
features
,
coupons_list
):
def
coupon_codes_features
(
features
,
coupons_list
,
course_id
):
"""
"""
Return list of Coupon Codes as dictionaries.
Return list of Coupon Codes as dictionaries.
...
@@ -254,13 +262,33 @@ def coupon_codes_features(features, coupons_list):
...
@@ -254,13 +262,33 @@ def coupon_codes_features(features, coupons_list):
coupon_features
=
[
x
for
x
in
COUPON_FEATURES
if
x
in
features
]
coupon_features
=
[
x
for
x
in
COUPON_FEATURES
if
x
in
features
]
coupon_dict
=
dict
((
feature
,
getattr
(
coupon
,
feature
))
for
feature
in
coupon_features
)
coupon_dict
=
dict
((
feature
,
getattr
(
coupon
,
feature
))
for
feature
in
coupon_features
)
coupon_
dict
[
'code_redeemed_count'
]
=
coupon
.
couponredemption_set
.
filter
(
coupon_
redemptions
=
coupon
.
couponredemption_set
.
filter
(
order__status
=
"purchased"
order__status
=
"purchased"
)
.
count
()
)
# we have to capture the redeemed_by value in the case of the downloading and spent registration
coupon_dict
[
'code_redeemed_count'
]
=
coupon_redemptions
.
count
()
seats_purchased_using_coupon
=
0
total_discounted_amount
=
0
for
coupon_redemption
in
coupon_redemptions
:
cart_items
=
coupon_redemption
.
order
.
orderitem_set
.
select_subclasses
()
found_items
=
[]
for
item
in
cart_items
:
if
getattr
(
item
,
'course_id'
,
None
):
if
item
.
course_id
==
course_id
:
found_items
.
append
(
item
)
for
order_item
in
found_items
:
seats_purchased_using_coupon
+=
order_item
.
qty
discounted_amount_for_item
=
float
(
order_item
.
list_price
*
order_item
.
qty
)
*
(
float
(
coupon
.
percentage_discount
)
/
100
)
total_discounted_amount
+=
discounted_amount_for_item
coupon_dict
[
'total_discounted_seats'
]
=
seats_purchased_using_coupon
coupon_dict
[
'total_discounted_amount'
]
=
total_discounted_amount
# We have to capture the redeemed_by value in the case of the downloading and spent registration
# codes csv. In the case of active and generated registration codes the redeemed_by value will be None.
# codes csv. In the case of active and generated registration codes the redeemed_by value will be None.
#
They have not been redeemed yet
# They have not been redeemed yet
coupon_dict
[
'expiration_date'
]
=
coupon
.
display_expiry_date
coupon_dict
[
'expiration_date'
]
=
coupon
.
display_expiry_date
coupon_dict
[
'course_id'
]
=
coupon_dict
[
'course_id'
]
.
to_deprecated_string
()
coupon_dict
[
'course_id'
]
=
coupon_dict
[
'course_id'
]
.
to_deprecated_string
()
...
...
lms/djangoapps/instructor_analytics/tests/test_basic.py
View file @
fcc97a3f
...
@@ -189,7 +189,7 @@ class TestCourseSaleRecordsAnalyticsBasic(ModuleStoreTestCase):
...
@@ -189,7 +189,7 @@ class TestCourseSaleRecordsAnalyticsBasic(ModuleStoreTestCase):
self
.
assertEqual
(
sale_record
[
'total_used_codes'
],
0
)
self
.
assertEqual
(
sale_record
[
'total_used_codes'
],
0
)
self
.
assertEqual
(
sale_record
[
'total_codes'
],
5
)
self
.
assertEqual
(
sale_record
[
'total_codes'
],
5
)
def
test_sale_order_features
(
self
):
def
test_sale_order_features
_with_discount
(
self
):
"""
"""
Test Order Sales Report CSV
Test Order Sales Report CSV
"""
"""
...
@@ -267,6 +267,78 @@ class TestCourseSaleRecordsAnalyticsBasic(ModuleStoreTestCase):
...
@@ -267,6 +267,78 @@ class TestCourseSaleRecordsAnalyticsBasic(ModuleStoreTestCase):
self
.
assertEqual
(
sale_order_record
[
'status'
],
item
.
status
)
self
.
assertEqual
(
sale_order_record
[
'status'
],
item
.
status
)
self
.
assertEqual
(
sale_order_record
[
'coupon_code'
],
coupon_redemption
[
0
]
.
coupon
.
code
)
self
.
assertEqual
(
sale_order_record
[
'coupon_code'
],
coupon_redemption
[
0
]
.
coupon
.
code
)
def
test_sale_order_features_without_discount
(
self
):
"""
Test Order Sales Report CSV
"""
query_features
=
[
(
'id'
,
'Order Id'
),
(
'company_name'
,
'Company Name'
),
(
'company_contact_name'
,
'Company Contact Name'
),
(
'company_contact_email'
,
'Company Contact Email'
),
(
'total_amount'
,
'Total Amount'
),
(
'total_codes'
,
'Total Codes'
),
(
'total_used_codes'
,
'Total Used Codes'
),
(
'logged_in_username'
,
'Login Username'
),
(
'logged_in_email'
,
'Login User Email'
),
(
'purchase_time'
,
'Date of Sale'
),
(
'customer_reference_number'
,
'Customer Reference Number'
),
(
'recipient_name'
,
'Recipient Name'
),
(
'recipient_email'
,
'Recipient Email'
),
(
'bill_to_street1'
,
'Street 1'
),
(
'bill_to_street2'
,
'Street 2'
),
(
'bill_to_city'
,
'City'
),
(
'bill_to_state'
,
'State'
),
(
'bill_to_postalcode'
,
'Postal Code'
),
(
'bill_to_country'
,
'Country'
),
(
'order_type'
,
'Order Type'
),
(
'status'
,
'Order Item Status'
),
(
'coupon_code'
,
'Coupon Code'
),
(
'unit_cost'
,
'Unit Price'
),
(
'list_price'
,
'List Price'
),
(
'codes'
,
'Registration Codes'
),
(
'course_id'
,
'Course Id'
),
(
'quantity'
,
'Quantity'
),
(
'total_discount'
,
'Total Discount'
),
(
'total_amount'
,
'Total Amount Paid'
),
]
# add the coupon code for the course
order
=
Order
.
get_cart_for_user
(
self
.
instructor
)
order
.
order_type
=
'business'
order
.
save
()
order
.
add_billing_details
(
company_name
=
'Test Company'
,
company_contact_name
=
'Test'
,
company_contact_email
=
'test@123'
,
recipient_name
=
'R1'
,
recipient_email
=
''
,
customer_reference_number
=
'PO#23'
)
CourseRegCodeItem
.
add_to_order
(
order
,
self
.
course
.
id
,
4
)
order
.
purchase
()
# get the updated item
item
=
order
.
orderitem_set
.
all
()
.
select_subclasses
()[
0
]
db_columns
=
[
x
[
0
]
for
x
in
query_features
]
sale_order_records_list
=
sale_order_record_features
(
self
.
course
.
id
,
db_columns
)
for
sale_order_record
in
sale_order_records_list
:
self
.
assertEqual
(
sale_order_record
[
'recipient_email'
],
order
.
recipient_email
)
self
.
assertEqual
(
sale_order_record
[
'recipient_name'
],
order
.
recipient_name
)
self
.
assertEqual
(
sale_order_record
[
'company_name'
],
order
.
company_name
)
self
.
assertEqual
(
sale_order_record
[
'company_contact_name'
],
order
.
company_contact_name
)
self
.
assertEqual
(
sale_order_record
[
'company_contact_email'
],
order
.
company_contact_email
)
self
.
assertEqual
(
sale_order_record
[
'customer_reference_number'
],
order
.
customer_reference_number
)
self
.
assertEqual
(
sale_order_record
[
'unit_cost'
],
item
.
unit_cost
)
# Make sure list price is not None and matches the unit price since no discount was applied.
self
.
assertIsNotNone
(
sale_order_record
[
'list_price'
])
self
.
assertEqual
(
sale_order_record
[
'list_price'
],
item
.
unit_cost
)
self
.
assertEqual
(
sale_order_record
[
'status'
],
item
.
status
)
self
.
assertEqual
(
sale_order_record
[
'coupon_code'
],
'N/A'
)
self
.
assertEqual
(
sale_order_record
[
'total_amount'
],
item
.
unit_cost
*
item
.
qty
)
self
.
assertEqual
(
sale_order_record
[
'total_discount'
],
0
)
self
.
assertEqual
(
sale_order_record
[
'quantity'
],
item
.
qty
)
class
TestCourseRegistrationCodeAnalyticsBasic
(
ModuleStoreTestCase
):
class
TestCourseRegistrationCodeAnalyticsBasic
(
ModuleStoreTestCase
):
""" Test basic course registration codes analytics functions. """
""" Test basic course registration codes analytics functions. """
...
@@ -340,7 +412,8 @@ class TestCourseRegistrationCodeAnalyticsBasic(ModuleStoreTestCase):
...
@@ -340,7 +412,8 @@ class TestCourseRegistrationCodeAnalyticsBasic(ModuleStoreTestCase):
def
test_coupon_codes_features
(
self
):
def
test_coupon_codes_features
(
self
):
query_features
=
[
query_features
=
[
'course_id'
,
'percentage_discount'
,
'code_redeemed_count'
,
'description'
,
'expiration_date'
'course_id'
,
'percentage_discount'
,
'code_redeemed_count'
,
'description'
,
'expiration_date'
,
'total_discounted_amount'
,
'total_discounted_seats'
]
]
for
i
in
range
(
10
):
for
i
in
range
(
10
):
coupon
=
Coupon
(
coupon
=
Coupon
(
...
@@ -366,7 +439,7 @@ class TestCourseRegistrationCodeAnalyticsBasic(ModuleStoreTestCase):
...
@@ -366,7 +439,7 @@ class TestCourseRegistrationCodeAnalyticsBasic(ModuleStoreTestCase):
Q
(
expiration_date__gt
=
datetime
.
datetime
.
now
(
pytz
.
UTC
))
|
Q
(
expiration_date__gt
=
datetime
.
datetime
.
now
(
pytz
.
UTC
))
|
Q
(
expiration_date__isnull
=
True
)
Q
(
expiration_date__isnull
=
True
)
)
)
active_coupons_list
=
coupon_codes_features
(
query_features
,
active_coupons
)
active_coupons_list
=
coupon_codes_features
(
query_features
,
active_coupons
,
self
.
course
.
id
)
self
.
assertEqual
(
len
(
active_coupons_list
),
len
(
active_coupons
))
self
.
assertEqual
(
len
(
active_coupons_list
),
len
(
active_coupons
))
for
active_coupon
in
active_coupons_list
:
for
active_coupon
in
active_coupons_list
:
self
.
assertEqual
(
set
(
active_coupon
.
keys
()),
set
(
query_features
))
self
.
assertEqual
(
set
(
active_coupon
.
keys
()),
set
(
query_features
))
...
...
lms/djangoapps/shoppingcart/models.py
View file @
fcc97a3f
...
@@ -220,9 +220,8 @@ class Order(models.Model):
...
@@ -220,9 +220,8 @@ class Order(models.Model):
Reset the items price state in the user cart
Reset the items price state in the user cart
"""
"""
for
item
in
self
.
orderitem_set
.
all
():
# pylint: disable=no-member
for
item
in
self
.
orderitem_set
.
all
():
# pylint: disable=no-member
if
item
.
list_price
:
if
item
.
is_discounted
:
item
.
unit_cost
=
item
.
list_price
item
.
unit_cost
=
item
.
list_price
item
.
list_price
=
None
item
.
save
()
item
.
save
()
def
clear
(
self
):
def
clear
(
self
):
...
@@ -300,19 +299,12 @@ class Order(models.Model):
...
@@ -300,19 +299,12 @@ class Order(models.Model):
"""
"""
items_data
=
[]
items_data
=
[]
for
item
in
order_items
:
for
item
in
order_items
:
if
item
.
list_price
is
not
None
:
discount_price
=
item
.
list_price
-
item
.
unit_cost
price
=
item
.
list_price
else
:
discount_price
=
0
price
=
item
.
unit_cost
item_total
=
item
.
qty
*
item
.
unit_cost
item_total
=
item
.
qty
*
item
.
unit_cost
items_data
.
append
({
items_data
.
append
({
'item_description'
:
item
.
pdf_receipt_display_name
,
'item_description'
:
item
.
pdf_receipt_display_name
,
'quantity'
:
item
.
qty
,
'quantity'
:
item
.
qty
,
'list_price'
:
price
,
'list_price'
:
item
.
get_list_price
()
,
'discount'
:
discount_price
,
'discount'
:
item
.
get_list_price
()
-
item
.
unit_cost
,
'item_total'
:
item_total
'item_total'
:
item_total
})
})
pdf_buffer
=
BytesIO
()
pdf_buffer
=
BytesIO
()
...
@@ -719,6 +711,23 @@ class OrderItem(TimeStampedModel):
...
@@ -719,6 +711,23 @@ class OrderItem(TimeStampedModel):
return
OrderItemSubclassPK
(
type
(
self
),
self
.
pk
)
return
OrderItemSubclassPK
(
type
(
self
),
self
.
pk
)
@property
@property
def
is_discounted
(
self
):
"""
Returns True if the item a discount coupon has been applied to the OrderItem and False otherwise.
Earlier, the OrderItems were stored with an empty list_price if a discount had not been applied.
Now we consider the item to be non discounted if list_price is None or list_price == unit_cost. In
these lines, an item is discounted if it's non-None and list_price and unit_cost mismatch.
This should work with both new and old records.
"""
return
self
.
list_price
and
self
.
list_price
!=
self
.
unit_cost
def
get_list_price
(
self
):
"""
Returns the unit_cost if no discount has been applied, or the list_price if it is defined.
"""
return
self
.
list_price
if
self
.
list_price
else
self
.
unit_cost
@property
def
single_item_receipt_template
(
self
):
def
single_item_receipt_template
(
self
):
"""
"""
The template that should be used when there's only one item in the order
The template that should be used when there's only one item in the order
...
@@ -1449,6 +1458,7 @@ class PaidCourseRegistration(OrderItem):
...
@@ -1449,6 +1458,7 @@ class PaidCourseRegistration(OrderItem):
item
.
mode
=
course_mode
.
slug
item
.
mode
=
course_mode
.
slug
item
.
qty
=
1
item
.
qty
=
1
item
.
unit_cost
=
cost
item
.
unit_cost
=
cost
item
.
list_price
=
cost
item
.
line_desc
=
_
(
u'Registration for Course: {course_name}'
)
.
format
(
item
.
line_desc
=
_
(
u'Registration for Course: {course_name}'
)
.
format
(
course_name
=
course
.
display_name_with_default
)
course_name
=
course
.
display_name_with_default
)
item
.
currency
=
currency
item
.
currency
=
currency
...
@@ -1602,6 +1612,7 @@ class CourseRegCodeItem(OrderItem):
...
@@ -1602,6 +1612,7 @@ class CourseRegCodeItem(OrderItem):
item
.
status
=
order
.
status
item
.
status
=
order
.
status
item
.
mode
=
course_mode
.
slug
item
.
mode
=
course_mode
.
slug
item
.
unit_cost
=
cost
item
.
unit_cost
=
cost
item
.
list_price
=
cost
item
.
qty
=
qty
item
.
qty
=
qty
item
.
line_desc
=
_
(
u'Enrollment codes for Course: {course_name}'
)
.
format
(
item
.
line_desc
=
_
(
u'Enrollment codes for Course: {course_name}'
)
.
format
(
course_name
=
course
.
display_name_with_default
)
course_name
=
course
.
display_name_with_default
)
...
@@ -1803,6 +1814,7 @@ class CertificateItem(OrderItem):
...
@@ -1803,6 +1814,7 @@ class CertificateItem(OrderItem):
item
.
status
=
order
.
status
item
.
status
=
order
.
status
item
.
qty
=
1
item
.
qty
=
1
item
.
unit_cost
=
cost
item
.
unit_cost
=
cost
item
.
list_price
=
cost
course_name
=
modulestore
()
.
get_course
(
course_id
)
.
display_name
course_name
=
modulestore
()
.
get_course
(
course_id
)
.
display_name
# Translators: In this particular case, mode_name refers to a
# Translators: In this particular case, mode_name refers to a
# particular mode (i.e. Honor Code Certificate, Verified Certificate, etc)
# particular mode (i.e. Honor Code Certificate, Verified Certificate, etc)
...
...
lms/djangoapps/shoppingcart/tests/test_models.py
View file @
fcc97a3f
...
@@ -430,6 +430,40 @@ class OrderItemTest(TestCase):
...
@@ -430,6 +430,40 @@ class OrderItemTest(TestCase):
self
.
assertDictEqual
({
item
.
pk_with_subclass
:
set
([])},
inst_dict
)
self
.
assertDictEqual
({
item
.
pk_with_subclass
:
set
([])},
inst_dict
)
self
.
assertEquals
(
set
([]),
inst_set
)
self
.
assertEquals
(
set
([]),
inst_set
)
def
test_is_discounted
(
self
):
"""
This tests the is_discounted property of the OrderItem
"""
cart
=
Order
.
get_cart_for_user
(
self
.
user
)
item
=
OrderItem
(
user
=
self
.
user
,
order
=
cart
)
item
.
list_price
=
None
item
.
unit_cost
=
100
self
.
assertFalse
(
item
.
is_discounted
)
item
.
list_price
=
100
item
.
unit_cost
=
100
self
.
assertFalse
(
item
.
is_discounted
)
item
.
list_price
=
100
item
.
unit_cost
=
90
self
.
assertTrue
(
item
.
is_discounted
)
def
test_get_list_price
(
self
):
"""
This tests the get_list_price() method of the OrderItem
"""
cart
=
Order
.
get_cart_for_user
(
self
.
user
)
item
=
OrderItem
(
user
=
self
.
user
,
order
=
cart
)
item
.
list_price
=
None
item
.
unit_cost
=
100
self
.
assertEqual
(
item
.
get_list_price
(),
item
.
unit_cost
)
item
.
list_price
=
200
item
.
unit_cost
=
100
self
.
assertEqual
(
item
.
get_list_price
(),
item
.
list_price
)
class
PaidCourseRegistrationTest
(
ModuleStoreTestCase
):
class
PaidCourseRegistrationTest
(
ModuleStoreTestCase
):
def
setUp
(
self
):
def
setUp
(
self
):
...
...
lms/djangoapps/shoppingcart/tests/test_views.py
View file @
fcc97a3f
...
@@ -664,8 +664,10 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
...
@@ -664,8 +664,10 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
for
item
in
items
:
for
item
in
items
:
if
item
.
id
==
reg_item
.
id
:
if
item
.
id
==
reg_item
.
id
:
self
.
assertEquals
(
item
.
unit_cost
,
self
.
get_discount
(
self
.
cost
))
self
.
assertEquals
(
item
.
unit_cost
,
self
.
get_discount
(
self
.
cost
))
self
.
assertEquals
(
item
.
list_price
,
self
.
cost
)
elif
item
.
id
==
cert_item
.
id
:
elif
item
.
id
==
cert_item
.
id
:
self
.
assertEquals
(
item
.
list_price
,
None
)
self
.
assertEquals
(
item
.
list_price
,
self
.
cost
)
self
.
assertEquals
(
item
.
unit_cost
,
self
.
cost
)
# Delete the discounted item, corresponding coupon redemption should
# Delete the discounted item, corresponding coupon redemption should
# be removed for that particular discounted item
# be removed for that particular discounted item
...
...
lms/templates/shoppingcart/receipt.html
View file @
fcc97a3f
...
@@ -320,7 +320,7 @@ from microsite_configuration import microsite
...
@@ -320,7 +320,7 @@ from microsite_configuration import microsite
<div
class=
"three-col"
>
<div
class=
"three-col"
>
% if item.status == "purchased":
% if item.status == "purchased":
<div
class=
"col-1"
>
<div
class=
"col-1"
>
% if item.
list_price != None
:
% if item.
is_discounted
:
<div
class=
"price"
>
${_('Price per student:')}
<span
class=
"line-through"
>
${currency_symbol}${"{0:0.2f}".format(item.list_price)}
</span>
<div
class=
"price"
>
${_('Price per student:')}
<span
class=
"line-through"
>
${currency_symbol}${"{0:0.2f}".format(item.list_price)}
</span>
</div>
</div>
<div
class=
"price green"
>
${_('Discount Applied:')}
<span>
${currency_symbol}${"{0:0.2f}".format(item.unit_cost)}
</span></div>
<div
class=
"price green"
>
${_('Discount Applied:')}
<span>
${currency_symbol}${"{0:0.2f}".format(item.unit_cost)}
</span></div>
...
@@ -338,7 +338,7 @@ from microsite_configuration import microsite
...
@@ -338,7 +338,7 @@ from microsite_configuration import microsite
</div>
</div>
% elif item.status == "refunded":
% elif item.status == "refunded":
<div
class=
"col-1"
>
<div
class=
"col-1"
>
% if item.
list_price != None
:
% if item.
is_discounted
:
<div
class=
"price"
>
${_('Price per student:')}
<span
class=
"line-through"
>
${currency_symbol}${"{0:0.2f}".format(item.list_price)}
</span>
<div
class=
"price"
>
${_('Price per student:')}
<span
class=
"line-through"
>
${currency_symbol}${"{0:0.2f}".format(item.list_price)}
</span>
</div>
</div>
<div
class=
"price green"
>
${_('Discount Applied:')}
<span><del>
${currency_symbol}${"{0:0.2f}".format(item.unit_cost)}
<div
class=
"price green"
>
${_('Discount Applied:')}
<span><del>
${currency_symbol}${"{0:0.2f}".format(item.unit_cost)}
...
...
lms/templates/shoppingcart/shopping_cart.html
View file @
fcc97a3f
...
@@ -78,7 +78,7 @@ from django.utils.translation import ungettext
...
@@ -78,7 +78,7 @@ from django.utils.translation import ungettext
<hr>
<hr>
<div
class=
"three-col"
>
<div
class=
"three-col"
>
<div
class=
"col-1"
>
<div
class=
"col-1"
>
% if item.
list_price != None
:
% if item.
is_discounted
:
<
%
discount_applied =
True
%
>
<
%
discount_applied =
True
%
>
<div
class=
"price"
>
${_('Price per student:')}
<div
class=
"price"
>
${_('Price per student:')}
<span
class=
"line-through"
>
<span
class=
"line-through"
>
...
...
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