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
3f9c52cd
Commit
3f9c52cd
authored
Aug 08, 2013
by
Jason Bau
Committed by
Diana Huang
Aug 22, 2013
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Move shopping cart from session into model/db
parent
ea7cf3a2
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
321 additions
and
97 deletions
+321
-97
common/djangoapps/student/views.py
+1
-1
lms/djangoapps/shoppingcart/inventory_types.py
+0
-68
lms/djangoapps/shoppingcart/migrations/0001_initial.py
+117
-0
lms/djangoapps/shoppingcart/migrations/__init__.py
+0
-0
lms/djangoapps/shoppingcart/models.py
+159
-1
lms/djangoapps/shoppingcart/urls.py
+3
-1
lms/djangoapps/shoppingcart/views.py
+35
-23
lms/envs/common.py
+3
-0
lms/templates/shoppingcart/list.html
+3
-3
No files found.
common/djangoapps/student/views.py
View file @
3f9c52cd
...
...
@@ -386,7 +386,7 @@ def change_enrollment(request):
CourseEnrollment
.
unenroll
(
user
,
course_id
)
org
,
course_num
,
run
=
course_id
.
split
(
"/"
)
log
.
increment
(
"common.student.unenrollment"
,
statsd
.
increment
(
"common.student.unenrollment"
,
tags
=
[
"org:{0}"
.
format
(
org
),
"course:{0}"
.
format
(
course_num
),
"run:{0}"
.
format
(
run
)])
...
...
lms/djangoapps/shoppingcart/inventory_types.py
deleted
100644 → 0
View file @
ea7cf3a2
import
logging
from
django.contrib.auth.models
import
User
from
student.views
import
course_from_id
from
student.models
import
CourseEnrollmentAllowed
,
CourseEnrollment
from
statsd
import
statsd
log
=
logging
.
getLogger
(
"shoppingcart"
)
class
InventoryItem
(
object
):
"""
This is the abstract interface for inventory items.
Inventory items are things that fill up the shopping cart.
Each implementation of InventoryItem should have purchased_callback as
a method and data attributes as defined in __init__ below
"""
def
__init__
(
self
):
# Set up default data attribute values
self
.
qty
=
1
self
.
unit_cost
=
0
# in dollars
self
.
line_cost
=
0
# qty * unit_cost
self
.
line_desc
=
"Misc Item"
def
purchased_callback
(
self
,
user_id
):
"""
This is called on each inventory item in the shopping cart when the
purchase goes through. The parameter provided is the id of the user who
made the purchase.
"""
raise
NotImplementedError
class
PaidCourseRegistration
(
InventoryItem
):
"""
This is an inventory item for paying for a course registration
"""
def
__init__
(
self
,
course_id
,
unit_cost
):
course
=
course_from_id
(
course_id
)
# actually fetch the course to make sure it exists, use this to
# throw errors if it doesn't
self
.
qty
=
1
self
.
unit_cost
=
unit_cost
self
.
line_cost
=
unit_cost
self
.
course_id
=
course_id
self
.
line_desc
=
"Registration for Course {0}"
.
format
(
course_id
)
def
purchased_callback
(
self
,
user_id
):
"""
When purchased, this should enroll the user in the course. We are assuming that
course settings for enrollment date are configured such that only if the (user.email, course_id) pair is found in
CourseEnrollmentAllowed will the user be allowed to enroll. Otherwise requiring payment
would in fact be quite silly since there's a clear back door.
"""
user
=
User
.
objects
.
get
(
id
=
user_id
)
course
=
course_from_id
(
self
.
course_id
)
# actually fetch the course to make sure it exists, use this to
# throw errors if it doesn't
# use get_or_create here to gracefully handle case where the user is already enrolled in the course, for
# whatever reason.
# Don't really need to create CourseEnrollmentAllowed object, but doing it for bookkeeping and consistency
# with rest of codebase.
CourseEnrollmentAllowed
.
objects
.
get_or_create
(
email
=
user
.
email
,
course_id
=
self
.
course_id
,
auto_enroll
=
True
)
CourseEnrollment
.
objects
.
get_or_create
(
user
=
user
,
course_id
=
self
.
course_id
)
log
.
info
(
"Enrolled {0} in paid course {1}, paid ${2}"
.
format
(
user
.
email
,
self
.
course_id
,
self
.
line_cost
))
org
,
course_num
,
run
=
self
.
course_id
.
split
(
"/"
)
statsd
.
increment
(
"shoppingcart.PaidCourseRegistration.purchased_callback.enrollment"
,
tags
=
[
"org:{0}"
.
format
(
org
),
"course:{0}"
.
format
(
course_num
),
"run:{0}"
.
format
(
run
)])
lms/djangoapps/shoppingcart/migrations/0001_initial.py
0 → 100644
View file @
3f9c52cd
# -*- coding: utf-8 -*-
import
datetime
from
south.db
import
db
from
south.v2
import
SchemaMigration
from
django.db
import
models
class
Migration
(
SchemaMigration
):
def
forwards
(
self
,
orm
):
# Adding model 'Order'
db
.
create_table
(
'shoppingcart_order'
,
(
(
'id'
,
self
.
gf
(
'django.db.models.fields.AutoField'
)(
primary_key
=
True
)),
(
'user'
,
self
.
gf
(
'django.db.models.fields.related.ForeignKey'
)(
to
=
orm
[
'auth.User'
])),
(
'status'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
default
=
'cart'
,
max_length
=
32
)),
(
'nonce'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
max_length
=
128
)),
(
'purchase_time'
,
self
.
gf
(
'django.db.models.fields.DateTimeField'
)(
null
=
True
,
blank
=
True
)),
))
db
.
send_create_signal
(
'shoppingcart'
,
[
'Order'
])
# Adding model 'OrderItem'
db
.
create_table
(
'shoppingcart_orderitem'
,
(
(
'id'
,
self
.
gf
(
'django.db.models.fields.AutoField'
)(
primary_key
=
True
)),
(
'order'
,
self
.
gf
(
'django.db.models.fields.related.ForeignKey'
)(
to
=
orm
[
'shoppingcart.Order'
])),
(
'user'
,
self
.
gf
(
'django.db.models.fields.related.ForeignKey'
)(
to
=
orm
[
'auth.User'
])),
(
'status'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
default
=
'cart'
,
max_length
=
32
)),
(
'qty'
,
self
.
gf
(
'django.db.models.fields.IntegerField'
)(
default
=
1
)),
(
'unit_cost'
,
self
.
gf
(
'django.db.models.fields.FloatField'
)(
default
=
0.0
)),
(
'line_cost'
,
self
.
gf
(
'django.db.models.fields.FloatField'
)(
default
=
0.0
)),
(
'line_desc'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
default
=
'Misc. Item'
,
max_length
=
1024
)),
))
db
.
send_create_signal
(
'shoppingcart'
,
[
'OrderItem'
])
# Adding model 'PaidCourseRegistration'
db
.
create_table
(
'shoppingcart_paidcourseregistration'
,
(
(
'orderitem_ptr'
,
self
.
gf
(
'django.db.models.fields.related.OneToOneField'
)(
to
=
orm
[
'shoppingcart.OrderItem'
],
unique
=
True
,
primary_key
=
True
)),
(
'course_id'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
max_length
=
128
,
db_index
=
True
)),
))
db
.
send_create_signal
(
'shoppingcart'
,
[
'PaidCourseRegistration'
])
def
backwards
(
self
,
orm
):
# Deleting model 'Order'
db
.
delete_table
(
'shoppingcart_order'
)
# Deleting model 'OrderItem'
db
.
delete_table
(
'shoppingcart_orderitem'
)
# Deleting model 'PaidCourseRegistration'
db
.
delete_table
(
'shoppingcart_paidcourseregistration'
)
models
=
{
'auth.group'
:
{
'Meta'
:
{
'object_name'
:
'Group'
},
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'80'
}),
'permissions'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Permission']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
})
},
'auth.permission'
:
{
'Meta'
:
{
'ordering'
:
"('content_type__app_label', 'content_type__model', 'codename')"
,
'unique_together'
:
"(('content_type', 'codename'),)"
,
'object_name'
:
'Permission'
},
'codename'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'content_type'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['contenttypes.ContentType']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'50'
})
},
'auth.user'
:
{
'Meta'
:
{
'object_name'
:
'User'
},
'date_joined'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'email'
:
(
'django.db.models.fields.EmailField'
,
[],
{
'max_length'
:
'75'
,
'blank'
:
'True'
}),
'first_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'30'
,
'blank'
:
'True'
}),
'groups'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Group']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'is_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'is_staff'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'is_superuser'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'last_login'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'last_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'30'
,
'blank'
:
'True'
}),
'password'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
}),
'user_permissions'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Permission']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
}),
'username'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'30'
})
},
'contenttypes.contenttype'
:
{
'Meta'
:
{
'ordering'
:
"('name',)"
,
'unique_together'
:
"(('app_label', 'model'),)"
,
'object_name'
:
'ContentType'
,
'db_table'
:
"'django_content_type'"
},
'app_label'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'model'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
})
},
'shoppingcart.order'
:
{
'Meta'
:
{
'object_name'
:
'Order'
},
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'nonce'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
}),
'purchase_time'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
,
'blank'
:
'True'
}),
'status'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'cart'"
,
'max_length'
:
'32'
}),
'user'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
})
},
'shoppingcart.orderitem'
:
{
'Meta'
:
{
'object_name'
:
'OrderItem'
},
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'line_cost'
:
(
'django.db.models.fields.FloatField'
,
[],
{
'default'
:
'0.0'
}),
'line_desc'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'Misc. Item'"
,
'max_length'
:
'1024'
}),
'order'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['shoppingcart.Order']"
}),
'qty'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'1'
}),
'status'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'cart'"
,
'max_length'
:
'32'
}),
'unit_cost'
:
(
'django.db.models.fields.FloatField'
,
[],
{
'default'
:
'0.0'
}),
'user'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
})
},
'shoppingcart.paidcourseregistration'
:
{
'Meta'
:
{
'object_name'
:
'PaidCourseRegistration'
,
'_ormbases'
:
[
'shoppingcart.OrderItem'
]},
'course_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
,
'db_index'
:
'True'
}),
'orderitem_ptr'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
"orm['shoppingcart.OrderItem']"
,
'unique'
:
'True'
,
'primary_key'
:
'True'
})
}
}
complete_apps
=
[
'shoppingcart'
]
\ No newline at end of file
lms/djangoapps/shoppingcart/migrations/__init__.py
0 → 100644
View file @
3f9c52cd
lms/djangoapps/shoppingcart/models.py
View file @
3f9c52cd
import
pytz
import
logging
from
datetime
import
datetime
from
django.db
import
models
from
django.core.exceptions
import
ObjectDoesNotExist
from
django.contrib.auth.models
import
User
from
student.views
import
course_from_id
from
student.models
import
CourseEnrollmentAllowed
,
CourseEnrollment
from
statsd
import
statsd
log
=
logging
.
getLogger
(
"shoppingcart"
)
# Create your models here.
ORDER_STATUSES
=
(
(
'cart'
,
'cart'
),
(
'purchased'
,
'purchased'
),
(
'refunded'
,
'refunded'
),
# Not used for now
)
class
Order
(
models
.
Model
):
"""
This is the model for an order. Before purchase, an Order and its related OrderItems are used
as the shopping cart.
THERE SHOULD ONLY EVER BE ZERO OR ONE ORDER WITH STATUS='cart' PER USER.
"""
user
=
models
.
ForeignKey
(
User
,
db_index
=
True
)
status
=
models
.
CharField
(
max_length
=
32
,
default
=
'cart'
,
choices
=
ORDER_STATUSES
)
# Because we allow an external service to tell us when something is purchased, and our order numbers
# are their pk and therefore predicatble, let's protect against
# forged/replayed replies with a nonce.
nonce
=
models
.
CharField
(
max_length
=
128
)
purchase_time
=
models
.
DateTimeField
(
null
=
True
,
blank
=
True
)
@classmethod
def
get_cart_for_user
(
cls
,
user
):
"""
Use this to enforce the property that at most 1 order per user has status = 'cart'
"""
order
,
created
=
cls
.
objects
.
get_or_create
(
user
=
user
,
status
=
'cart'
)
return
order
@property
def
total_cost
(
self
):
return
sum
([
i
.
line_cost
for
i
in
self
.
orderitem_set
.
all
()])
def
purchase
(
self
):
"""
Call to mark this order as purchased. Iterates through its OrderItems and calls
their purchased_callback
"""
self
.
status
=
'purchased'
self
.
purchase_time
=
datetime
.
now
(
pytz
.
utc
)
self
.
save
()
for
item
in
self
.
orderitem_set
.
all
():
item
.
status
=
'purchased'
item
.
purchased_callback
()
item
.
save
()
class
OrderItem
(
models
.
Model
):
"""
This is the basic interface for order items.
Order items are line items that fill up the shopping carts and orders.
Each implementation of OrderItem should provide its own purchased_callback as
a method.
"""
order
=
models
.
ForeignKey
(
Order
,
db_index
=
True
)
# this is denormalized, but convenient for SQL queries for reports, etc. user should always be = order.user
user
=
models
.
ForeignKey
(
User
,
db_index
=
True
)
# this is denormalized, but convenient for SQL queries for reports, etc. status should always be = order.status
status
=
models
.
CharField
(
max_length
=
32
,
default
=
'cart'
,
choices
=
ORDER_STATUSES
)
qty
=
models
.
IntegerField
(
default
=
1
)
unit_cost
=
models
.
FloatField
(
default
=
0.0
)
line_cost
=
models
.
FloatField
(
default
=
0.0
)
# qty * unit_cost
line_desc
=
models
.
CharField
(
default
=
"Misc. Item"
,
max_length
=
1024
)
def
add_to_order
(
self
,
*
args
,
**
kwargs
):
"""
A suggested convenience function for subclasses.
"""
raise
NotImplementedError
def
purchased_callback
(
self
):
"""
This is called on each inventory item in the shopping cart when the
purchase goes through.
NOTE: We want to provide facilities for doing something like
for item in OrderItem.objects.filter(order_id=order_id):
item.purchased_callback()
Unfortunately the QuerySet used determines the class to be OrderItem, and not its most specific
subclasses. That means this parent class implementation of purchased_callback needs to act as
a dispatcher to call the callback the proper subclasses, and as such it needs to know about all subclasses.
So please add
"""
for
classname
,
lc_classname
in
ORDER_ITEM_SUBTYPES
:
try
:
sub_instance
=
getattr
(
self
,
lc_classname
)
sub_instance
.
purchased_callback
()
except
(
ObjectDoesNotExist
,
AttributeError
):
log
.
exception
(
'Cannot call purchase_callback on non-existent subclass attribute {0} of OrderItem'
\
.
format
(
lc_classname
))
pass
# Each entry is a tuple of ('ModelName', 'lower_case_model_name')
# See https://docs.djangoproject.com/en/1.4/topics/db/models/#multi-table-inheritance for
# PLEASE KEEP THIS LIST UP_TO_DATE WITH THE SUBCLASSES OF OrderItem
ORDER_ITEM_SUBTYPES
=
[
(
'PaidCourseRegistration'
,
'paidcourseregistration'
)
]
class
PaidCourseRegistration
(
OrderItem
):
"""
This is an inventory item for paying for a course registration
"""
course_id
=
models
.
CharField
(
max_length
=
128
,
db_index
=
True
)
@classmethod
def
add_to_order
(
cls
,
order
,
course_id
,
cost
):
"""
A standardized way to create these objects, with sensible defaults filled in.
Will update the cost if called on an order that already carries the course.
Returns the order item
"""
# TODO: Possibly add checking for whether student is already enrolled in course
course
=
course_from_id
(
course_id
)
# actually fetch the course to make sure it exists, use this to
# throw errors if it doesn't
item
,
created
=
cls
.
objects
.
get_or_create
(
order
=
order
,
user
=
order
.
user
,
course_id
=
course_id
)
item
.
status
=
order
.
status
item
.
qty
=
1
item
.
unit_cost
=
cost
item
.
line_cost
=
cost
item
.
line_desc
=
"Registration for Course {0}"
.
format
(
course_id
)
item
.
save
()
return
item
def
purchased_callback
(
self
):
"""
When purchased, this should enroll the user in the course. We are assuming that
course settings for enrollment date are configured such that only if the (user.email, course_id) pair is found in
CourseEnrollmentAllowed will the user be allowed to enroll. Otherwise requiring payment
would in fact be quite silly since there's a clear back door.
"""
course
=
course_from_id
(
self
.
course_id
)
# actually fetch the course to make sure it exists, use this to
# throw errors if it doesn't
# use get_or_create here to gracefully handle case where the user is already enrolled in the course, for
# whatever reason.
# Don't really need to create CourseEnrollmentAllowed object, but doing it for bookkeeping and consistency
# with rest of codebase.
CourseEnrollmentAllowed
.
objects
.
get_or_create
(
email
=
self
.
user
.
email
,
course_id
=
self
.
course_id
,
auto_enroll
=
True
)
CourseEnrollment
.
objects
.
get_or_create
(
user
=
self
.
user
,
course_id
=
self
.
course_id
)
log
.
info
(
"Enrolled {0} in paid course {1}, paid ${2}"
.
format
(
self
.
user
.
email
,
self
.
course_id
,
self
.
line_cost
))
org
,
course_num
,
run
=
self
.
course_id
.
split
(
"/"
)
statsd
.
increment
(
"shoppingcart.PaidCourseRegistration.purchased_callback.enrollment"
,
tags
=
[
"org:{0}"
.
format
(
org
),
"course:{0}"
.
format
(
course_num
),
"run:{0}"
.
format
(
run
)])
lms/djangoapps/shoppingcart/urls.py
View file @
3f9c52cd
...
...
@@ -3,7 +3,8 @@ from django.conf.urls import patterns, include, url
urlpatterns
=
patterns
(
'shoppingcart.views'
,
# nopep8
url
(
r'^$'
,
'show_cart'
),
url
(
r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/$'
,
'test'
),
url
(
r'^add/(?P<course_id>[^/]+/[^/]+/[^/]+)/$'
,
'add_course_to_cart'
),
url
(
r'^add/
course/
(?P<course_id>[^/]+/[^/]+/[^/]+)/$'
,
'add_course_to_cart'
),
url
(
r'^clear/$'
,
'clear_cart'
),
url
(
r'^remove_item/$'
,
'remove_item'
),
url
(
r'^purchased/$'
,
'purchased'
),
)
\ No newline at end of file
lms/djangoapps/shoppingcart/views.py
View file @
3f9c52cd
...
...
@@ -7,10 +7,10 @@ from hashlib import sha1
from
django.conf
import
settings
from
collections
import
OrderedDict
from
django.http
import
HttpResponse
from
django.http
import
HttpResponse
,
HttpResponseRedirect
from
django.contrib.auth.decorators
import
login_required
from
mitxmako.shortcuts
import
render_to_response
from
.
inventory_type
s
import
*
from
.
model
s
import
*
log
=
logging
.
getLogger
(
"shoppingcart"
)
...
...
@@ -21,49 +21,61 @@ def test(request, course_id):
return
HttpResponse
(
'OK'
)
@login_required
def
purchased
(
request
):
#verify() -- signatures, total cost match up, etc. Need error handling code (
# If verify fails probaly need to display a contact email/number)
cart
=
Order
.
get_cart_for_user
(
request
.
user
)
cart
.
purchase
()
return
HttpResponseRedirect
(
'/'
)
@login_required
def
add_course_to_cart
(
request
,
course_id
):
cart
=
request
.
session
.
get
(
'shopping_cart'
,
[])
course_ids_in_cart
=
[
i
.
course_id
for
i
in
cart
if
isinstance
(
i
,
PaidCourseRegistration
)]
if
course_id
not
in
course_ids_in_cart
:
cart
=
Order
.
get_cart_for_user
(
request
.
user
)
# TODO: Catch 500 here for course that does not exist, period
item
=
PaidCourseRegistration
(
course_id
,
200
)
cart
.
append
(
item
)
request
.
session
[
'shopping_cart'
]
=
cart
return
HttpResponse
(
'Added'
)
else
:
return
HttpResponse
(
"Item exists, not adding"
)
PaidCourseRegistration
.
add_to_order
(
cart
,
course_id
,
200
)
return
HttpResponse
(
"Added"
)
@login_required
def
show_cart
(
request
):
cart
=
request
.
session
.
get
(
'shopping_cart'
,
[])
total_cost
=
"{0:0.2f}"
.
format
(
sum
([
i
.
line_cost
for
i
in
cart
]))
cart
=
Order
.
get_cart_for_user
(
request
.
user
)
total_cost
=
cart
.
total_cost
cart_items
=
cart
.
orderitem_set
.
all
()
params
=
OrderedDict
()
params
[
'amount'
]
=
total_cost
params
[
'currency'
]
=
'usd'
params
[
'orderPage_transactionType'
]
=
'sale'
params
[
'orderNumber'
]
=
"{0:d}"
.
format
(
random
.
randint
(
1
,
10000
))
params
[
'orderNumber'
]
=
"{0:d}"
.
format
(
cart
.
id
)
params
[
'billTo_email'
]
=
request
.
user
.
email
idx
=
1
for
item
in
cart_items
:
prefix
=
"item_{0:d}_"
.
format
(
idx
)
params
[
prefix
+
'productSKU'
]
=
"{0:d}"
.
format
(
item
.
id
)
params
[
prefix
+
'quantity'
]
=
item
.
qty
params
[
prefix
+
'productName'
]
=
item
.
line_desc
params
[
prefix
+
'unitPrice'
]
=
item
.
unit_cost
params
[
prefix
+
'taxAmount'
]
=
"0.00"
signed_param_dict
=
cybersource_sign
(
params
)
return
render_to_response
(
"shoppingcart/list.html"
,
{
'shoppingcart_items'
:
cart
,
{
'shoppingcart_items'
:
cart
_items
,
'total_cost'
:
total_cost
,
'params'
:
signed_param_dict
,
})
@login_required
def
clear_cart
(
request
):
request
.
session
[
'shopping_cart'
]
=
[]
cart
=
Order
.
get_cart_for_user
(
request
.
user
)
cart
.
orderitem_set
.
all
()
.
delete
()
return
HttpResponse
(
'Cleared'
)
@login_required
def
remove_item
(
request
):
# doing this with indexes to replicate the function that generated the list on the HTML page
item_idx
=
request
.
REQUEST
.
get
(
'idx'
,
'blank'
)
item_id
=
request
.
REQUEST
.
get
(
'id'
,
'-1'
)
try
:
cart
=
request
.
session
.
get
(
'shopping_cart'
,
[]
)
cart
.
pop
(
int
(
item_idx
))
request
.
session
[
'shopping_cart'
]
=
cart
except
IndexError
,
ValueError
:
log
.
exception
(
'Cannot remove
element at index {0} from cart'
.
format
(
item_idx
))
item
=
OrderItem
.
objects
.
get
(
id
=
item_id
,
status
=
'cart'
)
if
item
.
user
==
request
.
user
:
item
.
delete
()
except
OrderItem
.
DoesNotExist
:
log
.
exception
(
'Cannot remove
cart OrderItem id={0}. DoesNotExist or item is already purchased'
.
format
(
item_id
))
return
HttpResponse
(
'OK'
)
...
...
lms/envs/common.py
View file @
3f9c52cd
...
...
@@ -778,6 +778,9 @@ INSTALLED_APPS = (
'rest_framework'
,
'user_api'
,
# shopping cart
'shoppingcart'
,
# Notification preferences setting
'notification_prefs'
,
...
...
lms/templates/shoppingcart/list.html
View file @
3f9c52cd
...
...
@@ -13,9 +13,9 @@
<tr><td>
Qty
</td><td>
Description
</td><td>
Unit Price
</td><td>
Price
</td></tr>
</thead>
<tbody>
% for i
dx,item in enumerate(shoppingcart_items)
:
% for i
tem in shoppingcart_items
:
<tr><td>
${item.qty}
</td><td>
${item.line_desc}
</td><td>
${item.unit_cost}
</td><td>
${item.line_cost}
</td>
<td><a
data-item-id
x=
"${idx
}"
class=
'remove_line_item'
href=
'#'
>
[x]
</a></td></tr>
<td><a
data-item-id
=
"${item.id
}"
class=
'remove_line_item'
href=
'#'
>
[x]
</a></td></tr>
% endfor
<tr><td></td><td></td><td></td><td>
Total Cost
</td></tr>
<tr><td></td><td></td><td></td><td>
${total_cost}
</td></tr>
...
...
@@ -41,7 +41,7 @@
$
(
'a.remove_line_item'
).
click
(
function
(
event
)
{
event
.
preventDefault
();
var
post_url
=
"${reverse('shoppingcart.views.remove_item')}"
;
$
.
post
(
post_url
,
{
id
x
:
$
(
this
).
data
(
'item-idx
'
)})
$
.
post
(
post_url
,
{
id
:
$
(
this
).
data
(
'item-id
'
)})
.
always
(
function
(
data
){
location
.
reload
(
true
);
});
...
...
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