Example #1
0
def calculate_run_price(course_run, user):
    """
    Calculate the price of a course given the coupons and financial aid available to the user.

    Args:
        course_run (CourseRun): A course run
        user (django.contrib.auth.models.User): A user

    Returns:
        (decimal.Decimal, Coupon):
            The adjusted of the course, and the coupon used if any
    """
    program = course_run.course.program
    enrollment = get_object_or_404(ProgramEnrollment, program=program, user=user)
    price = get_formatted_course_price(enrollment)['price']
    coupons = [coupon for coupon in pick_coupons(user) if coupon.program == program]

    if not coupons:
        # There is no coupon for this program
        return price, None
    coupon = coupons[0]

    if course_run.edx_course_key not in coupon.course_keys:
        # coupon does not apply to this particular course run
        return price, None

    price = calculate_coupon_price(coupon, price, course_run.edx_course_key)
    return price, coupon
Example #2
0
 def get(self, request, *args, **kwargs):
     """
     GET handler
     """
     user = request.user
     program_enrollment = get_object_or_404(
         ProgramEnrollment,
         user=user,
         program__id=self.kwargs["program_id"],
         program__live=True)
     serializer = FormattedCoursePriceSerializer(
         get_formatted_course_price(program_enrollment))
     return Response(data=serializer.data)
Example #3
0
 def test_calculate_run_price_coupon(self):
     """
     If there is a coupon calculate_run_price should use calculate_coupon_price to get the discounted price
     """
     course_run, user = create_purchasable_course_run()
     coupon = CouponFactory.create(content_object=course_run.course)
     UserCoupon.objects.create(coupon=coupon, user=user)
     discounted_price = 5
     with patch('ecommerce.api.calculate_coupon_price', autospec=True) as _calculate_coupon_price:
         _calculate_coupon_price.return_value = discounted_price
         assert calculate_run_price(course_run, user) == (discounted_price, coupon)
     program_enrollment = course_run.course.program.programenrollment_set.first()
     fa_price = get_formatted_course_price(program_enrollment)['price']
     _calculate_coupon_price.assert_called_with(coupon, fa_price, course_run.edx_course_key)
Example #4
0
 def test_get_all_course_prices(self):
     """
     Test that the course_price_list route will return a list of formatted course prices
     which should only include entries from the program the user is enrolled in.
     """
     resp = self.make_http_request(self.client.get, self.course_price_url, status.HTTP_200_OK)
     expected = sorted([
         get_formatted_course_price(enrollment)
         for enrollment in ProgramEnrollment.objects.filter(user=self.profile.user)
     ], key=lambda x: x['program_id'])
     actual = sorted([
         decimalize_price(fcp) for fcp in resp.data
     ], key=lambda x: x['program_id'])
     assert actual == expected
Example #5
0
 def test_calculate_run_price_coupon(self):
     """
     If there is a coupon calculate_run_price should use calculate_coupon_price to get the discounted price
     """
     course_run, user = create_purchasable_course_run()
     coupon = CouponFactory.create(content_object=course_run.course)
     UserCoupon.objects.create(coupon=coupon, user=user)
     discounted_price = 5
     with patch('ecommerce.api.calculate_coupon_price', autospec=True) as _calculate_coupon_price:
         _calculate_coupon_price.return_value = discounted_price
         assert calculate_run_price(course_run, user) == (discounted_price, coupon)
     program_enrollment = course_run.course.program.programenrollment_set.first()
     fa_price = get_formatted_course_price(program_enrollment)['price']
     _calculate_coupon_price.assert_called_with(coupon, fa_price, course_run.edx_course_key)
Example #6
0
 def test_staff_can_get_other_user(self):
     """
     A staff user should be able to get the data for a learner enrolled in their program
     """
     self.client.force_login(self.staff_user_profile.user)
     resp = self.make_http_request(self.client.get, self.course_price_url, status.HTTP_200_OK)
     expected = sorted([
         get_formatted_course_price(enrollment)
         for enrollment in ProgramEnrollment.objects.filter(user=self.profile.user)
     ], key=lambda x: x['program_id'])
     actual = sorted([
         decimalize_price(fcp) for fcp in resp.data
     ], key=lambda x: x['program_id'])
     assert actual == expected
Example #7
0
 def test_calculate_run_price_no_coupons(self):
     """
     If there are no coupons for this program the price should be what get_formatted_course_price returned
     """
     course_run, user = create_purchasable_course_run()
     # This coupon is for a different program
     coupon = CouponFactory.create()
     UserCoupon.objects.create(coupon=coupon, user=user)
     discounted_price = 5
     program_enrollment = course_run.course.program.programenrollment_set.first()
     fa_price = get_formatted_course_price(program_enrollment)['price']
     with patch('ecommerce.api.calculate_coupon_price', autospec=True) as _calculate_coupon_price:
         _calculate_coupon_price.return_value = discounted_price
         assert calculate_run_price(course_run, user) == (fa_price, None)
     assert _calculate_coupon_price.called is False
Example #8
0
 def test_calculate_run_price_no_coupons(self):
     """
     If there are no coupons for this program the price should be what get_formatted_course_price returned
     """
     course_run, user = create_purchasable_course_run()
     # This coupon is for a different program
     coupon = CouponFactory.create()
     UserCoupon.objects.create(coupon=coupon, user=user)
     discounted_price = 5
     program_enrollment = course_run.course.program.programenrollment_set.first()
     fa_price = get_formatted_course_price(program_enrollment)['price']
     with patch('ecommerce.api.calculate_coupon_price', autospec=True) as _calculate_coupon_price:
         _calculate_coupon_price.return_value = discounted_price
         assert calculate_run_price(course_run, user) == (fa_price, None)
     assert _calculate_coupon_price.called is False
Example #9
0
 def test_calculate_run_price_other_run(self):
     """
     If the coupon is for another course in this program it should not be returned here
     """
     course_run, user = create_purchasable_course_run()
     other_course = CourseRunFactory.create(course__program=course_run.course.program).course
     coupon = CouponFactory.create(content_object=other_course)
     UserCoupon.objects.create(coupon=coupon, user=user)
     discounted_price = 5
     program_enrollment = course_run.course.program.programenrollment_set.first()
     fa_price = get_formatted_course_price(program_enrollment)['price']
     with patch('ecommerce.api.calculate_coupon_price', autospec=True) as _calculate_coupon_price:
         _calculate_coupon_price.return_value = discounted_price
         assert calculate_run_price(course_run, user) == (fa_price, None)
     assert _calculate_coupon_price.called is False
Example #10
0
 def test_calculate_run_price_other_run(self):
     """
     If the coupon is for another course in this program it should not be returned here
     """
     course_run, user = create_purchasable_course_run()
     other_course = CourseRunFactory.create(course__program=course_run.course.program).course
     coupon = CouponFactory.create(content_object=other_course)
     UserCoupon.objects.create(coupon=coupon, user=user)
     discounted_price = 5
     program_enrollment = course_run.course.program.programenrollment_set.first()
     fa_price = get_formatted_course_price(program_enrollment)['price']
     with patch('ecommerce.api.calculate_coupon_price', autospec=True) as _calculate_coupon_price:
         _calculate_coupon_price.return_value = discounted_price
         assert calculate_run_price(course_run, user) == (fa_price, None)
     assert _calculate_coupon_price.called is False
Example #11
0
 def test_generate_financial_aid_email_approved(self):
     """
     Tests generate_financial_aid_email() with status APPROVED
     """
     self.financial_aid.status = FinancialAidStatus.APPROVED
     self.financial_aid.save()
     email_dict = generate_financial_aid_email(self.financial_aid)
     assert email_dict["subject"] == FINANCIAL_AID_APPROVAL_SUBJECT.format(
         program_name=self.financial_aid.tier_program.program.title)
     assert email_dict["body"] == FINANCIAL_AID_EMAIL_BODY.format(
         first_name=self.financial_aid.user.profile.first_name,
         message=FINANCIAL_AID_APPROVAL_MESSAGE.format(
             program_name=self.financial_aid.tier_program.program.title,
             price=get_formatted_course_price(
                 self.program_enrollment)["price"]),
         program_name=self.financial_aid.tier_program.program.title)
Example #12
0
 def test_get_course_price_for_learner_with_no_financial_aid_request(self):
     """
     Tests get_course_price_for_learner() who has no financial aid request
     """
     enrollment = ProgramEnrollment.objects.get(program=self.program,
                                                user=self.profile.user)
     # Enrolled and has no financial aid
     course_price = self.program.price
     expected_response = {
         "program_id": enrollment.program.id,
         "price": course_price,
         "financial_aid_availability": True,
         "has_financial_aid_request": False
     }
     self.assertDictEqual(get_formatted_course_price(enrollment),
                          expected_response)
Example #13
0
 def test_get_course_price_for_learner_in_no_financial_aid_program(self):
     """
     Tests get_course_price_for_learner() for a program without financial aid
     """
     enrollment = ProgramEnrollment.objects.get(program=self.program,
                                                user=self.profile.user)
     self.program.financial_aid_availability = False
     self.program.save()
     course_price = self.program.price
     expected_response = {
         "program_id": enrollment.program.id,
         "price": course_price,
         "financial_aid_availability": False,
         "has_financial_aid_request": False
     }
     self.assertDictEqual(get_formatted_course_price(enrollment),
                          expected_response)
Example #14
0
    def get(self, request, username, *args, **kwargs):
        """
        GET handler
        """
        user = get_object_or_404(User,
                                 social_auth__uid=username,
                                 social_auth__provider=EdxOrgOAuth2.name)

        program_enrollments = (ProgramEnrollment.objects.select_related(
            'user', 'program').filter(user=user, program__live=True).all())
        formatted_course_prices = [
            get_formatted_course_price(program_enrollment)
            for program_enrollment in program_enrollments
        ]
        serializer = FormattedCoursePriceSerializer(formatted_course_prices,
                                                    many=True)
        return Response(data=serializer.data)
Example #15
0
 def test_get_course_price_for_learner_with_no_financial_aid_request(self):
     """
     Tests get_course_price_for_learner() who has no financial aid request
     """
     enrollment = ProgramEnrollment.objects.get(program=self.program, user=self.profile.user)
     # Enrolled and has no financial aid
     course_price = self.program.price
     expected_response = {
         "program_id": enrollment.program.id,
         "price": course_price,
         "financial_aid_availability": True,
         "has_financial_aid_request": False
     }
     self.assertDictEqual(
         get_formatted_course_price(enrollment),
         expected_response
     )
Example #16
0
 def test_get_course_price_for_learner_in_no_financial_aid_program(self):
     """
     Tests get_course_price_for_learner() for a program without financial aid
     """
     enrollment = ProgramEnrollment.objects.get(program=self.program, user=self.profile.user)
     self.program.financial_aid_availability = False
     self.program.save()
     course_price = self.program.price
     expected_response = {
         "program_id": enrollment.program.id,
         "price": course_price,
         "financial_aid_availability": False,
         "has_financial_aid_request": False
     }
     self.assertDictEqual(
         get_formatted_course_price(enrollment),
         expected_response
     )
Example #17
0
 def test_generate_financial_aid_email_approved(self):
     """
     Tests generate_financial_aid_email() with status APPROVED
     """
     self.financial_aid.status = FinancialAidStatus.APPROVED
     self.financial_aid.save()
     email_dict = generate_financial_aid_email(self.financial_aid)
     assert email_dict["subject"] == FINANCIAL_AID_APPROVAL_SUBJECT.format(
         program_name=self.financial_aid.tier_program.program.title
     )
     assert email_dict["body"] == FINANCIAL_AID_EMAIL_BODY.format(
         first_name=self.financial_aid.user.profile.first_name,
         message=FINANCIAL_AID_APPROVAL_MESSAGE.format(
             program_name=self.financial_aid.tier_program.program.title,
             price=get_formatted_course_price(self.program_enrollment)["price"]
         ),
         program_name=self.financial_aid.tier_program.program.title
     )
Example #18
0
 def test_get_course_price_for_learner_with_financial_aid(self, status):
     """
     Tests get_course_price_for_learner() who has approved financial aid
     """
     enrollment = ProgramEnrollment.objects.get(program=self.program,
                                                user=self.profile.user)
     financial_aid = FinancialAidFactory.create(
         user=self.profile.user,
         tier_program=self.tier_programs['25k'],
         status=status,
     )
     course_price = self.program.price
     expected_response = {
         "program_id": enrollment.program.id,
         "price": course_price - financial_aid.tier_program.discount_amount,
         "financial_aid_availability": True,
         "has_financial_aid_request": True
     }
     self.assertDictEqual(get_formatted_course_price(enrollment),
                          expected_response)
Example #19
0
 def test_get_course_price_for_learner_with_financial_aid_in_reset(self):
     """
     Tests get_course_price_for_learner() who has financial aid request in status `reset`
     """
     enrollment = ProgramEnrollment.objects.get(program=self.program,
                                                user=self.profile.user)
     FinancialAidFactory.create(
         user=self.profile.user,
         tier_program=self.tier_programs['25k'],
         status=FinancialAidStatus.RESET,
     )
     # Enrolled and has no financial aid
     course_price = self.program.price
     expected_response = {
         "program_id": enrollment.program.id,
         "price": course_price,
         "financial_aid_availability": True,
         "has_financial_aid_request": False
     }
     self.assertDictEqual(get_formatted_course_price(enrollment),
                          expected_response)
Example #20
0
 def test_get_course_price_for_learner_with_financial_aid(self, status):
     """
     Tests get_course_price_for_learner() who has approved financial aid
     """
     enrollment = ProgramEnrollment.objects.get(program=self.program, user=self.profile.user)
     financial_aid = FinancialAidFactory.create(
         user=self.profile.user,
         tier_program=self.tier_programs['25k'],
         status=status,
     )
     course_price = self.program.price
     expected_response = {
         "program_id": enrollment.program.id,
         "price": course_price - financial_aid.tier_program.discount_amount,
         "financial_aid_availability": True,
         "has_financial_aid_request": True
     }
     self.assertDictEqual(
         get_formatted_course_price(enrollment),
         expected_response
     )
Example #21
0
 def test_get_course_price_for_learner_with_financial_aid_in_reset(self):
     """
     Tests get_course_price_for_learner() who has financial aid request in status `reset`
     """
     enrollment = ProgramEnrollment.objects.get(program=self.program, user=self.profile.user)
     FinancialAidFactory.create(
         user=self.profile.user,
         tier_program=self.tier_programs['25k'],
         status=FinancialAidStatus.RESET,
     )
     # Enrolled and has no financial aid
     course_price = self.program.price
     expected_response = {
         "program_id": enrollment.program.id,
         "price": course_price,
         "financial_aid_availability": True,
         "has_financial_aid_request": False
     }
     self.assertDictEqual(
         get_formatted_course_price(enrollment),
         expected_response
     )
Example #22
0
def generate_financial_aid_email(financial_aid):
    """
    Generates the email subject and body for a FinancialAid status update. Accepted statuses are
    FinancialAidStatus.APPROVED and FinancialAidStatus.PENDING_MANUAL_APPROVAL (documents have been received).
    Args:
        financial_aid (FinancialAid): The FinancialAid object in question
    Returns:
        dict: {"subject": (str), "body": (str)}
    """
    if financial_aid.status == FinancialAidStatus.APPROVED:
        program_enrollment = ProgramEnrollment.objects.get(
            user=financial_aid.user,
            program=financial_aid.tier_program.program
        )
        message = FINANCIAL_AID_APPROVAL_MESSAGE.format(
            program_name=financial_aid.tier_program.program.title,
            price=get_formatted_course_price(program_enrollment)["price"]
        )
        subject = FINANCIAL_AID_APPROVAL_SUBJECT.format(program_name=financial_aid.tier_program.program.title)
    elif financial_aid.status == FinancialAidStatus.PENDING_MANUAL_APPROVAL:
        message = FINANCIAL_AID_DOCUMENTS_RECEIVED_MESSAGE
        subject = FINANCIAL_AID_DOCUMENTS_RECEIVED_SUBJECT.format(
            program_name=financial_aid.tier_program.program.title
        )
    elif financial_aid.status == FinancialAidStatus.RESET:
        message = FINANCIAL_AID_DOCUMENTS_RESET_MESSAGE
        subject = FINANCIAL_AID_RESET_SUBJECT.format(
            program_name=financial_aid.tier_program.program.title
        )
    else:
        # django.core.exceptions.ValidationError
        raise ValidationError("Invalid status on FinancialAid for generate_financial_aid_email()")
    body = FINANCIAL_AID_EMAIL_BODY.format(
        first_name=financial_aid.user.profile.first_name,
        message=message,
        program_name=financial_aid.tier_program.program.title
    )
    return {"subject": subject, "body": body}
Example #23
0
def generate_financial_aid_email(financial_aid):
    """
    Generates the email subject and body for a FinancialAid status update. Accepted statuses are
    FinancialAidStatus.APPROVED and FinancialAidStatus.PENDING_MANUAL_APPROVAL (documents have been received).
    Args:
        financial_aid (FinancialAid): The FinancialAid object in question
    Returns:
        dict: {"subject": (str), "body": (str)}
    """
    if financial_aid.status == FinancialAidStatus.APPROVED:
        program_enrollment = ProgramEnrollment.objects.get(
            user=financial_aid.user,
            program=financial_aid.tier_program.program)
        message = FINANCIAL_AID_APPROVAL_MESSAGE.format(
            program_name=financial_aid.tier_program.program.title,
            price=get_formatted_course_price(program_enrollment)["price"])
        subject = FINANCIAL_AID_APPROVAL_SUBJECT.format(
            program_name=financial_aid.tier_program.program.title)
    elif financial_aid.status == FinancialAidStatus.PENDING_MANUAL_APPROVAL:
        message = FINANCIAL_AID_DOCUMENTS_RECEIVED_MESSAGE
        subject = FINANCIAL_AID_DOCUMENTS_RECEIVED_SUBJECT.format(
            program_name=financial_aid.tier_program.program.title)
    elif financial_aid.status == FinancialAidStatus.RESET:
        message = FINANCIAL_AID_DOCUMENTS_RESET_MESSAGE
        subject = FINANCIAL_AID_RESET_SUBJECT.format(
            program_name=financial_aid.tier_program.program.title)
    else:
        # django.core.exceptions.ValidationError
        raise ValidationError(
            "Invalid status on FinancialAid for generate_financial_aid_email()"
        )
    body = FINANCIAL_AID_EMAIL_BODY.format(
        first_name=financial_aid.user.profile.first_name,
        message=message,
        program_name=financial_aid.tier_program.program.title)
    return {"subject": subject, "body": body}