Exemple #1
0
    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_get_cart_for_user(self):
     # create a cart
     cart = Order.get_cart_for_user(user=self.user)
     # add something to it
     CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified')
     # should return the same cart
     cart2 = Order.get_cart_for_user(user=self.user)
     self.assertEquals(cart2.orderitem_set.count(), 1)
 def test_billing_info_storage_off(self):
     cart = Order.get_cart_for_user(self.user)
     cart = Order.get_cart_for_user(self.user)
     self.purchase_with_data(cart)
     self.assertNotEqual(cart.bill_to_first, '')
     self.assertNotEqual(cart.bill_to_last, '')
     self.assertNotEqual(cart.bill_to_city, '')
     self.assertNotEqual(cart.bill_to_state, '')
     self.assertNotEqual(cart.bill_to_country, '')
     self.assertNotEqual(cart.bill_to_postalcode, '')
     # things we expect to be missing when the feature is off
     self.assertEqual(cart.bill_to_street1, '')
     self.assertEqual(cart.bill_to_street2, '')
     self.assertEqual(cart.bill_to_ccnum, '')
     self.assertEqual(cart.bill_to_cardtype, '')
Exemple #4
0
    def setUp(self):
        patcher = patch('student.models.tracker')
        self.mock_tracker = patcher.start()
        self.user = UserFactory.create()
        self.user.set_password('password')
        self.user.save()
        self.instructor = AdminFactory.create()
        self.cost = 40
        self.coupon_code = 'abcde'
        self.reg_code = 'qwerty'
        self.percentage_discount = 10
        self.course = CourseFactory.create(org='MITx', number='999', display_name='Robot Super Course')
        self.course_key = self.course.id
        self.course_mode = CourseMode(course_id=self.course_key,
                                      mode_slug="honor",
                                      mode_display_name="honor cert",
                                      min_price=self.cost)
        self.course_mode.save()

        #Saving another testing course mode
        self.testing_cost = 20
        self.testing_course = CourseFactory.create(org='edX', number='888', display_name='Testing Super Course')
        self.testing_course_mode = CourseMode(course_id=self.testing_course.id,
                                              mode_slug="honor",
                                              mode_display_name="testing honor cert",
                                              min_price=self.testing_cost)
        self.testing_course_mode.save()

        verified_course = CourseFactory.create(org='org', number='test', display_name='Test Course')
        self.verified_course_key = verified_course.id
        self.cart = Order.get_cart_for_user(self.user)
        self.addCleanup(patcher.stop)
Exemple #5
0
    def setUp(self):
        patcher = patch("student.models.tracker")
        self.mock_tracker = patcher.start()
        self.user = UserFactory.create()
        self.user.set_password("password")
        self.user.save()
        self.instructor = AdminFactory.create()
        self.cost = 40
        self.coupon_code = "abcde"
        self.reg_code = "qwerty"
        self.percentage_discount = 10
        self.course = CourseFactory.create(org="MITx", number="999", display_name="Robot Super Course")
        self.course_key = self.course.id
        self.course_mode = CourseMode(
            course_id=self.course_key, mode_slug="honor", mode_display_name="honor cert", min_price=self.cost
        )
        self.course_mode.save()

        # Saving another testing course mode
        self.testing_cost = 20
        self.testing_course = CourseFactory.create(org="edX", number="888", display_name="Testing Super Course")
        self.testing_course_mode = CourseMode(
            course_id=self.testing_course.id,
            mode_slug="honor",
            mode_display_name="testing honor cert",
            min_price=self.testing_cost,
        )
        self.testing_course_mode.save()

        verified_course = CourseFactory.create(org="org", number="test", display_name="Test Course")
        self.verified_course_key = verified_course.id
        self.cart = Order.get_cart_for_user(self.user)
        self.addCleanup(patcher.stop)
    def test_student_used_enrollment_code_for_course_enrollment(self):
        """
        test to check the user enrollment source and payment status in the
        enrollment detailed report
        """
        student = UserFactory()
        self.client.login(username=student.username, password='******')
        student_cart = Order.get_cart_for_user(student)
        paid_course_reg_item = PaidCourseRegistration.add_to_order(student_cart, self.course.id)
        # update the quantity of the cart item paid_course_reg_item
        resp = self.client.post(reverse('shoppingcart.views.update_user_cart'),
                                {'ItemId': paid_course_reg_item.id, 'qty': '4'})
        self.assertEqual(resp.status_code, 200)
        student_cart.purchase()

        course_reg_codes = CourseRegistrationCode.objects.filter(order=student_cart)
        redeem_url = reverse('register_code_redemption', args=[course_reg_codes[0].code])
        response = self.client.get(redeem_url)
        self.assertEquals(response.status_code, 200)
        # check button text
        self.assertTrue('Activate Course Enrollment' in response.content)

        response = self.client.post(redeem_url)
        self.assertEquals(response.status_code, 200)

        task_input = {'features': []}
        with patch('instructor_task.tasks_helper._get_current_task'):
            result = upload_enrollment_report(None, None, self.course.id, task_input, 'generating_enrollment_report')
        self.assertDictContainsSubset({'attempted': 1, 'succeeded': 1, 'failed': 0}, result)
        self._verify_cell_data_in_csv(student.username, 'Enrollment Source', 'Used Registration Code')
        self._verify_cell_data_in_csv(student.username, 'Payment Status', 'purchased')
Exemple #7
0
 def test_purchase_item_email_boto_failure(self, error_logger):
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor')
     with patch.object(EmailMessage, 'send') as mock_send:
         mock_send.side_effect = BotoServerError("status", "reason")
         cart.purchase()
         self.assertTrue(error_logger.called)
 def test_cart_clear(self):
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified')
     CertificateItem.add_to_order(cart, 'test/course1', self.cost, 'verified')
     self.assertEquals(cart.orderitem_set.count(), 2)
     cart.clear()
     self.assertEquals(cart.orderitem_set.count(), 0)
Exemple #9
0
    def test_single_item_template(self):
        cart = Order.get_cart_for_user(user=self.user)
        cert_item = CertificateItem.add_to_order(cart, self.course_key, self.cost, 'verified')
        self.assertEquals(cert_item.single_item_receipt_template, 'shoppingcart/receipt.html')

        cert_item = CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor')
        self.assertEquals(cert_item.single_item_receipt_template, 'shoppingcart/receipt.html')
Exemple #10
0
 def setUp(self):
     """ Create a user and an order. """
     self.user = UserFactory()
     self.order = Order.get_cart_for_user(self.user)
     self.order_item = OrderItem.objects.create(
         order=self.order, user=self.user, unit_cost=self.COST, line_cost=self.COST
     )
 def test_purchase_twice(self):
     cart = Order.get_cart_for_user(self.user)
     CertificateItem.add_to_order(cart, self.course_id, self.cost, 'honor')
     # purchase the cart more than once
     cart.purchase()
     cart.purchase()
     self.assertEquals(len(mail.outbox), 1)
Exemple #12
0
    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
        many_days = datetime.timedelta(days=60)

        course = CourseFactory.create(org="refund_before_expiration", number="test", display_name="one")
        course_key = course.id
        course_mode = CourseMode(
            course_id=course_key,
            mode_slug="verified",
            mode_display_name="verified cert",
            min_price=self.cost,
            expiration_datetime=(datetime.datetime.now(pytz.utc) + many_days),
        )
        course_mode.save()

        CourseEnrollment.enroll(self.user, course_key, "verified")
        cart = Order.get_cart_for_user(user=self.user)
        CertificateItem.add_to_order(cart, course_key, self.cost, "verified")
        cart.purchase()

        CourseEnrollment.unenroll(self.user, course_key)
        target_certs = CertificateItem.objects.filter(
            course_id=course_key, user_id=self.user, status="refunded", mode="verified"
        )
        self.assertTrue(target_certs[0])
        self.assertTrue(target_certs[0].refund_requested_time)
        self.assertEquals(target_certs[0].order.status, "refunded")
Exemple #13
0
    def test_refund_cert_callback_before_expiration_email(self):
        """ Test that refund emails are being sent correctly. """
        course = CourseFactory.create()
        course_key = course.id
        many_days = datetime.timedelta(days=60)

        course_mode = CourseMode(course_id=course_key,
                                 mode_slug="verified",
                                 mode_display_name="verified cert",
                                 min_price=self.cost,
                                 expiration_datetime=datetime.datetime.now(pytz.utc) + many_days)
        course_mode.save()

        CourseEnrollment.enroll(self.user, course_key, 'verified')
        cart = Order.get_cart_for_user(user=self.user)
        CertificateItem.add_to_order(cart, course_key, self.cost, 'verified')
        cart.purchase()

        mail.outbox = []
        with patch('shoppingcart.models.log.error') as mock_error_logger:
            CourseEnrollment.unenroll(self.user, course_key)
            self.assertFalse(mock_error_logger.called)
            self.assertEquals(len(mail.outbox), 1)
            self.assertEquals('[Refund] User-Requested Refund', mail.outbox[0].subject)
            self.assertEquals(settings.PAYMENT_SUPPORT_EMAIL, mail.outbox[0].from_email)
            self.assertIn('has requested a refund on Order', mail.outbox[0].body)
Exemple #14
0
 def test_orderItem_purchased_callback(self):
     """
     This tests that calling purchased_callback on the base OrderItem class raises NotImplementedError
     """
     item = OrderItem(user=self.user, order=Order.get_cart_for_user(self.user))
     with self.assertRaises(NotImplementedError):
         item.purchased_callback()
Exemple #15
0
    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
        many_days = datetime.timedelta(days=60)

        course = CourseFactory.create()
        self.course_key = course.id
        course_mode = CourseMode(course_id=self.course_key,
                                 mode_slug="verified",
                                 mode_display_name="verified cert",
                                 min_price=self.cost,
                                 expiration_datetime=(datetime.datetime.now(pytz.utc) + many_days))
        course_mode.save()

        # need to prevent analytics errors from appearing in stderr
        with patch('sys.stderr', sys.stdout.write):
            CourseEnrollment.enroll(self.user, self.course_key, 'verified')
            cart = Order.get_cart_for_user(user=self.user)
            CertificateItem.add_to_order(cart, self.course_key, self.cost, 'verified')
            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].refund_requested_time)
        self.assertEquals(target_certs[0].order.status, 'refunded')
        self._assert_refund_tracked()
Exemple #16
0
 def _create_cart(self):
     """Creates a cart and adds a CertificateItem to it"""
     cart = Order.get_cart_for_user(UserFactory.create())
     item = CertificateItem.add_to_order(
         cart, self.course_key, 10, 'honor', currency='usd'
     )
     return cart, item
Exemple #17
0
def checkout_with_shoppingcart(request, user, course_key, course_mode, amount):
    """ Create an order and trigger checkout using shoppingcart."""
    cart = Order.get_cart_for_user(user)
    cart.clear()
    enrollment_mode = course_mode.slug
    CertificateItem.add_to_order(cart, course_key, 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")
    )

    payment_data = {
        'payment_processor_name': settings.CC_PROCESSOR_NAME,
        'payment_page_url': get_purchase_endpoint(),
        'payment_form_data': get_signed_purchase_params(
            cart,
            callback_url=callback_url,
            extra_data=[unicode(course_key), course_mode.slug]
        ),
    }
    return payment_data
Exemple #18
0
    def test_confirmation_email_error(self):
        CourseMode.objects.create(
            course_id=self.course_key,
            mode_slug="verified",
            mode_display_name="Verified",
            min_price=self.cost
        )

        cart = Order.get_cart_for_user(self.user)
        CertificateItem.add_to_order(cart, self.course_key, self.cost, 'verified')

        # Simulate an error when sending the confirmation
        # email.  This should NOT raise an exception.
        # If it does, then the implicit view-level
        # transaction could cause a roll-back, effectively
        # reversing order fulfillment.
        with patch.object(mail.message.EmailMessage, 'send') as mock_send:
            mock_send.side_effect = Exception("Kaboom!")
            cart.purchase()

        # Verify that the purchase completed successfully
        self.assertEqual(cart.status, 'purchased')

        # Verify that the user is enrolled as "verified"
        mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course_key)
        self.assertTrue(is_active)
        self.assertEqual(mode, 'verified')
Exemple #19
0
    def test_payment_separate_from_verification_email(self):
        cart = Order.get_cart_for_user(user=self.user)
        item = CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor')
        cart.purchase()

        self.assertEquals(len(mail.outbox), 1)
        # Verify that the verification reminder appears in the sent email.
        self.assertIn(item.additional_instruction_text(), mail.outbox[0].body)
 def add_to_cart(self):
     """
     Adds content to self.user's cart
     """
     course = CourseFactory.create(org='MITx', number='999', display_name='Robot Super Course')
     CourseModeFactory.create(course_id=course.id)
     cart = Order.get_cart_for_user(self.user)
     PaidCourseRegistration.add_to_order(cart, course.id)
Exemple #21
0
    def test_retire_order_cart(self):
        """Test that an order in cart can successfully be retired"""
        cart = Order.get_cart_for_user(user=self.user)
        CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor', currency='usd')

        cart.retire()
        self.assertEqual(cart.status, 'defunct-cart')
        self.assertEqual(cart.orderitem_set.get().status, 'defunct-cart')
 def test_user_cart_has_items(self):
     anon = AnonymousUser()
     self.assertFalse(Order.user_cart_has_items(anon))
     self.assertFalse(Order.user_cart_has_items(self.user))
     cart = Order.get_cart_for_user(self.user)
     item = OrderItem(order=cart, user=self.user)
     item.save()
     self.assertTrue(Order.user_cart_has_items(self.user))
Exemple #23
0
 def test_cart_clear(self):
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor')
     CertificateItem.add_to_order(cart, SlashSeparatedCourseKey('org', 'test', 'Test_Course_1'), self.cost, 'honor')
     self.assertEquals(cart.orderitem_set.count(), 2)
     self.assertTrue(cart.has_items())
     cart.clear()
     self.assertEquals(cart.orderitem_set.count(), 0)
     self.assertFalse(cart.has_items())
 def test_cart_clear(self):
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_id, self.cost, 'honor')
     CertificateItem.add_to_order(cart, 'org/test/Test_Course_1', self.cost, 'honor')
     self.assertEquals(cart.orderitem_set.count(), 2)
     self.assertTrue(cart.has_items())
     cart.clear()
     self.assertEquals(cart.orderitem_set.count(), 0)
     self.assertFalse(cart.has_items())
Exemple #25
0
 def test_user_cart_has_both_items(self):
     """
     This test exists b/c having both CertificateItem and PaidCourseRegistration in an order used to break
     PaidCourseRegistration.contained_in_order
     """
     cart = Order.get_cart_for_user(self.user)
     CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor')
     PaidCourseRegistration.add_to_order(self.cart, self.course_key)
     self.assertTrue(PaidCourseRegistration.contained_in_order(cart, self.course_key))
Exemple #26
0
 def test_existing_enrollment(self):
     CourseEnrollment.enroll(self.user, self.course_id)
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified')
     # verify that we are still enrolled
     self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course_id))
     cart.purchase()
     enrollment = CourseEnrollment.objects.get(user=self.user, course_id=self.course_id)
     self.assertEquals(enrollment.mode, u'verified')
Exemple #27
0
 def _enroll(self, purchase=True):
     # pylint: disable=missing-docstring
     CourseEnrollment.enroll(self.student, self.course_id, self.course_mode.mode_slug)
     if purchase:
         self.order = Order.get_cart_for_user(self.student)
         CertificateItem.add_to_order(self.order, self.course_id, 1, self.course_mode.mode_slug)
         self.order.purchase()
     self.course_mode.expiration_datetime = datetime.datetime(1983, 4, 6, tzinfo=pytz.UTC)
     self.course_mode.save()
Exemple #28
0
def create_order(request):
    """
    Submit PhotoVerification and create a new Order for this verified cert
    """
    if not SoftwareSecurePhotoVerification.user_has_valid_or_pending(request.user):
        attempt = SoftwareSecurePhotoVerification(user=request.user)
        b64_face_image = request.POST['face_image'].split(",")[1]
        b64_photo_id_image = request.POST['photo_id_image'].split(",")[1]

        attempt.upload_face_image(b64_face_image.decode('base64'))
        attempt.upload_photo_id_image(b64_photo_id_image.decode('base64'))
        attempt.mark_ready()

        attempt.save()

    course_id = request.POST['course_id']
    course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    donation_for_course = request.session.get('donation_for_course', {})
    current_donation = donation_for_course.get(course_id, decimal.Decimal(0))
    contribution = request.POST.get("contribution", donation_for_course.get(course_id, 0))
    try:
        amount = decimal.Decimal(contribution).quantize(decimal.Decimal('.01'), rounding=decimal.ROUND_DOWN)
    except decimal.InvalidOperation:
        return HttpResponseBadRequest(_("Selected price is not valid number."))

    if amount != current_donation:
        donation_for_course[course_id] = amount
        request.session['donation_for_course'] = donation_for_course

    # prefer professional mode over verified_mode
    current_mode = CourseMode.verified_mode_for_course(course_id)

    if current_mode.slug == 'professional':
        amount = current_mode.min_price

    # make sure this course has a verified mode
    if not current_mode:
        return HttpResponseBadRequest(_("This course doesn't support verified certificates"))

    if amount < current_mode.min_price:
        return HttpResponseBadRequest(_("No selected price or selected price is below minimum."))

    # I know, we should check this is valid. All kinds of stuff missing here
    cart = Order.get_cart_for_user(request.user)
    cart.clear()
    enrollment_mode = current_mode.slug
    CertificateItem.add_to_order(cart, course_id, amount, enrollment_mode)

    callback_url = request.build_absolute_uri(
        reverse("shoppingcart.views.postpay_callback")
    )
    params = get_signed_purchase_params(
        cart, callback_url=callback_url
    )

    return HttpResponse(json.dumps(params), content_type="text/json")
Exemple #29
0
 def test_purchase_item_failure(self):
     # once again, we're testing against the specific implementation of
     # CertificateItem
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified')
     with patch('shoppingcart.models.CertificateItem.save', side_effect=DatabaseError):
         with self.assertRaises(DatabaseError):
             cart.purchase()
             # verify that we rolled back the entire transaction
             self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course_id))
Exemple #30
0
 def test_user_cart_has_items(self):
     anon = AnonymousUser()
     self.assertFalse(Order.user_cart_has_items(anon))
     self.assertFalse(Order.user_cart_has_items(self.user))
     cart = Order.get_cart_for_user(self.user)
     item = OrderItem(order=cart, user=self.user)
     item.save()
     self.assertTrue(Order.user_cart_has_items(self.user))
     self.assertFalse(Order.user_cart_has_items(self.user, [CertificateItem]))
     self.assertFalse(Order.user_cart_has_items(self.user, [PaidCourseRegistration]))
    def test_lookup_valid_redeemed_registration_code(self):
        """
        test to lookup for the valid and redeemed registration code
        and then mark that registration code as un_redeemed
        which will unenroll the user and delete the redemption
        entry from the database.
        """
        student = UserFactory()
        self.client.login(username=student.username, password='******')
        cart = Order.get_cart_for_user(student)
        cart.order_type = 'business'
        cart.save()
        CourseRegCodeItem.add_to_order(cart, self.course.id, 2)
        cart.purchase()

        reg_code = CourseRegistrationCode.objects.filter(order=cart)[0]

        enrollment = CourseEnrollment.enroll(student, self.course.id)

        RegistrationCodeRedemption.objects.create(registration_code=reg_code,
                                                  redeemed_by=student,
                                                  course_enrollment=enrollment)
        self.client.login(username=self.instructor.username, password='******')
        data = {'registration_code': reg_code.code}
        response = self.client.get(self.lookup_code_url, data)
        self.assertEqual(response.status_code, 200)
        json_dict = json.loads(response.content)
        self.assertTrue(json_dict['is_registration_code_valid'])
        self.assertTrue(json_dict['is_registration_code_redeemed'])

        # now mark the registration code as unredeemed
        # this will unenroll the user and removed the redemption entry from
        # the database.

        data = {
            'registration_code': reg_code.code,
            'action_type': 'unredeem_registration_code'
        }
        response = self.client.post(self.registration_code_detail_url, data)
        self.assertEqual(response.status_code, 200)

        json_dict = json.loads(response.content)
        message = _('This enrollment code has been marked as unused.')
        self.assertEqual(message, json_dict['message'])

        redemption = RegistrationCodeRedemption.get_registration_code_redemption(
            reg_code.code, self.course.id)
        self.assertIsNone(redemption)

        # now the student course enrollment should be false.
        enrollment = CourseEnrollment.get_enrollment(student, self.course.id)
        self.assertEqual(enrollment.is_active, False)
Exemple #32
0
def create_order(request):
    """
    Submit PhotoVerification and create a new Order for this verified cert
    """
    if not SoftwareSecurePhotoVerification.user_has_valid_or_pending(
            request.user):
        attempt = SoftwareSecurePhotoVerification(user=request.user)
        b64_face_image = request.POST['face_image'].split(",")[1]
        b64_photo_id_image = request.POST['photo_id_image'].split(",")[1]

        attempt.upload_face_image(b64_face_image.decode('base64'))
        attempt.upload_photo_id_image(b64_photo_id_image.decode('base64'))
        attempt.mark_ready()

        attempt.save()

    course_id = request.POST['course_id']
    course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    donation_for_course = request.session.get('donation_for_course', {})
    current_donation = donation_for_course.get(course_id, decimal.Decimal(0))
    contribution = request.POST.get("contribution",
                                    donation_for_course.get(course_id, 0))
    try:
        amount = decimal.Decimal(contribution).quantize(
            decimal.Decimal('.01'), rounding=decimal.ROUND_DOWN)
    except decimal.InvalidOperation:
        return HttpResponseBadRequest(_("Selected price is not valid number."))

    if amount != current_donation:
        donation_for_course[course_id] = amount
        request.session['donation_for_course'] = donation_for_course

    verified_mode = CourseMode.modes_for_course_dict(course_id).get(
        'verified', None)

    # make sure this course has a verified mode
    if not verified_mode:
        return HttpResponseBadRequest(
            _("This course doesn't support verified certificates"))

    if amount < verified_mode.min_price:
        return HttpResponseBadRequest(
            _("No selected price or selected price is below minimum."))

    # I know, we should check this is valid. All kinds of stuff missing here
    cart = Order.get_cart_for_user(request.user)
    cart.clear()
    CertificateItem.add_to_order(cart, course_id, amount, 'verified')

    params = get_signed_purchase_params(cart)

    return HttpResponse(json.dumps(params), content_type="text/json")
Exemple #33
0
 def test_generate_receipt_instructions_callchain(self):
     """
     This tests the generate_receipt_instructions call chain (ie calling the function on the
     cart also calls it on items in the cart
     """
     cart = Order.get_cart_for_user(self.user)
     item = OrderItem(user=self.user, order=cart)
     item.save()
     self.assertTrue(cart.has_items())
     with patch.object(OrderItem, 'generate_receipt_instructions',
                       self.mock_gen_inst):
         cart.generate_receipt_instructions()
         self.mock_gen_inst.assert_called_with()
Exemple #34
0
 def test_purchase_item_failure(self):
     # once again, we're testing against the specific implementation of
     # CertificateItem
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_id, self.cost,
                                  'verified')
     with patch('shoppingcart.models.CertificateItem.save',
                side_effect=DatabaseError):
         with self.assertRaises(DatabaseError):
             cart.purchase()
             # verify that we rolled back the entire transaction
             self.assertFalse(
                 CourseEnrollment.is_enrolled(self.user, self.course_id))
Exemple #35
0
 def test_retire_order_already_retired(self, status):
     """
     Check that orders that have already been retired noop when the method
     is called on them again.
     """
     cart = Order.get_cart_for_user(user=self.user)
     item = CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor', currency='usd')
     cart.status = item.status = status
     cart.save()
     item.save()
     cart.retire()
     self.assertEqual(cart.status, status)
     self.assertEqual(item.status, status)
Exemple #36
0
 def setUp(self):
     self.user = UserFactory.create()
     self.user.set_password('password')
     self.user.save()
     self.course_id = "MITx/999/Robot_Super_Course"
     self.cost = 40
     self.course = CourseFactory.create(org='MITx', number='999', display_name='Robot Super Course')
     self.course_mode = CourseMode(course_id=self.course_id,
                                   mode_slug="honor",
                                   mode_display_name="honor cert",
                                   min_price=self.cost)
     self.course_mode.save()
     self.cart = Order.get_cart_for_user(self.user)
Exemple #37
0
    def setUp(self):
        super(PaidCourseRegistrationTest, self).setUp()

        self.user = UserFactory.create()
        self.cost = 40
        self.course = CourseFactory.create()
        self.course_key = self.course.id
        self.course_mode = CourseMode(course_id=self.course_key,
                                      mode_slug="honor",
                                      mode_display_name="honor cert",
                                      min_price=self.cost)
        self.course_mode.save()
        self.cart = Order.get_cart_for_user(self.user)
Exemple #38
0
 def _create_and_purchase_verified(self, student, course_id):
     """
     Creates a verified mode for the course and purchases it for the student.
     """
     course_mode = CourseMode(course_id=course_id,
                              mode_slug='verified',
                              mode_display_name='verified cert',
                              min_price=50)
     course_mode.save()
     # When there is no expiration date on a verified mode, the user can always get a refund
     cart = Order.get_cart_for_user(user=student)
     CertificateItem.add_to_order(cart, course_id, 50, 'verified')
     cart.purchase()
 def test_billing_info_storage_on(self):
     cart = Order.get_cart_for_user(self.user)
     self.purchase_with_data(cart)
     self.assertNotEqual(cart.bill_to_first, '')
     self.assertNotEqual(cart.bill_to_last, '')
     self.assertNotEqual(cart.bill_to_street1, '')
     self.assertNotEqual(cart.bill_to_street2, '')
     self.assertNotEqual(cart.bill_to_postalcode, '')
     self.assertNotEqual(cart.bill_to_ccnum, '')
     self.assertNotEqual(cart.bill_to_cardtype, '')
     self.assertNotEqual(cart.bill_to_city, '')
     self.assertNotEqual(cart.bill_to_state, '')
     self.assertNotEqual(cart.bill_to_country, '')
Exemple #40
0
    def test_retire_order_paying(self):
        """Test that an order in "paying" can successfully be retired"""
        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()

        cart.retire()
        self.assertEqual(cart.status, 'defunct-paying')
        self.assertEqual(cart.orderitem_set.get().status, 'defunct-paying')
Exemple #41
0
 def test_existing_enrollment(self):
     CourseEnrollment.enroll(self.user, self.course_key)
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_key, self.cost,
                                  'verified')
     # verify that we are still enrolled
     self.assertTrue(
         CourseEnrollment.is_enrolled(self.user, self.course_key))
     self.mock_tracker.reset_mock()
     cart.purchase()
     enrollment = CourseEnrollment.objects.get(user=self.user,
                                               course_id=self.course_key)
     self.assertEqual(enrollment.mode, u'verified')
Exemple #42
0
    def setUp(self):
        super(ItemizedPurchaseReportTest, self).setUp()

        self.user = UserFactory.create()
        self.cost = 40
        self.course = CourseFactory.create(org='MITx',
                                           number='999',
                                           display_name=u'Robot Super Course')
        self.course_key = self.course.id
        course_mode = CourseMode(course_id=self.course_key,
                                 mode_slug="honor",
                                 mode_display_name="honor cert",
                                 min_price=self.cost)
        course_mode.save()
        course_mode2 = CourseMode(course_id=self.course_key,
                                  mode_slug="verified",
                                  mode_display_name="verified cert",
                                  min_price=self.cost)
        course_mode2.save()
        self.annotation = PaidCourseRegistrationAnnotation(
            course_id=self.course_key, annotation=self.TEST_ANNOTATION)
        self.annotation.save()
        self.course_reg_code_annotation = CourseRegCodeItemAnnotation(
            course_id=self.course_key, annotation=self.TEST_ANNOTATION)
        self.course_reg_code_annotation.save()
        self.cart = Order.get_cart_for_user(self.user)
        self.reg = PaidCourseRegistration.add_to_order(
            self.cart, self.course_key, mode_slug=course_mode.mode_slug)
        self.cert_item = CertificateItem.add_to_order(self.cart,
                                                      self.course_key,
                                                      self.cost, 'verified')
        self.cart.purchase()
        self.now = datetime.datetime.now(pytz.UTC)

        paid_reg = PaidCourseRegistration.objects.get(
            course_id=self.course_key, user=self.user)
        paid_reg.fulfilled_time = self.now
        paid_reg.refund_requested_time = self.now
        paid_reg.save()

        cert = CertificateItem.objects.get(course_id=self.course_key,
                                           user=self.user)
        cert.fulfilled_time = self.now
        cert.refund_requested_time = self.now
        cert.save()

        self.CORRECT_CSV = dedent("""
            Purchase Time,Order ID,Status,Quantity,Unit Cost,Total Cost,Currency,Description,Comments
            {time_str},1,purchased,1,40.00,40.00,usd,Registration for Course: Robot Super Course,Ba\xc3\xbc\xe5\x8c\x85
            {time_str},1,purchased,1,40.00,40.00,usd,verified cert for course Robot Super Course,
            """.format(time_str=str(self.now)))
Exemple #43
0
def change_enrollment(strategy, user=None, *args, **kwargs):
    """Enroll a user in a course.

    If a user entered the authentication flow when trying to enroll
    in a course, then attempt to enroll the user.
    We will try to do this if the pipeline was started with the
    querystring param `enroll_course_id`.

    In the following cases, we can't enroll the user:
        * The course does not have an honor mode.
        * The course has an honor mode with a minimum price.
        * The course is not yet open for enrollment.
        * The course does not exist.

    If we can't enroll the user now, then skip this step.
    For paid courses, users will be redirected to the payment flow
    upon completion of the authentication pipeline
    (configured using the ?next parameter to the third party auth login url).

    """
    enroll_course_id = strategy.session_get('enroll_course_id')
    if enroll_course_id:
        course_id = CourseKey.from_string(enroll_course_id)
        modes = CourseMode.modes_for_course_dict(course_id)
        # If the email opt in parameter is found, set the preference.
        email_opt_in = strategy.session_get(AUTH_EMAIL_OPT_IN_KEY)
        if email_opt_in:
            opt_in = email_opt_in.lower() == 'true'
            profile.update_email_opt_in(user.username, course_id.org, opt_in)
        if CourseMode.can_auto_enroll(course_id, modes_dict=modes):
            try:
                CourseEnrollment.enroll(user, course_id, check_access=True)
            except CourseEnrollmentException:
                pass
            except Exception as ex:
                logger.exception(ex)

        # Handle white-label courses as a special case
        # If a course is white-label, we should add it to the shopping cart.
        elif CourseMode.is_white_label(course_id, modes_dict=modes):
            try:
                cart = Order.get_cart_for_user(user)
                PaidCourseRegistration.add_to_order(cart, course_id)
            except (CourseDoesNotExistException, ItemAlreadyInCartException,
                    AlreadyEnrolledInCourseException):
                pass
            # It's more important to complete login than to
            # ensure that the course was added to the shopping cart.
            # Log errors, but don't stop the authentication pipeline.
            except Exception as ex:
                logger.exception(ex)
Exemple #44
0
    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
        CourseEnrollment.enroll(self.user, self.course_id, 'verified')
        cart = Order.get_cart_for_user(user=self.user)
        CertificateItem.add_to_order(cart, self.course_id, self.cost,
                                     'verified')
        cart.purchase()

        CourseEnrollment.unenroll(self.user, self.course_id)
        target_certs = CertificateItem.objects.filter(course_id=self.course_id,
                                                      user_id=self.user,
                                                      status='refunded',
                                                      mode='verified')
        self.assertTrue(target_certs[0])
Exemple #45
0
    def test_already_in_cart(self):
        """
        This makes sure if a user has this course in the cart, that the expected message
        appears
        """
        self.setup_user()
        cart = Order.get_cart_for_user(self.user)
        PaidCourseRegistration.add_to_order(cart, self.course.id)

        url = reverse('about_course', args=[text_type(self.course.id)])
        resp = self.client.get(url)
        self.assertEqual(resp.status_code, 200)
        self.assertIn("This course is in your", resp.content)
        self.assertNotIn("Add buyme to Cart <span>($10 USD)</span>", resp.content)
 def test_total_cost(self):
     cart = Order.get_cart_for_user(user=self.user)
     # add items to the order
     course_costs = [('org/test/Test_Course_1', 30),
                     ('org/test/Test_Course_2', 40),
                     ('org/test/Test_Course_3', 10),
                     ('org/test/Test_Course_4', 20)]
     for course, cost in course_costs:
         CertificateItem.add_to_order(
             cart, SlashSeparatedCourseKey.from_deprecated_string(course),
             cost, 'honor')
     self.assertEquals(cart.orderitem_set.count(), len(course_costs))
     self.assertEquals(cart.total_cost,
                       sum(cost for _course, cost in course_costs))
Exemple #47
0
 def test_purchase_item_failure(self):
     # once again, we're testing against the specific implementation of
     # CertificateItem
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor')
     with patch('lms.djangoapps.shoppingcart.models.CertificateItem.save',
                side_effect=DatabaseError):
         with self.assertRaises(DatabaseError):
             cart.purchase()
             # verify that we rolled back the entire transaction
             self.assertFalse(
                 CourseEnrollment.is_enrolled(self.user, self.course_key))
             # verify that e-mail wasn't sent
             self.assertEqual(len(mail.outbox), 0)
 def test_billing_info_storage_off(self):
     cart = Order.get_cart_for_user(self.user)
     self.purchase_with_data(cart)
     self.assertNotEqual(cart.bill_to_first, '')
     self.assertNotEqual(cart.bill_to_last, '')
     self.assertNotEqual(cart.bill_to_city, '')
     self.assertNotEqual(cart.bill_to_state, '')
     self.assertNotEqual(cart.bill_to_country, '')
     self.assertNotEqual(cart.bill_to_postalcode, '')
     # things we expect to be missing when the feature is off
     self.assertEqual(cart.bill_to_street1, '')
     self.assertEqual(cart.bill_to_street2, '')
     self.assertEqual(cart.bill_to_ccnum, '')
     self.assertEqual(cart.bill_to_cardtype, '')
Exemple #49
0
    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)
Exemple #50
0
    def test_generate_receipt_instructions_callchain(self):
        """
        This tests the generate_receipt_instructions call chain (ie calling the function on the
        cart also calls it on items in the cart
        """
        mock_gen_inst = MagicMock(return_value=(OrderItemSubclassPK(OrderItem, 1), set([])))

        cart = Order.get_cart_for_user(self.user)
        item = OrderItem(user=self.user, order=cart)
        item.save()
        self.assertTrue(cart.has_items())
        with patch.object(OrderItem, 'generate_receipt_instructions', mock_gen_inst):
            cart.generate_receipt_instructions()
            mock_gen_inst.assert_called_with()
Exemple #51
0
 def test_billing_info_storage_on(self, render):
     cart = Order.get_cart_for_user(self.user)
     self.purchase_with_data(cart)
     self.assertNotEqual(cart.bill_to_first, '')
     self.assertNotEqual(cart.bill_to_last, '')
     self.assertNotEqual(cart.bill_to_street1, '')
     self.assertNotEqual(cart.bill_to_street2, '')
     self.assertNotEqual(cart.bill_to_postalcode, '')
     self.assertNotEqual(cart.bill_to_ccnum, '')
     self.assertNotEqual(cart.bill_to_cardtype, '')
     self.assertNotEqual(cart.bill_to_city, '')
     self.assertNotEqual(cart.bill_to_state, '')
     self.assertNotEqual(cart.bill_to_country, '')
     ((_, context), _) = render.call_args
     self.assertTrue(context['has_billing_info'])
Exemple #52
0
 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
     # CertificateItem, which is not quite good unit test form. Sorry.
     cart = Order.get_cart_for_user(user=self.user)
     self.assertFalse(
         CourseEnrollment.is_enrolled(self.user, self.course_id))
     CertificateItem.add_to_order(cart, self.course_id, self.cost,
                                  'verified')
     # course enrollment object should be created but still inactive
     self.assertFalse(
         CourseEnrollment.is_enrolled(self.user, self.course_id))
     cart.purchase()
     self.assertTrue(CourseEnrollment.is_enrolled(self.user,
                                                  self.course_id))
Exemple #53
0
    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
        # CertificateItem, which is not quite good unit test form. Sorry.
        cart = Order.get_cart_for_user(user=self.user)
        self.assertFalse(
            CourseEnrollment.is_enrolled(self.user, self.course_key))
        item = CertificateItem.add_to_order(cart, self.course_key, self.cost,
                                            'honor')
        # Course enrollment object should be created but still inactive
        self.assertFalse(
            CourseEnrollment.is_enrolled(self.user, self.course_key))
        # Analytics client pipes output to stderr when using the default client
        with patch('sys.stderr', sys.stdout.write):
            cart.purchase()
        self.assertTrue(
            CourseEnrollment.is_enrolled(self.user, self.course_key))

        # Test email sending
        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual('Order Payment Confirmation', mail.outbox[0].subject)
        self.assertIn(settings.PAYMENT_SUPPORT_EMAIL, mail.outbox[0].body)
        self.assertIn(six.text_type(cart.total_cost), mail.outbox[0].body)
        self.assertIn(item.additional_instruction_text(), mail.outbox[0].body)

        # Verify Google Analytics event fired for purchase
        self.mock_tracker.track.assert_called_once_with(
            self.user.id,
            'Completed Order',
            {
                'orderId':
                1,
                'currency':
                'usd',
                'total':
                '40.00',
                'revenue':
                '40.00',  # value for revenue field is same as total.
                'products': [{
                    'sku': u'CertificateItem.honor',
                    'name': six.text_type(self.course_key),
                    'category': six.text_type(self.course_key.org),
                    'price': '40.00',
                    'id': 1,
                    'quantity': 1
                }]
            },
        )
Exemple #54
0
    def test_retire_order_error(self, order_status, item_status, exception):
        """
        Test error cases for retiring an order:
        1) Order item has a different status than the order
        2) The order's status isn't in "cart" or "paying"
        """
        cart = Order.get_cart_for_user(user=self.user)
        item = CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor', currency='usd')

        cart.status = order_status
        cart.save()
        item.status = item_status
        item.save()

        with self.assertRaises(exception):
            cart.retire()
Exemple #55
0
    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

        # need to prevent analytics errors from appearing in stderr
        with patch('sys.stderr', sys.stdout.write):
            CourseEnrollment.enroll(self.user, self.course_key, 'verified')
            cart = Order.get_cart_for_user(user=self.user)
            CertificateItem.add_to_order(cart, self.course_key, self.cost, 'verified')
            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].refund_requested_time)
        self.assertEquals(target_certs[0].order.status, 'refunded')
        self._assert_refund_tracked()
Exemple #56
0
    def test_show_receipt_404s(self):
        PaidCourseRegistration.add_to_order(self.cart, self.course_key)
        CertificateItem.add_to_order(self.cart, self.verified_course_key, self.cost, 'honor')
        self.cart.purchase()

        user2 = UserFactory.create()
        cart2 = Order.get_cart_for_user(user2)
        PaidCourseRegistration.add_to_order(cart2, self.course_key)
        cart2.purchase()

        self.login_user()
        resp = self.client.get(reverse('shoppingcart.views.show_receipt', args=[cart2.id]))
        self.assertEqual(resp.status_code, 404)

        resp2 = self.client.get(reverse('shoppingcart.views.show_receipt', args=[1000]))
        self.assertEqual(resp2.status_code, 404)
Exemple #57
0
    def test_no_refund_on_cert_callback(self):
        # If we explicitly skip refunds, the unenroll action should not modify the purchase.
        CourseEnrollment.enroll(self.user, self.course_key, 'verified')
        cart = Order.get_cart_for_user(user=self.user)
        CertificateItem.add_to_order(cart, self.course_key, self.cost,
                                     'verified')
        cart.purchase()

        CourseEnrollment.unenroll(self.user, self.course_key, skip_refund=True)
        target_certs = CertificateItem.objects.filter(
            course_id=self.course_key,
            user_id=self.user,
            status='purchased',
            mode='verified')
        self.assertTrue(target_certs[0])
        self.assertFalse(target_certs[0].refund_requested_time)
        self.assertEqual(target_certs[0].order.status, 'purchased')
Exemple #58
0
    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)
Exemple #59
0
 def setUp(self):
     patcher = patch('student.models.tracker')
     self.mock_tracker = patcher.start()
     self.user = UserFactory.create()
     self.user.set_password('password')
     self.user.save()
     self.cost = 40
     self.course = CourseFactory.create(org='MITx', number='999', display_name='Robot Super Course')
     self.course_key = self.course.id
     self.course_mode = CourseMode(course_id=self.course_key,
                                   mode_slug="honor",
                                   mode_display_name="honor cert",
                                   min_price=self.cost)
     self.course_mode.save()
     verified_course = CourseFactory.create(org='org', number='test', display_name='Test Course')
     self.verified_course_key = verified_course.id
     self.cart = Order.get_cart_for_user(self.user)
     self.addCleanup(patcher.stop)
Exemple #60
0
    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
        # CertificateItem, which is not quite good unit test form. Sorry.
        cart = Order.get_cart_for_user(user=self.user)
        self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course_key))
        item = CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor')
        # course enrollment object should be created but still inactive
        self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course_key))
        cart.purchase()
        self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course_key))

        # test e-mail sending
        self.assertEquals(len(mail.outbox), 1)
        self.assertEquals('Order Payment Confirmation', mail.outbox[0].subject)
        self.assertIn(settings.PAYMENT_SUPPORT_EMAIL, mail.outbox[0].body)
        self.assertIn(unicode(cart.total_cost), mail.outbox[0].body)
        self.assertIn(item.additional_instruction_text, mail.outbox[0].body)