Commit 18deda93 by Diana Huang

Merge pull request #6270 from edx/diana/track-refunds

Add tracking for refunds.
parents df46e843 f3c26c89
...@@ -438,15 +438,23 @@ class Order(models.Model): ...@@ -438,15 +438,23 @@ class Order(models.Model):
csv_file, courses_info = self.generate_registration_codes_csv(orderitems, site_name) csv_file, courses_info = self.generate_registration_codes_csv(orderitems, site_name)
self.send_confirmation_emails(orderitems, self.order_type == OrderTypes.BUSINESS, csv_file, site_name, courses_info) self.send_confirmation_emails(orderitems, self.order_type == OrderTypes.BUSINESS, csv_file, site_name, courses_info)
self._emit_purchase_event(orderitems) self._emit_order_event('Completed Order', orderitems)
def _emit_purchase_event(self, orderitems): def refund(self):
""" """
Emit an analytics purchase event for this Order. Will iterate over all associated Refund the given order. As of right now, this just marks the order as refunded.
"""
self.status = 'refunded'
self.save()
orderitems = OrderItem.objects.filter(order=self).select_subclasses()
self._emit_order_event('Refunded Order', orderitems)
def _emit_order_event(self, event_name, orderitems):
"""
Emit an analytics event with the given name for this Order. Will iterate over all associated
OrderItems and add them as products in the event as well. OrderItems and add them as products in the event as well.
""" """
event_name = 'Completed Order' # Required event name by Segment
try: try:
if settings.FEATURES.get('SEGMENT_IO_LMS') and settings.SEGMENT_IO_LMS_KEY: if settings.FEATURES.get('SEGMENT_IO_LMS') and settings.SEGMENT_IO_LMS_KEY:
tracking_context = tracker.get_tracker().resolve_context() tracking_context = tracker.get_tracker().resolve_context()
...@@ -1243,8 +1251,8 @@ class CertificateItem(OrderItem): ...@@ -1243,8 +1251,8 @@ class CertificateItem(OrderItem):
target_cert.status = 'refunded' target_cert.status = 'refunded'
target_cert.refund_requested_time = datetime.now(pytz.utc) target_cert.refund_requested_time = datetime.now(pytz.utc)
target_cert.save() target_cert.save()
target_cert.order.status = 'refunded'
target_cert.order.save() target_cert.order.refund()
order_number = target_cert.order_id order_number = target_cert.order_id
# send billing an email so they can handle refunding # send billing an email so they can handle refunding
......
...@@ -576,6 +576,35 @@ class CertificateItemTest(ModuleStoreTestCase): ...@@ -576,6 +576,35 @@ class CertificateItemTest(ModuleStoreTestCase):
self.mock_tracker = patcher.start() self.mock_tracker = patcher.start()
self.addCleanup(patcher.stop) self.addCleanup(patcher.stop)
analytics_patcher = patch('shoppingcart.models.analytics')
self.mock_analytics_tracker = analytics_patcher.start()
self.addCleanup(analytics_patcher.stop)
def _assert_refund_tracked(self):
"""
Assert that we fired a refund event.
"""
self.mock_analytics_tracker.track.assert_called_with( # pylint: disable=maybe-no-member
self.user.id,
'Refunded Order',
{
'orderId': 1,
'currency': 'usd',
'total': '40',
'products': [
{
'sku': u'CertificateItem.verified',
'name': unicode(self.course_key),
'category': unicode(self.course_key.org),
'price': '40',
'id': 1,
'quantity': 1
}
]
},
context={'Google Analytics': {'clientId': None}}
)
def test_existing_enrollment(self): def test_existing_enrollment(self):
CourseEnrollment.enroll(self.user, self.course_key) CourseEnrollment.enroll(self.user, self.course_key)
cart = Order.get_cart_for_user(user=self.user) cart = Order.get_cart_for_user(user=self.user)
...@@ -598,18 +627,29 @@ class CertificateItemTest(ModuleStoreTestCase): ...@@ -598,18 +627,29 @@ class CertificateItemTest(ModuleStoreTestCase):
self.assertEquals(cert_item.single_item_receipt_template, self.assertEquals(cert_item.single_item_receipt_template,
'shoppingcart/receipt.html') 'shoppingcart/receipt.html')
@override_settings(
SEGMENT_IO_LMS_KEY="foobar",
FEATURES={
'SEGMENT_IO_LMS': True,
'STORE_BILLING_INFO': True,
}
)
def test_refund_cert_callback_no_expiration(self): def test_refund_cert_callback_no_expiration(self):
# When there is no expiration date on a verified mode, the user can always get a refund # When there is no expiration date on a verified mode, the user can always get a refund
CourseEnrollment.enroll(self.user, self.course_key, 'verified') CourseEnrollment.enroll(self.user, self.course_key, 'verified')
cart = Order.get_cart_for_user(user=self.user) cart = Order.get_cart_for_user(user=self.user)
CertificateItem.add_to_order(cart, self.course_key, self.cost, 'verified') CertificateItem.add_to_order(cart, self.course_key, self.cost, 'verified')
cart.purchase()
CourseEnrollment.unenroll(self.user, self.course_key) # need to prevent analytics errors from appearing in stderr
with patch('sys.stderr', sys.stdout.write):
cart.purchase()
CourseEnrollment.unenroll(self.user, self.course_key)
target_certs = CertificateItem.objects.filter(course_id=self.course_key, user_id=self.user, status='refunded', mode='verified') target_certs = CertificateItem.objects.filter(course_id=self.course_key, user_id=self.user, status='refunded', mode='verified')
self.assertTrue(target_certs[0]) self.assertTrue(target_certs[0])
self.assertTrue(target_certs[0].refund_requested_time) self.assertTrue(target_certs[0].refund_requested_time)
self.assertEquals(target_certs[0].order.status, 'refunded') self.assertEquals(target_certs[0].order.status, 'refunded')
self._assert_refund_tracked()
def test_no_refund_on_cert_callback(self): def test_no_refund_on_cert_callback(self):
# If we explicitly skip refunds, the unenroll action should not modify the purchase. # If we explicitly skip refunds, the unenroll action should not modify the purchase.
...@@ -629,29 +669,40 @@ class CertificateItemTest(ModuleStoreTestCase): ...@@ -629,29 +669,40 @@ class CertificateItemTest(ModuleStoreTestCase):
self.assertFalse(target_certs[0].refund_requested_time) self.assertFalse(target_certs[0].refund_requested_time)
self.assertEquals(target_certs[0].order.status, 'purchased') self.assertEquals(target_certs[0].order.status, 'purchased')
@override_settings(
SEGMENT_IO_LMS_KEY="foobar",
FEATURES={
'SEGMENT_IO_LMS': True,
'STORE_BILLING_INFO': True,
}
)
def test_refund_cert_callback_before_expiration(self): def test_refund_cert_callback_before_expiration(self):
# If the expiration date has not yet passed on a verified mode, the user can be refunded # If the expiration date has not yet passed on a verified mode, the user can be refunded
many_days = datetime.timedelta(days=60) many_days = datetime.timedelta(days=60)
course = CourseFactory.create() course = CourseFactory.create()
course_key = course.id self.course_key = course.id
course_mode = CourseMode(course_id=course_key, course_mode = CourseMode(course_id=self.course_key,
mode_slug="verified", mode_slug="verified",
mode_display_name="verified cert", mode_display_name="verified cert",
min_price=self.cost, min_price=self.cost,
expiration_datetime=(datetime.datetime.now(pytz.utc) + many_days)) expiration_datetime=(datetime.datetime.now(pytz.utc) + many_days))
course_mode.save() course_mode.save()
CourseEnrollment.enroll(self.user, course_key, 'verified') CourseEnrollment.enroll(self.user, self.course_key, 'verified')
cart = Order.get_cart_for_user(user=self.user) cart = Order.get_cart_for_user(user=self.user)
CertificateItem.add_to_order(cart, course_key, self.cost, 'verified') CertificateItem.add_to_order(cart, self.course_key, self.cost, 'verified')
cart.purchase()
CourseEnrollment.unenroll(self.user, course_key) # need to prevent analytics errors from appearing in stderr
target_certs = CertificateItem.objects.filter(course_id=course_key, user_id=self.user, status='refunded', mode='verified') with patch('sys.stderr', sys.stdout.write):
cart.purchase()
CourseEnrollment.unenroll(self.user, self.course_key)
target_certs = CertificateItem.objects.filter(course_id=self.course_key, user_id=self.user, status='refunded', mode='verified')
self.assertTrue(target_certs[0]) self.assertTrue(target_certs[0])
self.assertTrue(target_certs[0].refund_requested_time) self.assertTrue(target_certs[0].refund_requested_time)
self.assertEquals(target_certs[0].order.status, 'refunded') self.assertEquals(target_certs[0].order.status, 'refunded')
self._assert_refund_tracked()
def test_refund_cert_callback_before_expiration_email(self): def test_refund_cert_callback_before_expiration_email(self):
""" Test that refund emails are being sent correctly. """ """ Test that refund emails are being sent correctly. """
......
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