Commit b6474304 by Will Daly

Merge pull request #5122 from edx/will/ecom-218

Change shopping cart status once sent to Cybersource (ECOM-218)
parents 541419e3 a9c73d24
......@@ -40,8 +40,18 @@ from microsite_configuration import microsite
log = logging.getLogger("shoppingcart")
ORDER_STATUSES = (
# The user is selecting what he/she wants to purchase.
('cart', 'cart'),
# The user has been sent to the external payment processor.
# At this point, the order should NOT be modified.
# If the user returns to the payment flow, he/she will start a new order.
('paying', 'paying'),
# The user has successfully purchased the items in the order.
('purchased', 'purchased'),
# The user's order has been refunded.
('refunded', 'refunded'),
)
......@@ -129,6 +139,22 @@ class Order(models.Model):
"""
self.orderitem_set.all().delete()
@transaction.commit_on_success
def start_purchase(self):
"""
Start the purchase process. This will set the order status to "paying",
at which point it should no longer be modified.
Future calls to `Order.get_cart_for_user()` will filter out orders with
status "paying", effectively creating a new (empty) cart.
"""
if self.status == 'cart':
self.status = 'paying'
self.save()
for item in OrderItem.objects.filter(order=self).select_subclasses():
item.start_purchase()
def purchase(self, first='', last='', street1='', street2='', city='', state='', postalcode='',
country='', ccnum='', cardtype='', processor_reply_dump=''):
"""
......@@ -269,6 +295,14 @@ class OrderItem(models.Model):
self.fulfilled_time = datetime.now(pytz.utc)
self.save()
def start_purchase(self):
"""
Start the purchase process. This will set the order item status to "paying",
at which point it should no longer be modified.
"""
self.status = 'paying'
self.save()
def purchased_callback(self):
"""
This is called on each inventory item in the shopping cart when the
......
......@@ -102,6 +102,38 @@ class OrderTest(ModuleStoreTestCase):
self.assertEquals(cart.orderitem_set.count(), len(course_costs))
self.assertEquals(cart.total_cost, sum(cost for _course, cost in course_costs))
def test_start_purchase(self):
# Start the purchase, which will mark the cart as "paying"
cart = Order.get_cart_for_user(user=self.user)
CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor', currency='usd')
cart.start_purchase()
self.assertEqual(cart.status, 'paying')
for item in cart.orderitem_set.all():
self.assertEqual(item.status, 'paying')
# Starting the purchase should be idempotent
cart.start_purchase()
self.assertEqual(cart.status, 'paying')
for item in cart.orderitem_set.all():
self.assertEqual(item.status, 'paying')
# If we retrieve the cart for the user, we should get a different order
next_cart = Order.get_cart_for_user(user=self.user)
self.assertNotEqual(cart, next_cart)
self.assertEqual(next_cart.status, 'cart')
# Complete the first purchase
cart.purchase()
self.assertEqual(cart.status, 'purchased')
for item in cart.orderitem_set.all():
self.assertEqual(item.status, 'purchased')
# Starting the purchase again should be a no-op
cart.start_purchase()
self.assertEqual(cart.status, 'purchased')
for item in cart.orderitem_set.all():
self.assertEqual(item.status, 'purchased')
def test_purchase(self):
# This test is for testing the subclassing functionality of OrderItem, but in
# order to do this, we end up testing the specific functionality of
......
......@@ -31,8 +31,8 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey
from student.tests.factories import UserFactory
from student.models import CourseEnrollment
from course_modes.tests.factories import CourseModeFactory
from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE
from course_modes.models import CourseMode
from shoppingcart.models import Order, CertificateItem
from verify_student.views import render_to_response
from verify_student.models import SoftwareSecurePhotoVerification
from reverification.tests.factories import MidcourseReverificationWindowFactory
......@@ -66,8 +66,8 @@ class StartView(TestCase):
self.assertHttpForbidden(self.client.get(self.start_url()))
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
class TestCreateOrderView(TestCase):
@override_settings(MODULESTORE=MODULESTORE_CONFIG)
class TestCreateOrderView(ModuleStoreTestCase):
"""
Tests for the create_order view of verified course registration process
"""
......@@ -75,7 +75,7 @@ class TestCreateOrderView(TestCase):
self.user = UserFactory.create(username="rusty", password="test")
self.client.login(username="rusty", password="test")
self.course_id = 'Robot/999/Test_Course'
CourseFactory.create(org='Robot', number='999', display_name='Test Course')
self.course = CourseFactory.create(org='Robot', number='999', display_name='Test Course')
verified_mode = CourseMode(
course_id=SlashSeparatedCourseKey("Robot", "999", 'Test_Course'),
mode_slug="verified",
......@@ -156,6 +156,14 @@ class TestCreateOrderView(TestCase):
self.assertTrue(json_response.get('success'))
self.assertIsNotNone(json_response.get('orderNumber'))
# Verify that the order exists and is configured correctly
order = Order.objects.get(user=self.user)
self.assertEqual(order.status, 'paying')
item = CertificateItem.objects.get(order=order)
self.assertEqual(item.status, 'paying')
self.assertEqual(item.course_id, self.course.id)
self.assertEqual(item.mode, 'verified')
@override_settings(MODULESTORE=MODULESTORE_CONFIG)
class TestVerifyView(ModuleStoreTestCase):
......
......@@ -227,6 +227,15 @@ def create_order(request):
enrollment_mode = current_mode.slug
CertificateItem.add_to_order(cart, course_id, amount, enrollment_mode)
# Change the order's status so that we don't accidentally modify it later.
# We need to do this to ensure that the parameters we send to the payment system
# match what we store in the database.
# (Ordinarily we would do this client-side when the user submits the form, but since
# the JavaScript on this page does that immediately, we make the change here instead.
# This avoids a second AJAX call and some additional complication of the JavaScript.)
# If a user later re-enters the verification / payment flow, she will create a new order.
cart.start_purchase()
callback_url = request.build_absolute_uri(
reverse("shoppingcart.views.postpay_callback")
)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment