Beispiel #1
0
    def setUp(self):
        super(EdxOrderPlacementMixinTests, self).setUp()
        self.user = UserFactory(lms_user_id=61710)
        self.order = self.create_order(status=ORDER.OPEN)

        # Ensure that the basket attribute type exists for these tests
        self.basket_attribute_type, _ = BasketAttributeType.objects.get_or_create(
            name=EMAIL_OPT_IN_ATTRIBUTE)
Beispiel #2
0
    def test_generate_coupon_report(self):
        """ Verify the coupon report is generated properly. """
        self.setup_coupons_for_report()
        client = UserFactory()
        basket = Basket.get_basket(client, self.site)
        basket.add_product(self.coupon)

        vouchers = self.coupon_vouchers.first().vouchers.all()
        self.use_voucher('TESTORDER1', vouchers[1], self.user)

        user2 = UserFactory()
        self.use_voucher('TESTORDER2', vouchers[2], self.user)
        self.use_voucher('TESTORDER3', vouchers[2], user2)

        self.mock_course_api_response(course=self.course)
        field_names, rows = generate_coupon_report(self.coupon_vouchers)

        self.assertEqual(field_names, [
            'Code',
            'Coupon Name',
            'Maximum Coupon Usage',
            'Redemption Count',
            'Coupon Type',
            'URL',
            'Course ID',
            'Organization',
            'Client',
            'Category',
            'Note',
            'Price',
            'Invoiced Amount',
            'Discount Percentage',
            'Discount Amount',
            'Status',
            'Order Number',
            'Redeemed By Username',
            'Create Date',
            'Coupon Start Date',
            'Coupon Expiry Date',
            'Email Domains',
        ])

        voucher = Voucher.objects.get(name=rows[0]['Coupon Name'])
        self.assert_report_first_row(rows.pop(0), self.coupon, voucher)

        for row in rows:
            voucher = Voucher.objects.get(code=row['Code'])
            self.assert_report_row(row, voucher)

        self.assertNotIn('Catalog Query', field_names)
        self.assertNotIn('Course Seat Types', field_names)
        self.assertNotIn('Redeemed For Course ID', field_names)
Beispiel #3
0
    def setUp(self):
        super(EnrollmentFulfillmentModuleTests, self).setUp()

        self.user = UserFactory()
        self.user.tracking_context = {
            'ga_client_id': 'test-client-id', 'lms_user_id': 'test-user-id', 'lms_ip': '127.0.0.1'
        }
        self.user.save()
        self.course = CourseFactory(id=self.course_id, name='Demo Course', partner=self.partner)

        self.seat = self.course.create_or_update_seat(self.certificate_type, False, 100, self.provider)

        basket = factories.BasketFactory(owner=self.user, site=self.site)
        basket.add_product(self.seat, 1)
        self.order = create_order(number=1, basket=basket, user=self.user)
    def test_notify_purchaser_course_entielement(self, mock_task):
        """ Verify the notification is scheduled if the site has notifications enabled
        and the refund is for a course entitlement.
        """
        site_configuration = self.site.siteconfiguration
        site_configuration.send_refund_notifications = True

        user = UserFactory()

        course_entitlement = create_or_update_course_entitlement(
            'verified', 100, self.partner, '111-222-333-444', 'Course Entitlement')
        basket = create_basket(site=self.site, owner=user, empty=True)
        basket.add_product(course_entitlement, 1)

        order = create_order(number=1, basket=basket, user=user)
        order_url = get_receipt_page_url(site_configuration, order.number)

        refund = Refund.create_with_lines(order, order.lines.all())

        with LogCapture(REFUND_MODEL_LOGGER_NAME) as logger:
            refund._notify_purchaser()  # pylint: disable=protected-access

        msg = 'Course refund notification scheduled for Refund [{}].'.format(refund.id)
        logger.check_present(
            (REFUND_MODEL_LOGGER_NAME, 'INFO', msg)
        )

        amount = format_currency(order.currency, 100)
        mock_task.assert_called_once_with(
            user.email, refund.id, amount, course_entitlement.title, order.number,
            order_url, site_code=self.partner.short_code
        )
Beispiel #5
0
    def create_order_with_billing_address(self):
        """ Creates an order object with a bit of extra information for HubSpot unit tests"""
        enrollment_code = Product.objects.get(
            product_class__name=ENROLLMENT_CODE_PRODUCT_CLASS_NAME)
        user = UserFactory()
        basket = factories.BasketFactory(owner=user, site=self.site)
        basket.add_product(enrollment_code, self.QUANTITY)

        # add organization and purchaser attributes manually to the basket for testing purposes
        basket_data = {
            'organization': 'Dummy Business Client',
            PURCHASER_BEHALF_ATTRIBUTE: 'True'
        }
        basket_add_organization_attribute(basket, basket_data)

        # add some additional data the billing address to exercise some of the code paths in the unit we are testing
        billing_address = factories.BillingAddressFactory()
        billing_address.line2 = 'Suite 321'
        billing_address.line4 = "City"
        billing_address.state = "State"
        billing_address.country.name = "United States of America"

        # create new order adding in the additional billing address info
        return create_order(number=2,
                            basket=basket,
                            user=user,
                            billing_address=billing_address)
    def test_is_single_use_range_condition_satisfied(self):
        """
        Verify that the condition for a single use coupon is only satisfied by single-product baskets.
        """
        valid_user_email = 'valid@{domain}'.format(domain=self.valid_sub_domain)
        basket = factories.BasketFactory(site=self.site, owner=UserFactory(email=valid_user_email))
        product1 = self.create_entitlement_product()
        product2 = self.create_entitlement_product()

        _range = factories.RangeFactory()
        _range.course_seat_types = ','.join(Range.ALLOWED_SEAT_TYPES)
        _range.catalog_query = 'uuid:*'
        benefit = factories.BenefitFactory(range=_range)
        offer = factories.ConditionalOfferFactory(benefit=benefit)
        offer.set_voucher(
            factories.VoucherFactory(usage='Single-use')
        )
        self.mock_access_token_response()
        self.mock_catalog_query_contains_endpoint(
            course_run_ids=[], course_uuids=[product1.attr.UUID, product2.attr.UUID], absent_ids=[],
            query=benefit.range.catalog_query, discovery_api_url=self.site_configuration.discovery_api_url
        )

        # Verify that each product individually satisfies the condition
        basket.add_product(product1)
        self.assertTrue(offer.is_condition_satisfied(basket))

        basket.flush()
        basket.add_product(product2)
        self.assertTrue(offer.is_condition_satisfied(basket))

        # Verify that the offer cannot be applied to a multi-product basket
        basket.add_product(product1)
        self.assertFalse(offer.is_condition_satisfied(basket))
Beispiel #7
0
    def test_is_satisfied_program_without_entitlements(self):
        """
        User entitlements should not be retrieved if no course in the program has a course entitlement product
        """
        offer = factories.ProgramOfferFactory(partner=self.partner, condition=self.condition)
        basket = BasketFactory(site=self.site, owner=UserFactory())
        program = self.mock_program_detail_endpoint(
            self.condition.program_uuid, self.site_configuration.discovery_api_url, include_entitlements=False
        )
        enrollments = [{'mode': 'verified', 'course_details': {'course_id': 'course-v1:test-org+course+1'}},
                       {'mode': 'verified', 'course_details': {'course_id': 'course-v1:test-org+course+2'}}]
        entitlements_response = {
            "count": 0, "num_pages": 1, "current_page": 1, "results": [
                {'mode': 'verified', 'course_uuid': '268afbfc-cc1e-415b-a5d8-c58d955bcfc3'},
                {'mode': 'verified', 'course_uuid': '268afbfc-cc1e-415b-a5d8-c58d955bcfc4'}
            ], "next": None, "start": 0, "previous": None
        }
        self.mock_user_data(basket.owner.username, owned_products=enrollments)
        self.mock_user_data(basket.owner.username, mocked_api='entitlements', owned_products=entitlements_response)

        for course in program['courses'][2:len(program['courses']) - 1]:
            course_run = Course.objects.get(id=course['course_runs'][0]['key'])
            for seat in course_run.seat_products:
                if seat.attr.id_verification_required:
                    basket.add_product(seat)

        with mock.patch('ecommerce.programs.conditions.deprecated_traverse_pagination') as mock_processing_entitlements:
            self.assertFalse(self.condition.is_satisfied(offer, basket))
            mock_processing_entitlements.assert_not_called()
Beispiel #8
0
 def test_is_satisfied_program_retrieval_failure(self):
     """ The method should return False if no program is retrieved """
     offer = factories.ProgramOfferFactory(partner=self.partner, condition=self.condition)
     basket = BasketFactory(site=self.site, owner=UserFactory())
     basket.add_product(self.test_product)
     self.condition.program_uuid = None
     self.assertFalse(self.condition.is_satisfied(offer, basket))
Beispiel #9
0
    def test_success(self):
        product_price = 100
        percentage_discount = 10
        product = ProductFactory(stockrecords__price_excl_tax=product_price)
        voucher, product = prepare_voucher(
            _range=RangeFactory(products=[product]),
            benefit_value=percentage_discount)
        self.request.user = UserFactory()
        basket = prepare_basket(self.request, [product], voucher)

        ppr = PaymentProcessorResponse.objects.create(basket=basket,
                                                      transaction_id='abc',
                                                      processor_name='paypal')
        with mock.patch.object(Paypal, 'issue_credit') as mock_issue_credit:
            mock_issue_credit.return_value = None

            assert refund_basket_transactions(self.site, [basket.id]) == (
                1,
                0,
            )
            total = product_price * (100 - percentage_discount) / 100.
            mock_issue_credit.assert_called_once_with(basket.order_number,
                                                      basket,
                                                      ppr.transaction_id,
                                                      total, basket.currency)
    def test_is_satisfied_when_owner_has_no_assignment(self, mock_request):
        """
        Ensure that condition returns expected result the basket owner has no assignments.

        # voucher has free slots(3) available, no offer assignment for basket owner,
        # assignments(2) exist for other users, voucher has some redemptions(num_orders = 2)
        # basket owner is allowed to redeem the voucher
        """
        mock_request.return_value = self.request
        code = 'TA7WCQD3T4C7GHZ4'
        num_orders = 2
        max_global_applications = 7

        enterprise_offer = factories.EnterpriseOfferFactory(
            max_global_applications=max_global_applications)
        voucher = factories.VoucherFactory(usage=Voucher.MULTI_USE,
                                           code=code,
                                           num_orders=num_orders)
        voucher.offers.add(enterprise_offer)

        factories.OfferAssignmentFactory(offer=enterprise_offer,
                                         code=code,
                                         user_email='*****@*****.**')
        factories.OfferAssignmentFactory(offer=enterprise_offer,
                                         code=code,
                                         user_email='*****@*****.**')

        basket = BasketFactory(site=self.site,
                               owner=UserFactory(email='*****@*****.**'))
        basket.vouchers.add(voucher)

        assert self.condition.is_satisfied(enterprise_offer, basket) is True
    def test_is_satisfied_with_different_users(self, mock_request):
        """
        Ensure that condition returns expected result when wrong user is try to redeem the voucher.

        # code = 'ASD' assigned_to = '*****@*****.**'
        # code = 'ZXC' assigned_to = '*****@*****.**'
        # [email protected] try to redeem `ASD` code
        # `is_satisfied` should return False
        """
        mock_request.return_value = self.request

        voucher1 = factories.VoucherFactory(usage=Voucher.SINGLE_USE,
                                            code='ASD')
        voucher2 = factories.VoucherFactory(usage=Voucher.SINGLE_USE,
                                            code='ZXC')

        enterprise_offers = factories.EnterpriseOfferFactory.create_batch(2)
        voucher1.offers.add(enterprise_offers[0])
        voucher2.offers.add(enterprise_offers[1])

        basket = BasketFactory(site=self.site,
                               owner=UserFactory(email='*****@*****.**'))
        basket.vouchers.add(voucher1)

        factories.OfferAssignmentFactory(offer=enterprise_offers[0],
                                         code=voucher1.code,
                                         user_email='*****@*****.**')
        factories.OfferAssignmentFactory(offer=enterprise_offers[1],
                                         code=voucher2.code,
                                         user_email='*****@*****.**')

        assert self.condition.is_satisfied(enterprise_offers[1],
                                           basket) is False
Beispiel #12
0
 def _create_user(self):
     """ Create a new user. """
     user = UserFactory(username='******', password=self.PASSWORD)
     UserSocialAuth.objects.create(user=user,
                                   provider='edx-oauth2',
                                   uid=user.username)
     return user
Beispiel #13
0
    def test_handle_post_order_for_bulk_purchase(self, __):
        """
        Ensure that the bulk purchase order is linked to the provided business
        client when the method `handle_post_order` is invoked.
        """
        toggle_switch(ENROLLMENT_CODE_SWITCH, True)

        course = CourseFactory(partner=self.partner)
        course.create_or_update_seat('verified',
                                     True,
                                     50,
                                     create_enrollment_code=True)
        enrollment_code = Product.objects.get(
            product_class__name=ENROLLMENT_CODE_PRODUCT_CLASS_NAME)
        user = UserFactory()
        basket = BasketFactory(owner=user, site=self.site)
        basket.add_product(enrollment_code, quantity=1)
        order = create_order(number=1, basket=basket, user=user)
        request_data = {
            'organization': 'Dummy Business Client',
            PURCHASER_BEHALF_ATTRIBUTE: 'False',
        }
        # Manually add organization and purchaser attributes on the basket for testing
        basket_add_organization_attribute(basket, request_data)

        EdxOrderPlacementMixin().handle_post_order(order)

        # Now verify that a new business client has been created in current
        # order is now linked with that client through Invoice model.
        business_client = BusinessClient.objects.get(
            name=request_data['organization'])
        assert Invoice.objects.get(
            order=order).business_client == business_client
    def assert_condition(self, voucher_type, assignments,
                         expected_condition_result):
        """
        Verify that condition works as expected for different vouchers and assignments.
        """
        for assignment in assignments:
            code = assignment['code']
            email = assignment['user_email']
            # In some cases individual assignments have their own expected result
            expected_condition_result = assignment.get(
                'result', expected_condition_result)

            voucher = Voucher.objects.get(usage=voucher_type, code=code)
            basket = BasketFactory(site=self.site,
                                   owner=UserFactory(email=email))
            basket.vouchers.add(voucher)

            is_condition_satisfied = self.condition.is_satisfied(
                voucher.enterprise_offer, basket)
            assert is_condition_satisfied == expected_condition_result

            # update the `num_orders` so that we can also verify the redemptions check
            # also update the offer assignment status
            if expected_condition_result:
                voucher.num_orders += 1
                voucher.save()
                assignment = OfferAssignment.objects.filter(
                    offer=voucher.enterprise_offer,
                    code=code,
                    user_email=email).exclude(
                        status__in=[OFFER_REDEEMED, OFFER_ASSIGNMENT_REVOKED
                                    ]).first()
                if assignment:
                    assignment.status = OFFER_REDEEMED
                    assignment.save()
Beispiel #15
0
    def test_zero_dollar_refund(self, mock_revoke_line):
        """
        Given an order and order lines which total $0 and are not refunded, Refund.create_with_lines
        should create and approve a Refund with corresponding RefundLines.
        """
        httpretty.register_uri(httpretty.POST,
                               get_lms_enrollment_api_url(),
                               status=200,
                               body='{}',
                               content_type='application/json')

        order = self.create_order(user=UserFactory(), free=True)

        # Verify that the order totals $0.
        self.assertEqual(order.total_excl_tax, 0)

        refund = Refund.create_with_lines(order, list(order.lines.all()))

        # Verify that refund lines are not revoked.
        self.assertFalse(mock_revoke_line.called)

        # Verify that the refund has been successfully approved.
        self.assertEqual(refund.status, REFUND.COMPLETE)
        self.assertEqual({line.status
                          for line in refund.lines.all()},
                         {REFUND_LINE.COMPLETE})
Beispiel #16
0
    def test_handle_post_order_for_seat_purchase(self, __):
        """
        Ensure that the single seat purchase order is not linked any business
        client when the method `handle_post_order` is invoked.
        """
        toggle_switch(ENROLLMENT_CODE_SWITCH, False)

        course = CourseFactory(partner=self.partner)
        verified_product = course.create_or_update_seat('verified', True, 50)
        user = UserFactory()
        basket = BasketFactory(owner=user, site=self.site)
        basket.add_product(verified_product, quantity=1)
        order = create_order(number=1, basket=basket, user=user)
        request_data = {
            'organization': 'Dummy Business Client',
            PURCHASER_BEHALF_ATTRIBUTE: 'False',
        }
        # Manually add organization and purchaser attributes on the basket for testing
        basket_add_organization_attribute(basket, request_data)

        EdxOrderPlacementMixin().handle_post_order(order)

        # Now verify that the single seat order is not linked to business
        # client by checking that there is no record for BusinessClient.
        assert not BusinessClient.objects.all()
Beispiel #17
0
    def test_is_range_condition_satisfied(self):
        """
        Verify that a basket satisfies a condition only when all of its products are in its range's catalog queryset.
        """
        valid_user_email = 'valid@{domain}'.format(
            domain=self.valid_sub_domain)
        basket = factories.BasketFactory(
            site=self.site, owner=UserFactory(email=valid_user_email))
        product = self.create_entitlement_product()
        another_product = self.create_entitlement_product()

        _range = factories.RangeFactory()
        _range.course_seat_types = ','.join(Range.ALLOWED_SEAT_TYPES)
        _range.catalog_query = 'uuid:{course_uuid}'.format(
            course_uuid=product.attr.UUID)
        benefit = factories.BenefitFactory(range=_range)
        offer = factories.ConditionalOfferFactory(benefit=benefit)
        self.mock_access_token_response()
        self.mock_catalog_query_contains_endpoint(
            course_run_ids=[],
            course_uuids=[product.attr.UUID],
            absent_ids=[another_product.attr.UUID],
            query=benefit.range.catalog_query,
            discovery_api_url=self.site_configuration.discovery_api_url)

        basket.add_product(product)
        self.assertTrue(offer.is_condition_satisfied(basket))

        basket.add_product(another_product)
        self.assertFalse(offer.is_condition_satisfied(basket))

        # Verify that API return values are cached
        httpretty.disable()
        self.assertFalse(offer.is_condition_satisfied(basket))
Beispiel #18
0
 def setUp(self):
     super(CouponFulfillmentModuleTest, self).setUp()
     coupon = self.create_coupon()
     user = UserFactory()
     basket = factories.BasketFactory(owner=user, site=self.site)
     basket.add_product(coupon, 1)
     self.order = create_order(number=1, basket=basket, user=user)
Beispiel #19
0
 def setUp(self):
     super(RefundAdminTests, self).setUp()
     self.user = UserFactory(is_staff=True,
                             is_superuser=True,
                             password=self.password)
     self.client.login(username=self.user.username, password=self.password)
     self.role = EcommerceFeatureRole.objects.get(name=ORDER_MANAGER_ROLE)
 def setUp(self):
     super(EnterpriseCustomerConditionTests, self).setUp()
     self.user = UserFactory()
     self.condition = factories.EnterpriseCustomerConditionFactory()
     self.test_product = ProductFactory(stockrecords__price_excl_tax=10,
                                        categories=[])
     self.course_run = CourseFactory(partner=self.partner)
     self.course_run.create_or_update_seat('verified', True, Decimal(100))
Beispiel #21
0
 def create_user(self, lms_user_id=lms_user_id, **kwargs):
     """Create a user, with overrideable defaults."""
     not_provided = object()
     if kwargs.get('username', not_provided) is None:
         kwargs.pop('username')
     return UserFactory(password=self.password,
                        lms_user_id=lms_user_id,
                        **kwargs)
Beispiel #22
0
 def test_is_satisfied_site_mismatch(self):
     """ Ensure the condition returns False if the offer partner does not match the basket site partner. """
     offer = factories.ProgramOfferFactory(
         partner=SiteConfigurationFactory().partner,
         condition=self.condition)
     basket = BasketFactory(site=self.site, owner=UserFactory())
     basket.add_product(self.test_product)
     self.assertFalse(self.condition.is_satisfied(offer, basket))
Beispiel #23
0
 def test_is_satisfied_free_basket(self):
     """ Ensure the basket returns False if the basket total is zero. """
     offer = factories.ProgramOfferFactory(partner=self.partner, condition=self.condition)
     basket = BasketFactory(site=self.site, owner=UserFactory())
     test_product = factories.ProductFactory(stockrecords__price_excl_tax=0,
                                             stockrecords__partner__short_code='test')
     basket.add_product(test_product)
     self.assertFalse(self.condition.is_satisfied(offer, basket))
Beispiel #24
0
 def test_create_voucher_with_multi_use_per_customer_usage(self):
     """ Verify voucher is created with `MULTI_USE_PER_CUSTOMER` usage type. """
     voucher_data = dict(self.data, usage=Voucher.MULTI_USE_PER_CUSTOMER)
     voucher = Voucher.objects.create(**voucher_data)
     user = UserFactory()
     self.assertEqual(voucher.usage, Voucher.MULTI_USE_PER_CUSTOMER)
     is_available, message = voucher.is_available_to_user(user)
     self.assertTrue(is_available)
     self.assertEqual(message, '')
Beispiel #25
0
 def setUp(self):
     super(EnrollmentCodeFulfillmentModuleTests, self).setUp()
     course = CourseFactory(partner=self.partner)
     course.create_or_update_seat('verified', True, 50, create_enrollment_code=True)
     enrollment_code = Product.objects.get(product_class__name=ENROLLMENT_CODE_PRODUCT_CLASS_NAME)
     user = UserFactory()
     basket = factories.BasketFactory(owner=user, site=self.site)
     basket.add_product(enrollment_code, self.QUANTITY)
     self.order = create_order(number=1, basket=basket, user=user)
Beispiel #26
0
    def setUp(self):
        super(BenefitTests, self).setUp()

        _range = factories.RangeFactory(course_seat_types=','.join(
            Range.ALLOWED_SEAT_TYPES[1:]),
                                        catalog_query='uuid:*')
        self.benefit = factories.BenefitFactory(range=_range)
        self.offer = factories.ConditionalOfferFactory(benefit=self.benefit)
        self.user = UserFactory()
Beispiel #27
0
    def test_is_satisfied_with_exception_for_programs(self, value):
        """ The method should return False if there is an exception when trying to get program details. """
        offer = factories.ProgramOfferFactory(partner=self.partner, condition=self.condition)
        basket = BasketFactory(site=self.site, owner=UserFactory())
        basket.add_product(self.test_product)

        with mock.patch('ecommerce.programs.conditions.get_program',
                        side_effect=value):
            self.assertFalse(self.condition.is_satisfied(offer, basket))
Beispiel #28
0
    def setUp(self):
        super(JournalBundleConditionTests, self).setUp()
        user = self.create_user(is_staff=True)
        self.client.login(username=user.username, password=self.password)

        self.condition = factories.JournalConditionFactory()
        self.offer = factories.JournalBundleOfferFactory(
            partner=self.partner, condition=self.condition)
        self.basket = BasketFactory(site=self.site, owner=UserFactory())
        self.basket.add_product(self.create_product(self.client), 1)
    def test_get_transaction_parameters_with_quoted_product_title(self):
        """ Verify quotes are removed from item name """
        course = CourseFactory(id='a/b/c/d', name='Course with "quotes"')
        product = course.create_or_update_seat(self.CERTIFICATE_TYPE, False, 20)

        basket = create_basket(owner=UserFactory(), site=self.site, empty=True)
        basket.add_product(product)

        response = self.processor.get_transaction_parameters(basket)
        self.assertEqual(response['item_0_name'], 'Seat in Course with quotes with test-certificate-type certificate')
Beispiel #30
0
    def request_specific_voucher_report(self, coupon):
        client = UserFactory()
        basket = Basket.get_basket(client, self.site)
        basket.add_product(coupon)

        request = RequestFactory()
        response = CouponReportCSVView().get(request, coupon_id=coupon.id)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.content.splitlines()), 7)