コード例 #1
0
ファイル: views.py プロジェクト: praneethdodedu/ecommerce
    def get(self, request):
        partner = get_partner_for_site(request)

        skus = request.GET.getlist('sku')
        code = request.GET.get('code', None)

        if not skus:
            return HttpResponseBadRequest(_('No SKUs provided.'))

        products = Product.objects.filter(stockrecords__partner=partner,
                                          stockrecords__partner_sku__in=skus)
        if not products:
            return HttpResponseBadRequest(
                _('Products with SKU(s) [{skus}] do not exist.').format(
                    skus=', '.join(skus)))

        voucher = Voucher.objects.get(code=code) if code else None
        try:
            prepare_basket(request, products, voucher)
        except AlreadyPlacedOrderException:
            return render(
                request, 'edx/error.html',
                {'error': _('You have already purchased these products')})
        messages.add_message(
            request, messages.INFO,
            'Already purchased products will not be added to basket.')
        return HttpResponseRedirect(reverse('basket:summary'), status=303)
コード例 #2
0
    def test_prepare_basket_affiliate_cookie_lifecycle(self):
        """ Verify a basket is returned and referral captured. """
        product = ProductFactory()
        affiliate_id = 'test_affiliate'
        self.request.COOKIES['affiliate_id'] = affiliate_id
        basket = prepare_basket(self.request, product)

        # test affiliate id from cookie saved in referral
        referral = Referral.objects.get(basket_id=basket.id)
        self.assertEqual(referral.affiliate_id, affiliate_id)

        # update cookie
        new_affiliate_id = 'new_affiliate'
        self.request.COOKIES['affiliate_id'] = new_affiliate_id
        basket = prepare_basket(self.request, product)

        # test new affiliate id saved
        referral = Referral.objects.get(basket_id=basket.id)
        self.assertEqual(referral.affiliate_id, new_affiliate_id)

        # expire cookie
        del self.request.COOKIES['affiliate_id']
        basket = prepare_basket(self.request, product)

        # test referral record is deleted when no cookie set
        with self.assertRaises(Referral.DoesNotExist):
            Referral.objects.get(basket_id=basket.id)
コード例 #3
0
    def get(self, request):
        # Send time when this view is called - https://openedx.atlassian.net/browse/REV-984
        properties = {'emitted_at': time.time()}
        track_segment_event(request.site, request.user, 'Basket Add Items View Called', properties)

        try:
            skus = self._get_skus(request)
            products = self._get_products(request, skus)
            voucher = self._get_voucher(request)

            logger.info('Starting payment flow for user [%s] for products [%s].', request.user.username, skus)

            available_products = self._get_available_products(request, products)
            self._set_email_preference_on_basket(request)

            try:
                prepare_basket(request, available_products, voucher)
            except AlreadyPlacedOrderException:
                return render(request, 'edx/error.html', {'error': _('You have already purchased these products')})

            return self._redirect_response_to_basket_or_payment(request)

        except BadRequestException as e:
            return HttpResponseBadRequest(six.text_type(e))
        except RedirectException as e:
            return e.response
コード例 #4
0
ファイル: views.py プロジェクト: digideskio/ecommerce-2
    def get(self, request):
        partner = get_partner_for_site(request)

        sku = request.GET.get('sku', None)
        code = request.GET.get('code', None)

        if not sku:
            return HttpResponseBadRequest(_('No SKU provided.'))

        if code:
            voucher, __ = get_voucher_from_code(code=code)
        else:
            voucher = None

        try:
            product = StockRecord.objects.get(partner=partner, partner_sku=sku).product
        except StockRecord.DoesNotExist:
            return HttpResponseBadRequest(_('SKU [{sku}] does not exist.'.format(sku=sku)))

        purchase_info = request.strategy.fetch_for_product(product)
        if not purchase_info.availability.is_available_to_buy:
            return HttpResponseBadRequest(_('Product [{product}] not available to buy.'.format(product=product.title)))

        prepare_basket(request, product, voucher)
        return HttpResponseRedirect(reverse('basket:summary'), status=303)
コード例 #5
0
ファイル: views.py プロジェクト: mhoonjeon/ecommerce
    def get(self, request):
        try:
            skus = self._get_skus(request)
            products = self._get_products(request, skus)
            voucher = self._get_voucher(request)

            logger.info(
                'Starting payment flow for user [%s] for products [%s].',
                request.user.username, skus)

            self._redirect_for_enterprise_entitlement_if_needed(
                request, voucher, products, skus)
            available_products = self._get_available_products(
                request, products)
            self._set_email_preference_on_basket(request)

            try:
                prepare_basket(request, available_products, voucher)
            except AlreadyPlacedOrderException:
                return render(
                    request, 'edx/error.html',
                    {'error': _('You have already purchased these products')})

            return self._redirect_response_to_basket_or_payment(request)

        except BadRequestException as e:
            return HttpResponseBadRequest(six.text_type(e))
        except RedirectException as e:
            return e.response
コード例 #6
0
ファイル: views.py プロジェクト: iivic/ecommerce
    def get(self, request):
        partner = get_partner_for_site(request)

        skus = request.GET.getlist('sku')
        code = request.GET.get('code', None)

        if not skus:
            return HttpResponseBadRequest(_('No SKUs provided.'))

        products = Product.objects.filter(stockrecords__partner=partner, stockrecords__partner_sku__in=skus)
        if not products:
            return HttpResponseBadRequest(_('Products with SKU(s) [{skus}] do not exist.').format(skus=', '.join(skus)))

        voucher = Voucher.objects.get(code=code) if code else None

        if voucher is None:
            # If there is an Enterprise entitlement available for this basket,
            # we redirect to the CouponRedeemView to apply the discount to the
            # basket and handle the data sharing consent requirement.
            code_redemption_redirect = get_enterprise_code_redemption_redirect(
                request,
                products,
                skus,
                'basket:add-multi'
            )
            if code_redemption_redirect:
                return code_redemption_redirect

        try:
            prepare_basket(request, products, voucher)
        except AlreadyPlacedOrderException:
            return render(request, 'edx/error.html', {'error': _('You have already purchased these products')})
        url = add_utm_params_to_url(reverse('basket:summary'), self.request.GET.items())
        return HttpResponseRedirect(url, status=303)
コード例 #7
0
    def test_prepare_basket_with_enterprise_catalog(self):
        """
        Test `prepare_basket` with enterprise catalog.
        """
        product = ProductFactory()
        request = self.request
        expected_enterprise_catalog_uuid = str(uuid4())
        request.GET = {'catalog': expected_enterprise_catalog_uuid}
        basket = prepare_basket(request, [product])

        # Verify that the enterprise catalog attribute exists for the basket
        # when basket is prepared with the value of provide catalog UUID
        enterprise_catalog_uuid = BasketAttribute.objects.get(
            basket=basket,
            attribute_type__name=ENTERPRISE_CATALOG_ATTRIBUTE_TYPE).value_text
        assert expected_enterprise_catalog_uuid == enterprise_catalog_uuid

        # Now verify that `prepare_basket` method removes the enterprise
        # catalog attribute if there is no `catalog` query parameter in url
        request.GET = {}
        basket = prepare_basket(request, [product])

        # Verify that enterprise catalog attribute does not exists for a basket
        # when basket is prepared with the value of provided catalog UUID
        with self.assertRaises(BasketAttribute.DoesNotExist):
            BasketAttribute.objects.get(
                basket=basket,
                attribute_type__name=ENTERPRISE_CATALOG_ATTRIBUTE_TYPE)
コード例 #8
0
ファイル: test_utils.py プロジェクト: eduNEXT/ecommerce
    def test_prepare_basket_with_bundle_voucher(self):
        """
        Test prepare_basket clears vouchers for a bundle
        """
        product = ProductFactory(stockrecords__price_excl_tax=100)
        new_range = RangeFactory(products=[
            product,
        ])
        voucher, __ = prepare_voucher(_range=new_range, benefit_value=10)

        request = self.request
        basket = prepare_basket(request, [product], voucher)
        self.assertTrue(basket.vouchers.all())
        request.GET = {'bundle': TEST_BUNDLE_ID}
        program_data = {
            'marketing_slug': 'program-slug',
            'title': 'program title',
            'type_attrs': {
                'slug': 'micromasters'
            }
        }
        with mock.patch('ecommerce.extensions.basket.utils.get_program',
                        return_value=program_data):
            basket = prepare_basket(request, [product])
        self.assertFalse(basket.vouchers.all())
コード例 #9
0
ファイル: test_utils.py プロジェクト: 10clouds/ecommerce
    def test_prepare_basket_affiliate_cookie_lifecycle(self):
        """ Verify a basket is returned and referral captured. """
        product = ProductFactory()
        affiliate_id = 'test_affiliate'
        self.request.COOKIES['affiliate_id'] = affiliate_id
        basket = prepare_basket(self.request, product)

        # test affiliate id from cookie saved in referral
        referral = Referral.objects.get(basket_id=basket.id)
        self.assertEqual(referral.affiliate_id, affiliate_id)

        # update cookie
        new_affiliate_id = 'new_affiliate'
        self.request.COOKIES['affiliate_id'] = new_affiliate_id
        basket = prepare_basket(self.request, product)

        # test new affiliate id saved
        referral = Referral.objects.get(basket_id=basket.id)
        self.assertEqual(referral.affiliate_id, new_affiliate_id)

        # expire cookie
        del self.request.COOKIES['affiliate_id']
        basket = prepare_basket(self.request, product)

        # test referral record is deleted when no cookie set
        with self.assertRaises(Referral.DoesNotExist):
            Referral.objects.get(basket_id=basket.id)
コード例 #10
0
ファイル: test_utils.py プロジェクト: regisb/ecommerce
 def test_prepare_basket_with_duplicate_seat(self):
     """ Verify a basket fixes the case where flush doesn't work and we attempt adding duplicate seat. """
     with mock.patch('ecommerce.extensions.basket.utils.Basket.flush'):
         product_type_seat = ProductClass.objects.create(name='Seat')
         product1 = ProductFactory(stockrecords__partner__short_code='test1', product_class=product_type_seat)
         prepare_basket(self.request, [product1])
         basket = prepare_basket(self.request, [product1])  # try to add a duplicate seat
         self.assertEqual(basket.product_quantity(product1), 1)
コード例 #11
0
 def test_prepare_basket_raises_exception_for_purchased_product(self):
     """
     Test prepare_basket raises AlreadyPlacedOrderException if the product is already purchased by user
     """
     product = ProductFactory()
     with mock.patch.object(UserAlreadyPlacedOrder, 'user_already_placed_order', return_value=True):
         with self.assertRaises(AlreadyPlacedOrderException):
             prepare_basket(self.request, [product])
コード例 #12
0
    def get(self, request):
        partner = get_partner_for_site(request)

        skus = [escape(sku) for sku in request.GET.getlist('sku')]
        code = request.GET.get('code', None)

        if not skus:
            return HttpResponseBadRequest(_('No SKUs provided.'))

        products = Product.objects.filter(stockrecords__partner=partner, stockrecords__partner_sku__in=skus)
        if not products:
            return HttpResponseBadRequest(_('Products with SKU(s) [{skus}] do not exist.').format(skus=', '.join(skus)))

        logger.info('Starting payment flow for user[%s] for products[%s].', request.user.username, skus)

        voucher = Voucher.objects.get(code=code) if code else None

        if voucher is None:
            # If there is an Enterprise entitlement available for this basket,
            # we redirect to the CouponRedeemView to apply the discount to the
            # basket and handle the data sharing consent requirement.
            code_redemption_redirect = get_enterprise_code_redemption_redirect(
                request,
                products,
                skus,
                'basket:basket-add'
            )
            if code_redemption_redirect:
                return code_redemption_redirect

        # check availability of products
        unavailable_product_ids = []
        for product in products:
            purchase_info = request.strategy.fetch_for_product(product)
            if not purchase_info.availability.is_available_to_buy:
                logger.warning('Product [%s] is not available to buy.', product.title)
                unavailable_product_ids.append(product.id)

        available_products = products.exclude(id__in=unavailable_product_ids)
        if not available_products:
            msg = _('No product is available to buy.')
            return HttpResponseBadRequest(msg)

        # Associate the user's email opt in preferences with the basket in
        # order to opt them in later as part of fulfillment
        BasketAttribute.objects.update_or_create(
            basket=request.basket,
            attribute_type=BasketAttributeType.objects.get(name=EMAIL_OPT_IN_ATTRIBUTE),
            defaults={'value_text': request.GET.get('email_opt_in') == 'true'},
        )

        try:
            prepare_basket(request, available_products, voucher)
        except AlreadyPlacedOrderException:
            return render(request, 'edx/error.html', {'error': _('You have already purchased these products')})
        url = add_utm_params_to_url(reverse('basket:summary'), self.request.GET.items())
        return HttpResponseRedirect(url, status=303)
コード例 #13
0
ファイル: views.py プロジェクト: siddhartharay007/ecommerce
    def get(self, request):
        partner = get_partner_for_site(request)

        sku = request.GET.get('sku', None)
        code = request.GET.get('code', None)

        if not sku:
            return HttpResponseBadRequest(_('No SKU provided.'))

        if code:
            voucher, __ = get_voucher_from_code(code=code)
        else:
            voucher = None

        try:
            product = StockRecord.objects.get(partner=partner, partner_sku=sku).product
            course_key = product.attr.course_key

            api = EdxRestApiClient(
                get_lms_enrollment_base_api_url(),
                oauth_access_token=request.user.access_token,
                append_slash=False
            )
            logger.debug(
                'Getting enrollment information for [%s] in [%s].',
                request.user.username,
                course_key
            )
            status = api.enrollment(','.join([request.user.username, course_key])).get()
            username = request.user.username
            seat_type = mode_for_seat(product)
            if status and status.get('mode') == seat_type and status.get('is_active'):
                logger.warning(
                    'User [%s] attempted to repurchase the [%s] seat of course [%s]',
                    username,
                    seat_type,
                    course_key
                )
                return HttpResponseBadRequest(_('You are already enrolled in {course}.').format(
                    course=product.course.name))
        except StockRecord.DoesNotExist:
            return HttpResponseBadRequest(_('SKU [{sku}] does not exist.').format(sku=sku))
        except (ConnectionError, SlumberBaseException, Timeout) as ex:
            logger.exception(
                'Failed to retrieve enrollment details for [%s] in course [%s], Because of [%s]',
                request.user.username,
                course_key,
                ex,
            )
            return HttpResponseBadRequest(_('An error occurred while retrieving enrollment details. Please try again.'))
        purchase_info = request.strategy.fetch_for_product(product)
        if not purchase_info.availability.is_available_to_buy:
            return HttpResponseBadRequest(_('Product [{product}] not available to buy.').format(product=product.title))

        prepare_basket(request, product, voucher)
        return HttpResponseRedirect(reverse('basket:summary'), status=303)
コード例 #14
0
 def test_prepare_basket_with_multiple_products(self):
     """ Verify a basket is returned and only contains a single product. """
     product1 = ProductFactory(stockrecords__partner__short_code='test1')
     product2 = ProductFactory(stockrecords__partner__short_code='test2')
     prepare_basket(self.request, [product1])
     basket = prepare_basket(self.request, [product2])
     self.assertIsNotNone(basket)
     self.assertEqual(basket.status, Basket.OPEN)
     self.assertEqual(basket.lines.count(), 1)
     self.assertEqual(basket.lines.first().product, product2)
     self.assertEqual(basket.product_quantity(product2), 1)
コード例 #15
0
ファイル: test_utils.py プロジェクト: eduNEXT/ecommerce
    def test_prepare_basket_attribute_delete(self):
        """
        Test prepare_basket removes the bundle attribute for a basket when a user is purchasing a single course
        """
        product = ProductFactory(categories=[],
                                 stockrecords__partner__short_code='second')
        request = self.request
        request.GET = {'bundle': TEST_BUNDLE_ID}
        program_data = {
            'marketing_slug': 'program-slug',
            'title': 'program title',
            'type_attrs': {
                'slug': 'micromasters'
            }
        }
        with mock.patch('ecommerce.extensions.basket.utils.get_program',
                        return_value=program_data):
            basket = prepare_basket(request, [product])

        # Verify that the bundle attribute exists for the basket when bundle is added to basket
        bundle_id = BasketAttribute.objects.get(
            basket=basket, attribute_type__name=BUNDLE).value_text
        self.assertEqual(bundle_id, TEST_BUNDLE_ID)

        # Verify that the attribute is deleted when a non-bundle product is added to the basket
        request.GET = {}
        with mock.patch(
                'ecommerce.extensions.basket.models.track_segment_event'
        ) as mock_track:
            with mock.patch('ecommerce.extensions.basket.models.get_program',
                            return_value=program_data):
                prepare_basket(request, [product])
            properties = {
                'bundle_id':
                TEST_BUNDLE_ID,
                'marketing_slug':
                program_data['type_attrs']['slug'] + '/' +
                program_data['marketing_slug'],
                'title':
                program_data['title'],
                'total_price':
                basket.total_excl_tax,
                'quantity':
                basket.lines.count(),
            }
            mock_track.assert_any_call(
                request.site, request.user,
                'edx.bi.ecommerce.basket.bundle_removed', properties)
        with self.assertRaises(BasketAttribute.DoesNotExist):
            BasketAttribute.objects.get(basket=basket,
                                        attribute_type__name=BUNDLE)

        # Verify that no exception is raised when no basket attribute exists fitting the delete statement parameters
        prepare_basket(request, [product])
コード例 #16
0
ファイル: test_utils.py プロジェクト: 10clouds/ecommerce
 def test_prepare_basket_with_multiple_products(self):
     """ Verify a basket is returned and only contains a single product. """
     product1 = ProductFactory(stockrecords__partner__short_code='test1')
     product2 = ProductFactory(stockrecords__partner__short_code='test2')
     basket = prepare_basket(self.request, product1)
     basket = prepare_basket(self.request, product2)
     self.assertIsNotNone(basket)
     self.assertEqual(basket.status, Basket.OPEN)
     self.assertEqual(basket.lines.count(), 1)
     self.assertEqual(basket.lines.first().product, product2)
     self.assertEqual(basket.product_quantity(product2), 1)
コード例 #17
0
 def test_prepare_basket_with_bundle_voucher(self):
     """
     Test prepare_basket clears vouchers for a bundle
     """
     product = ProductFactory()
     voucher = VoucherFactory(code='FIRST')
     request = self.request
     basket = prepare_basket(request, [product], voucher)
     self.assertTrue(basket.vouchers.all())
     request.GET = {'bundle': 'test_bundle'}
     basket = prepare_basket(request, [product])
     self.assertFalse(basket.vouchers.all())
コード例 #18
0
ファイル: views.py プロジェクト: mrhobbeys/ecommerce
    def get(self, request):
        partner = get_partner_for_site(request)

        sku = request.GET.get('sku', None)
        code = request.GET.get('code', None)

        if not sku:
            return HttpResponseBadRequest(_('No SKU provided.'))

        voucher = Voucher.objects.get(code=code) if code else None

        try:
            product = StockRecord.objects.get(partner=partner,
                                              partner_sku=sku).product
        except StockRecord.DoesNotExist:
            return HttpResponseBadRequest(
                _('SKU [{sku}] does not exist.').format(sku=sku))

        if voucher is None:
            # Find and apply the enterprise entitlement on the learner basket
            voucher = get_entitlement_voucher(request, product)

        # If the product isn't available then there's no reason to continue with the basket addition
        purchase_info = request.strategy.fetch_for_product(product)
        if not purchase_info.availability.is_available_to_buy:
            msg = _('Product [{product}] not available to buy.').format(
                product=product.title)
            return HttpResponseBadRequest(msg)

        # If the product is not an Enrollment Code, we check to see if the user is already
        # enrolled to prevent double-enrollment and/or accidental coupon usage
        if product.get_product_class(
        ).name != ENROLLMENT_CODE_PRODUCT_CLASS_NAME:
            try:
                if request.user.is_user_already_enrolled(request, product):
                    logger.warning(
                        'User [%s] attempted to repurchase the [%s] seat of course [%s]',
                        request.user.username, mode_for_seat(product),
                        product.attr.course_key)
                    msg = _('You are already enrolled in {course}.').format(
                        course=product.course.name)
                    return HttpResponseBadRequest(msg)
            except (ConnectionError, SlumberBaseException, Timeout):
                msg = _(
                    'An error occurred while retrieving enrollment details. Please try again.'
                )
                return HttpResponseBadRequest(msg)

        # At this point we're either adding an Enrollment Code product to the basket,
        # or the user is adding a Seat product for which they are not already enrolled
        prepare_basket(request, product, voucher)
        return HttpResponseRedirect(reverse('basket:summary'), status=303)
コード例 #19
0
    def test_prepare_basket_raises_exception_for_purchased_product(self):
        """
        Test prepare_basket raises AlreadyPlacedOrderException if the product is already purchased by user
        """
        order = create_order(user=self.request.user)
        product = order.lines.first().product

        with self.assertRaises(AlreadyPlacedOrderException):
            prepare_basket(self.request, [product])

        # If the switch is enabled, no validation should be performed.
        toggle_switch(DISABLE_REPEAT_ORDER_CHECK_SWITCH_NAME, True)
        prepare_basket(self.request, [product])
コード例 #20
0
ファイル: test_utils.py プロジェクト: uzairr/ecommerce
    def test_multiple_vouchers(self):
        """ Verify only the last entered voucher is contained in the basket. """
        product = ProductFactory()
        voucher1 = VoucherFactory(code='FIRST')
        basket = prepare_basket(self.request, product, voucher1)
        self.assertEqual(basket.vouchers.count(), 1)
        self.assertEqual(basket.vouchers.first(), voucher1)

        voucher2 = VoucherFactory(code='SECOND')
        new_basket = prepare_basket(self.request, product, voucher2)
        self.assertEqual(basket, new_basket)
        self.assertEqual(new_basket.vouchers.count(), 1)
        self.assertEqual(new_basket.vouchers.first(), voucher2)
コード例 #21
0
ファイル: test_utils.py プロジェクト: regisb/ecommerce
 def test_prepare_basket_with_bundle(self):
     """
     Test prepare_basket updates or creates a basket attribute for the associated bundle
     """
     product = ProductFactory()
     request = self.request
     basket = prepare_basket(request, [product])
     with self.assertRaises(BasketAttribute.DoesNotExist):
         BasketAttribute.objects.get(basket=basket, attribute_type__name=BUNDLE)
     request.GET = {'bundle': TEST_BUNDLE_ID}
     basket = prepare_basket(request, [product])
     bundle_id = BasketAttribute.objects.get(basket=basket, attribute_type__name=BUNDLE).value_text
     self.assertEqual(bundle_id, TEST_BUNDLE_ID)
コード例 #22
0
ファイル: test_utils.py プロジェクト: 10clouds/ecommerce
    def test_multiple_vouchers(self):
        """ Verify only the last entered voucher is contained in the basket. """
        product = ProductFactory()
        voucher1 = VoucherFactory(code='FIRST')
        basket = prepare_basket(self.request, product, voucher1)
        self.assertEqual(basket.vouchers.count(), 1)
        self.assertEqual(basket.vouchers.first(), voucher1)

        voucher2 = VoucherFactory(code='SECOND')
        new_basket = prepare_basket(self.request, product, voucher2)
        self.assertEqual(basket, new_basket)
        self.assertEqual(new_basket.vouchers.count(), 1)
        self.assertEqual(new_basket.vouchers.first(), voucher2)
コード例 #23
0
ファイル: test_utils.py プロジェクト: regisb/ecommerce
    def test_prepare_basket_with_bundle_voucher(self):
        """
        Test prepare_basket clears vouchers for a bundle
        """
        product = ProductFactory(stockrecords__price_excl_tax=100)
        new_range = RangeFactory(products=[product, ])
        voucher, __ = prepare_voucher(_range=new_range, benefit_value=10)

        request = self.request
        basket = prepare_basket(request, [product], voucher)
        self.assertTrue(basket.vouchers.all())
        request.GET = {'bundle': TEST_BUNDLE_ID}
        basket = prepare_basket(request, [product])
        self.assertFalse(basket.vouchers.all())
コード例 #24
0
ファイル: test_utils.py プロジェクト: regisb/ecommerce
    def test_multiple_vouchers(self):
        """ Verify only the last entered voucher is contained in the basket. """
        product = ProductFactory(stockrecords__price_excl_tax=100)
        new_range = RangeFactory(products=[product, ])
        voucher1, __ = prepare_voucher(code='TEST1', _range=new_range, benefit_value=10)
        basket = prepare_basket(self.request, [product], voucher1)
        self.assertEqual(basket.vouchers.count(), 1)
        self.assertEqual(basket.vouchers.first(), voucher1)

        voucher2, __ = prepare_voucher(code='TEST2', _range=new_range, benefit_value=20)
        new_basket = prepare_basket(self.request, [product], voucher2)
        self.assertEqual(basket, new_basket)
        self.assertEqual(new_basket.vouchers.count(), 1)
        self.assertEqual(new_basket.vouchers.first(), voucher2)
コード例 #25
0
    def get(self, request):
        partner = get_partner_for_site(request)

        sku = request.GET.get('sku', None)
        code = request.GET.get('code', None)

        if not sku:
            return HttpResponseBadRequest(_('No SKU provided.'))

        voucher = Voucher.objects.get(code=code) if code else None

        try:
            product = StockRecord.objects.get(partner=partner,
                                              partner_sku=sku).product
        except StockRecord.DoesNotExist:
            return HttpResponseBadRequest(
                _('SKU [{sku}] does not exist.').format(sku=sku))

        logger.info('Starting payment flow for user[%s] for product[%s].',
                    request.user.username, sku)

        if voucher is None:
            # If there is an Enterprise entitlement available for this basket,
            # we redirect to the CouponRedeemView to apply the discount to the
            # basket and handle the data sharing consent requirement.
            code_redemption_redirect = get_enterprise_code_redemption_redirect(
                request, [product], [sku], 'basket:single-item')
            if code_redemption_redirect:
                return code_redemption_redirect

        # If the product isn't available then there's no reason to continue with the basket addition
        purchase_info = request.strategy.fetch_for_product(product)
        if not purchase_info.availability.is_available_to_buy:
            msg = _('Product [{product}] not available to buy.').format(
                product=product.title)
            return HttpResponseBadRequest(msg)

        # At this point we're either adding an Enrollment Code product to the basket,
        # or the user is adding a Seat product for which they are not already enrolled
        try:
            prepare_basket(request, [product], voucher)
        except AlreadyPlacedOrderException:
            msg = _('You have already purchased {course} seat.').format(
                course=product.title)
            return render(request, 'edx/error.html', {'error': msg})
        url = add_utm_params_to_url(reverse('basket:summary'),
                                    self.request.GET.items())
        return HttpResponseRedirect(url, status=303)
コード例 #26
0
ファイル: coupons.py プロジェクト: virajut/ecommerce
    def create(self, request, *args, **kwargs):
        """Adds coupon to the user's basket.

        Expects request array to contain all the necessary data (listed out below).
        This information is then used to create a coupon product, add to a
        basket and create an order from it.

        Arguments:
            request (HttpRequest): With parameters title, client,
            stock_record_ids, start_date, end_date, code, benefit_type, benefit_value,
            voucher_type, quantity, price, category, note and invoice data in the body.

        Returns:
            200 if the order was created successfully; the basket ID is included in the response
                body along with the order ID and payment information.
            400 if a custom code is received that already exists,
                if a course mode is selected that is not supported.
            401 if an unauthenticated request is denied permission to access the endpoint.
            429 if the client has made requests at a rate exceeding that allowed by the configured rate limit.
            500 if an error occurs when attempting to create a coupon.
        """
        try:
            with transaction.atomic():
                try:
                    self.validate_access_for_enterprise_switch(request.data)
                    cleaned_voucher_data = self.clean_voucher_request_data(
                        request.data, request.site.siteconfiguration.partner
                    )
                except ValidationError as error:
                    logger.exception('Failed to create coupon. %s', error.message)
                    # FIXME This should ALWAYS return 400.
                    return Response(error.message, status=error.code or 400)

                try:
                    coupon_product = self.create_coupon_and_vouchers(cleaned_voucher_data)
                except (KeyError, IntegrityError) as error:
                    logger.exception('Coupon creation failed!')
                    return Response(str(error), status=status.HTTP_400_BAD_REQUEST)

                basket = prepare_basket(request, [coupon_product])

                # Create an order now since payment is handled out of band via an invoice.
                client, __ = BusinessClient.objects.update_or_create(
                    name=cleaned_voucher_data['enterprise_customer_name'] or request.data.get('client'),
                    defaults={'enterprise_customer_uuid': cleaned_voucher_data['enterprise_customer']}
                )
                invoice_data = self.create_update_data_dict(data=request.data, fields=Invoice.UPDATEABLE_INVOICE_FIELDS)
                response_data = self.create_order_for_invoice(
                    basket, coupon_id=coupon_product.id, client=client, invoice_data=invoice_data
                )
                if cleaned_voucher_data['notify_email']:
                    self.send_codes_availability_email(
                        self.request.site,
                        cleaned_voucher_data['notify_email'],
                        cleaned_voucher_data['enterprise_customer'],
                        coupon_product.id
                    )
                return Response(response_data, status=status.HTTP_200_OK)
        except ValidationError as e:
            raise serializers.ValidationError(e.message)
コード例 #27
0
ファイル: test_utils.py プロジェクト: open-craft/ecommerce
    def test_attribution_atomic_transaction(self):
        """
        Verify that an IntegrityError raised while creating a referral
        does not prevent a basket from being created.
        """
        self._setup_request_cookie()
        product = ProductFactory()
        existing_basket = Basket.get_basket(self.request.user, self.request.site)
        existing_referral = Referral(basket=existing_basket, site=self.request.site)
        # Let's save an existing referral object to force the duplication happen in database
        existing_referral.save()

        with transaction.atomic():
            with mock.patch('ecommerce.extensions.basket.utils._referral_from_basket_site') as mock_get_referral:
                # Mock to return a duplicated referral object, so when saved, a DB integrity error is raised
                # Mocking with side_effect to raise IntegrityError will not roll back the DB transaction
                # We actually would handle the exception in the attribute_cookie_data method.
                # Only causing the true database conflict like what we are doing here, would cause the roll back
                mock_get_referral.return_value = Referral(basket=existing_basket, site=self.request.site)
                basket = prepare_basket(self.request, product)
                referral = Referral.objects.filter(basket=basket)

        self.assertEqual(len(referral), 1)
        self.assertIsNotNone(basket)
        self.assertTrue(basket.id > 0)
        self.assertEqual(basket.status, Basket.OPEN)
        self.assertEqual(basket.lines.count(), 1)
        self.assertEqual(basket.lines.first().product, product)
コード例 #28
0
    def get(self, request):
        """
        Looks up the passed code and adds the matching product to a basket,
        then applies the voucher and if the basket total is FREE places the order and
        enrolls the user in the course.
        """
        template_name = 'coupons/offer.html'
        code = request.GET.get('code', None)

        if not code:
            return render(request, template_name, {'error': _('Code not provided')})
        try:
            voucher, product = get_voucher_from_code(code=code)
        except Voucher.DoesNotExist:
            msg = 'No voucher found with code {code}'.format(code=code)
            return render(request, template_name, {'error': _(msg)})
        except exceptions.ProductNotFoundError:
            return render(request, template_name, {'error': _('The voucher is not applicable to your current basket.')})

        valid_voucher, msg = voucher_is_valid(voucher, product, request)
        if not valid_voucher:
            return render(request, template_name, {'error': msg})

        basket = prepare_basket(request, product, voucher)
        if basket.total_excl_tax == AC.FREE:
            self.place_free_order(basket)
        else:
            return HttpResponseRedirect(reverse('basket:summary'))

        return HttpResponseRedirect(get_lms_url(''))
コード例 #29
0
    def test_attribution_atomic_transaction(self):
        """
        Verify that an IntegrityError raised while creating a referral
        does not prevent a basket from being created.
        """
        self._setup_request_cookie()
        product = ProductFactory()
        existing_basket = Basket.get_basket(self.request.user,
                                            self.request.site)
        existing_referral = Referral(basket=existing_basket,
                                     site=self.request.site)
        # Let's save an existing referral object to force the duplication happen in database
        existing_referral.save()

        with transaction.atomic():
            with mock.patch(
                    'ecommerce.extensions.basket.utils._referral_from_basket_site'
            ) as mock_get_referral:
                # Mock to return a duplicated referral object, so when saved, a DB integrity error is raised
                # Mocking with side_effect to raise IntegrityError will not roll back the DB transaction
                # We actually would handle the exception in the attribute_cookie_data method.
                # Only causing the true database conflict like what we are doing here, would cause the roll back
                mock_get_referral.return_value = Referral(
                    basket=existing_basket, site=self.request.site)
                basket = prepare_basket(self.request, [product])
                referral = Referral.objects.filter(basket=basket)

        self.assertEqual(len(referral), 1)
        self.assertIsNotNone(basket)
        self.assertTrue(basket.id > 0)
        self.assertEqual(basket.status, Basket.OPEN)
        self.assertEqual(basket.lines.count(), 1)
        self.assertEqual(basket.lines.first().product, product)
コード例 #30
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)
コード例 #31
0
    def test_prepare_basket_applies_valid_voucher_argument(self):
        """
            Tests that prepare_basket applies a valid voucher passed as an
            an argument, even when there is also a valid voucher already on
            the basket.
        """
        product = ProductFactory(stockrecords__partner__short_code='test1',
                                 stockrecords__price_excl_tax=100)
        new_range = RangeFactory(products=[product])
        new_voucher, __ = prepare_voucher(code='xyz',
                                          _range=new_range,
                                          benefit_value=10)
        existing_voucher, __ = prepare_voucher(code='test',
                                               _range=new_range,
                                               benefit_value=50)

        basket = BasketFactory(owner=self.request.user, site=self.request.site)
        basket.vouchers.add(existing_voucher)
        self.assertEqual(basket.vouchers.count(), 1)

        basket = prepare_basket(self.request, [product], new_voucher)
        self.assertIsNotNone(basket)
        self.assertEqual(basket.vouchers.count(), 1)
        self.assertEqual(basket.vouchers.first().code, 'XYZ')
        self.assertEqual(basket.total_discount, 10.00)
コード例 #32
0
ファイル: test_utils.py プロジェクト: regisb/ecommerce
    def test_prepare_basket_enrollment_with_voucher(self):
        """Verify the basket does not contain a voucher if enrollment code is added to it."""
        course = CourseFactory(partner=self.partner)
        course.create_or_update_seat('verified', False, 10, create_enrollment_code=True)
        enrollment_code = Product.objects.get(product_class__name=ENROLLMENT_CODE_PRODUCT_CLASS_NAME)
        voucher, product = prepare_voucher()

        basket = prepare_basket(self.request, [product], voucher)
        self.assertIsNotNone(basket)
        self.assertEqual(basket.all_lines()[0].product, product)
        self.assertTrue(basket.contains_a_voucher)

        basket = prepare_basket(self.request, [enrollment_code], voucher)
        self.assertIsNotNone(basket)
        self.assertEqual(basket.all_lines()[0].product, enrollment_code)
        self.assertFalse(basket.contains_a_voucher)
コード例 #33
0
ファイル: views.py プロジェクト: jeannsebold666br/ecommerce
    def get(self, request):
        """
        Looks up the passed code and adds the matching product to a basket,
        then applies the voucher and if the basket total is FREE places the order and
        enrolls the user in the course.
        """
        template_name = 'coupons/offer.html'
        code = request.GET.get('code', None)

        if not code:
            return render(request, template_name,
                          {'error': _('Code not provided')})
        try:
            voucher, product = get_voucher_from_code(code=code)
        except Voucher.DoesNotExist:
            msg = 'No voucher found with code {code}'.format(code=code)
            return render(request, template_name, {'error': _(msg)})
        except exceptions.ProductNotFoundError:
            return render(
                request, template_name, {
                    'error':
                    _('The voucher is not applicable to your current basket.')
                })

        valid_voucher, msg = voucher_is_valid(voucher, product, request)
        if not valid_voucher:
            return render(request, template_name, {'error': msg})

        basket = prepare_basket(request, product, voucher)
        if basket.total_excl_tax == AC.FREE:
            self.place_free_order(basket)
        else:
            return HttpResponseRedirect(reverse('basket:summary'))

        return HttpResponseRedirect(get_lms_url(''))
コード例 #34
0
ファイル: views.py プロジェクト: open-craft/ecommerce
    def get(self, request):
        partner = get_partner_for_site(request)

        sku = request.GET.get('sku', None)
        code = request.GET.get('code', None)

        if not sku:
            return HttpResponseBadRequest(_('No SKU provided.'))

        voucher = Voucher.objects.get(code=code) if code else None

        try:
            product = StockRecord.objects.get(partner=partner, partner_sku=sku).product
        except StockRecord.DoesNotExist:
            return HttpResponseBadRequest(_('SKU [{sku}] does not exist.').format(sku=sku))

        if voucher is None:
            # Find and apply the enterprise entitlement on the learner basket
            voucher = get_entitlement_voucher(request, product)

        # If the product isn't available then there's no reason to continue with the basket addition
        purchase_info = request.strategy.fetch_for_product(product)
        if not purchase_info.availability.is_available_to_buy:
            msg = _('Product [{product}] not available to buy.').format(product=product.title)
            return HttpResponseBadRequest(msg)

        # If the product is not an Enrollment Code, we check to see if the user is already
        # enrolled to prevent double-enrollment and/or accidental coupon usage
        if product.get_product_class().name != ENROLLMENT_CODE_PRODUCT_CLASS_NAME:
            try:
                if request.user.is_user_already_enrolled(request, product):
                    logger.warning(
                        'User [%s] attempted to repurchase the [%s] seat of course [%s]',
                        request.user.username,
                        mode_for_seat(product),
                        product.attr.course_key
                    )
                    msg = _('You are already enrolled in {course}.').format(course=product.course.name)
                    return HttpResponseBadRequest(msg)
            except (ConnectionError, SlumberBaseException, Timeout):
                msg = _('An error occurred while retrieving enrollment details. Please try again.')
                return HttpResponseBadRequest(msg)

        # At this point we're either adding an Enrollment Code product to the basket,
        # or the user is adding a Seat product for which they are not already enrolled
        prepare_basket(request, product, voucher)
        return HttpResponseRedirect(reverse('basket:summary'), status=303)
コード例 #35
0
ファイル: views.py プロジェクト: mrhobbeys/ecommerce
    def get(self, request):
        """
        Looks up the passed code and adds the matching product to a basket,
        then applies the voucher and if the basket total is FREE places the order and
        enrolls the user in the course.
        """
        template_name = 'coupons/_offer_error.html'
        code = request.GET.get('code')
        sku = request.GET.get('sku')

        if not code:
            return render(request, template_name,
                          {'error': _('Code not provided.')})
        if not sku:
            return render(request, template_name,
                          {'error': _('SKU not provided.')})

        try:
            voucher = Voucher.objects.get(code=code)
        except Voucher.DoesNotExist:
            msg = 'No voucher found with code {code}'.format(code=code)
            return render(request, template_name, {'error': _(msg)})

        try:
            product = StockRecord.objects.get(partner_sku=sku).product
        except StockRecord.DoesNotExist:
            return render(request, template_name,
                          {'error': _('The product does not exist.')})

        valid_voucher, msg = voucher_is_valid(voucher, [product], request)
        if not valid_voucher:
            return render(request, template_name, {'error': msg})

        if not voucher.offers.first().is_email_valid(request.user.email):
            return render(
                request, template_name,
                {'error': _('You are not eligible to use this coupon.')})

        if not request.user.account_details(request)['is_active']:
            return render(
                request, template_name, {
                    'error':
                    _('You need to activate your account in order to redeem this coupon.'
                      )
                })

        if request.user.is_user_already_enrolled(request, product):
            return render(
                request, template_name,
                {'error': _('You are already enrolled in the course.')})

        basket = prepare_basket(request, product, voucher)
        if basket.total_excl_tax == 0:
            self.place_free_order(basket)
        else:
            return HttpResponseRedirect(reverse('basket:summary'))

        return HttpResponseRedirect(
            request.site.siteconfiguration.student_dashboard_url)
コード例 #36
0
 def test_prepare_basket_calls_attribution_method(self):
     """ Verify a basket is returned and referral method called. """
     with mock.patch(
             'ecommerce.extensions.basket.utils.attribute_cookie_data'
     ) as mock_attr_method:
         product = ProductFactory()
         basket = prepare_basket(self.request, [product])
         mock_attr_method.assert_called_with(basket, self.request)
コード例 #37
0
ファイル: test_utils.py プロジェクト: open-craft/ecommerce
    def test_prepare_basket_enrollment_with_voucher(self):
        """Verify the basket does not contain a voucher if enrollment code is added to it."""
        course = CourseFactory()
        toggle_switch(ENROLLMENT_CODE_SWITCH, True)
        course.create_or_update_seat('verified', False, 10, self.partner, create_enrollment_code=True)
        enrollment_code = Product.objects.get(product_class__name=ENROLLMENT_CODE_PRODUCT_CLASS_NAME)
        voucher, product = prepare_voucher()

        basket = prepare_basket(self.request, product, voucher)
        self.assertIsNotNone(basket)
        self.assertEqual(basket.all_lines()[0].product, product)
        self.assertTrue(basket.contains_a_voucher)

        basket = prepare_basket(self.request, enrollment_code, voucher)
        self.assertIsNotNone(basket)
        self.assertEqual(basket.all_lines()[0].product, enrollment_code)
        self.assertFalse(basket.contains_a_voucher)
コード例 #38
0
ファイル: test_utils.py プロジェクト: regisb/ecommerce
    def test_is_duplicate_seat_attempt__enrollment_code(self):
        """ Verify we get a correct response for duplicate seat check (false for Enrollment code)"""
        enrollment_class = ProductClass.objects.create(name='Enrollment Code')
        enrollment_product = ProductFactory(stockrecords__partner__short_code='test3', product_class=enrollment_class)
        basket_with_enrollment_code = prepare_basket(self.request, [enrollment_product])
        result_product3 = is_duplicate_seat_attempt(basket_with_enrollment_code, enrollment_product)

        self.assertFalse(result_product3)
コード例 #39
0
ファイル: test_utils.py プロジェクト: 10clouds/ecommerce
 def test_prepare_basket_without_voucher(self):
     """ Verify a basket is returned and does not contain a voucher. """
     product = ProductFactory()
     basket = prepare_basket(self.request, product)
     self.assertIsNotNone(basket)
     self.assertEqual(basket.status, Basket.OPEN)
     self.assertEqual(basket.lines.count(), 1)
     self.assertEqual(basket.lines.first().product, product)
     self.assertFalse(basket.vouchers.all())
     self.assertFalse(basket.applied_offers())
コード例 #40
0
ファイル: views.py プロジェクト: open-craft/ecommerce
    def get(self, request):
        """
        Looks up the passed code and adds the matching product to a basket,
        then applies the voucher and if the basket total is FREE places the order and
        enrolls the user in the course.
        """
        template_name = 'coupons/_offer_error.html'
        code = request.GET.get('code')
        sku = request.GET.get('sku')

        if not code:
            return render(request, template_name, {'error': _('Code not provided.')})
        if not sku:
            return render(request, template_name, {'error': _('SKU not provided.')})

        try:
            voucher = Voucher.objects.get(code=code)
        except Voucher.DoesNotExist:
            msg = 'No voucher found with code {code}'.format(code=code)
            return render(request, template_name, {'error': _(msg)})

        try:
            product = StockRecord.objects.get(partner_sku=sku).product
        except StockRecord.DoesNotExist:
            return render(request, template_name, {'error': _('The product does not exist.')})

        valid_voucher, msg = voucher_is_valid(voucher, [product], request)
        if not valid_voucher:
            return render(request, template_name, {'error': msg})

        if not voucher.offers.first().is_email_valid(request.user.email):
            return render(request, template_name, {'error': _('You are not eligible to use this coupon.')})

        if not request.user.account_details(request)['is_active']:
            return render(
                request,
                template_name,
                {'error': _('You need to activate your account in order to redeem this coupon.')}
            )

        if request.user.is_user_already_enrolled(request, product):
            return render(request, template_name, {'error': _('You are already enrolled in the course.')})

        basket = prepare_basket(request, product, voucher)
        if basket.total_excl_tax == 0:
            self.place_free_order(basket)
        else:
            return HttpResponseRedirect(reverse('basket:summary'))

        return HttpResponseRedirect(request.site.siteconfiguration.student_dashboard_url)
コード例 #41
0
ファイル: views.py プロジェクト: pjha1994/ecommerce-1
    def get(self, request):
        """
        Looks up the passed code and adds the matching product to a basket,
        then applies the voucher and if the basket total is FREE places the order and
        enrolls the user in the course.
        """
        template_name = 'coupons/offer.html'
        code = request.GET.get('code', None)

        if not code:
            return render(request, template_name, {'error': _('Code not provided')})

        voucher, product = get_voucher_from_code(code=code)
        valid_voucher, msg = voucher_is_valid(voucher, product, request)
        if not valid_voucher:
            return render(request, template_name, {'error': msg})

        basket = prepare_basket(request, product, voucher)
        if basket.total_excl_tax == AC.FREE:
            basket.freeze()
            order_metadata = data_api.get_order_metadata(basket)

            logger.info(
                u"Preparing to place order [%s] for the contents of basket [%d]",
                order_metadata[AC.KEYS.ORDER_NUMBER],
                basket.id,
            )

            # Place an order. If order placement succeeds, the order is committed
            # to the database so that it can be fulfilled asynchronously.
            self.handle_order_placement(
                order_number=order_metadata[AC.KEYS.ORDER_NUMBER],
                user=basket.owner,
                basket=basket,
                shipping_address=None,
                shipping_method=order_metadata[AC.KEYS.SHIPPING_METHOD],
                shipping_charge=order_metadata[AC.KEYS.SHIPPING_CHARGE],
                billing_address=None,
                order_total=order_metadata[AC.KEYS.ORDER_TOTAL],
            )
        else:
            return HttpResponseRedirect(reverse('basket:summary'))

        return HttpResponseRedirect(get_lms_url(''))
コード例 #42
0
ファイル: test_utils.py プロジェクト: 10clouds/ecommerce
    def test_prepare_basket_with_voucher(self):
        """ Verify a basket is returned and contains a voucher and the voucher is applied. """
        # Prepare a product with price of 100 and a voucher with 10% discount for that product.
        product = ProductFactory(stockrecords__price_excl_tax=100)
        new_range = RangeFactory(products=[product, ])
        voucher, product = prepare_voucher(_range=new_range, benefit_value=10)

        stock_record = StockRecord.objects.get(product=product)
        self.assertEqual(stock_record.price_excl_tax, 100.00)

        basket = prepare_basket(self.request, product, voucher)
        self.assertIsNotNone(basket)
        self.assertEqual(basket.status, Basket.OPEN)
        self.assertEqual(basket.lines.count(), 1)
        self.assertEqual(basket.lines.first().product, product)
        self.assertEqual(basket.vouchers.count(), 1)
        self.assertIsNotNone(basket.applied_offers())
        self.assertEqual(basket.total_discount, 10.00)
        self.assertEqual(basket.total_excl_tax, 90.00)
コード例 #43
0
ファイル: views.py プロジェクト: 10clouds/ecommerce
    def get(self, request):
        partner = get_partner_for_site(request)

        sku = request.GET.get('sku', None)
        code = request.GET.get('code', None)

        if not sku:
            return HttpResponseBadRequest(_('No SKU provided.'))

        if code:
            voucher, __ = get_voucher_and_products_from_code(code=code)
        else:
            voucher = None

        try:
            product = StockRecord.objects.get(partner=partner, partner_sku=sku).product
        except StockRecord.DoesNotExist:
            return HttpResponseBadRequest(_('SKU [{sku}] does not exist.').format(sku=sku))

        # If the product isn't available then there's no reason to continue with the basket addition
        purchase_info = request.strategy.fetch_for_product(product)
        if not purchase_info.availability.is_available_to_buy:
            msg = _('Product [{product}] not available to buy.').format(product=product.title)
            return HttpResponseBadRequest(msg)

        # If the product is not an Enrollment Code, we check to see if the user is already
        # enrolled to prevent double-enrollment and/or accidental coupon usage
        if product.get_product_class().name != ENROLLMENT_CODE_PRODUCT_CLASS_NAME:

            course_key = product.attr.course_key

            # Submit a query to the LMS Enrollment API
            try:
                api = EdxRestApiClient(
                    get_lms_enrollment_base_api_url(),
                    oauth_access_token=request.user.access_token,
                    append_slash=False
                )
                logger.debug(
                    'Getting enrollment information for [%s] in [%s].',
                    request.user.username,
                    course_key
                )
                status = api.enrollment(','.join([request.user.username, course_key])).get()
            except (ConnectionError, SlumberBaseException, Timeout) as ex:
                logger.exception(
                    'Failed to retrieve enrollment details for [%s] in course [%s], Because of [%s]',
                    request.user.username,
                    course_key,
                    ex,
                )
                msg = _('An error occurred while retrieving enrollment details. Please try again.')
                return HttpResponseBadRequest(msg)

            # Enrollment API response received, now perform the actual enrollment check
            username = request.user.username
            seat_type = mode_for_seat(product)
            if status and status.get('mode') == seat_type and status.get('is_active'):
                logger.warning(
                    'User [%s] attempted to repurchase the [%s] seat of course [%s]',
                    username,
                    seat_type,
                    course_key
                )
                msg = _('You are already enrolled in {course}.').format(course=product.course.name)
                return HttpResponseBadRequest(msg)

        # At this point we're either adding an Enrollment Code product to the basket,
        # or the user is adding a Seat product for which they are not already enrolled
        prepare_basket(request, product, voucher)
        return HttpResponseRedirect(reverse('basket:summary'), status=303)
コード例 #44
0
ファイル: test_utils.py プロジェクト: open-craft/ecommerce
 def test_prepare_basket_calls_attribution_method(self):
     """ Verify a basket is returned and referral method called. """
     with mock.patch('ecommerce.extensions.basket.utils.attribute_cookie_data') as mock_attr_method:
         product = ProductFactory()
         basket = prepare_basket(self.request, product)
         mock_attr_method.assert_called_with(basket, self.request)
コード例 #45
0
ファイル: coupons.py プロジェクト: digideskio/ecommerce-2
    def create(self, request, *args, **kwargs):
        """Adds coupon to the user's basket.

        Expects request array to contain all the necessary data (listed out below).
        This information is then used to create a coupon product, add to a
        basket and create an order from it.

        Arguments:
            request (HttpRequest): With parameters title, client_username,
            stock_record_ids, start_date, end_date, code, benefit_type, benefit_value,
            voucher_type, quantity, price, category and note in the body.

        Returns:
            200 if the order was created successfully; the basket ID is included in the response
                body along with the order ID and payment information.
            401 if an unauthenticated request is denied permission to access the endpoint.
            429 if the client has made requests at a rate exceeding that allowed by the configured rate limit.
            500 if an error occurs when attempting to create a coupon.
        """
        with transaction.atomic():
            title = request.data[AC.KEYS.TITLE]
            client_username = request.data[AC.KEYS.CLIENT_USERNAME]
            stock_record_ids = request.data[AC.KEYS.STOCK_RECORD_IDS]
            start_date = dateutil.parser.parse(request.data[AC.KEYS.START_DATE])
            end_date = dateutil.parser.parse(request.data[AC.KEYS.END_DATE])
            code = request.data[AC.KEYS.CODE]
            benefit_type = request.data[AC.KEYS.BENEFIT_TYPE]
            benefit_value = request.data[AC.KEYS.BENEFIT_VALUE]
            voucher_type = request.data[AC.KEYS.VOUCHER_TYPE]
            quantity = request.data[AC.KEYS.QUANTITY]
            price = request.data[AC.KEYS.PRICE]
            partner = request.site.siteconfiguration.partner
            categories = Category.objects.filter(id__in=request.data[AC.KEYS.CATEGORY_IDS])
            client, __ = BusinessClient.objects.get_or_create(name=client_username)
            note = request.data.get('note', None)
            max_uses = request.data.get('max_uses', None)

            # Maximum number of uses can be set for each voucher type and disturb
            # the predefined behaviours of the different voucher types. Therefor
            # here we enforce that the max_uses variable is used only for
            # ONCE_PER_CUSTOMER types
            if max_uses and voucher_type == Voucher.ONCE_PER_CUSTOMER:
                max_uses = int(max_uses)
            else:
                max_uses = None

            # We currently do not support multi-use voucher types.
            if voucher_type == Voucher.MULTI_USE:
                raise NotImplementedError('Multi-use voucher types are not supported')

            # When a black-listed course mode is received raise an exception.
            # Audit modes do not have a certificate type and therefore will raise
            # an AttributeError exception.
            seats = Product.objects.filter(stockrecords__id__in=stock_record_ids)
            for seat in seats:
                try:
                    if seat.attr.certificate_type in settings.BLACK_LIST_COUPON_COURSE_MODES:
                        raise Exception('Course mode not supported')
                except AttributeError:
                    raise Exception('Course mode not supported')

            stock_records_string = ' '.join(str(id) for id in stock_record_ids)

            coupon_catalog, __ = get_or_create_catalog(
                name='Catalog for stock records: {}'.format(stock_records_string),
                partner=partner,
                stock_record_ids=stock_record_ids
            )

            data = {
                'partner': partner,
                'title': title,
                'benefit_type': benefit_type,
                'benefit_value': benefit_value,
                'catalog': coupon_catalog,
                'end_date': end_date,
                'code': code,
                'quantity': quantity,
                'start_date': start_date,
                'voucher_type': voucher_type,
                'categories': categories,
                'note': note,
                'max_uses': max_uses,
            }

            coupon_product = self.create_coupon_product(title, price, data)

            basket = prepare_basket(request, coupon_product)

            # Create an order now since payment is handled out of band via an invoice.
            response_data = self.create_order_for_invoice(basket, coupon_id=coupon_product.id, client=client)

            return Response(response_data, status=status.HTTP_200_OK)
コード例 #46
0
ファイル: mixins.py プロジェクト: digideskio/ecommerce-2
    def create_coupon(
            self,
            title='Test coupon',
            price=100,
            client=None,
            partner=None,
            catalog=None,
            code='',
            benefit_value=100,
            note=None,
            max_uses=None,
            quantity=5
    ):
        """Helper method for creating a coupon.

        Arguments:
            title(str): Title of the coupon
            price(int): Price of the coupon
            partner(Partner): Partner used for creating a catalog
            catalog(Catalog): Catalog of courses for which the coupon applies
            code(str): Custom coupon code
            benefit_value(int): The voucher benefit value

        Returns:
            coupon (Coupon)

        """
        if partner is None:
            partner = PartnerFactory(name='Tester')
        if client is None:
            client, __ = BusinessClient.objects.get_or_create(name='Test Client')
        if catalog is None:
            catalog = Catalog.objects.create(partner=partner)
        if code is not '':
            quantity = 1
        data = {
            'partner': partner,
            'benefit_type': Benefit.PERCENTAGE,
            'benefit_value': benefit_value,
            'catalog': catalog,
            'end_date': datetime.date(2020, 1, 1),
            'code': code,
            'quantity': quantity,
            'start_date': datetime.date(2015, 1, 1),
            'voucher_type': Voucher.SINGLE_USE,
            'categories': [self.category],
            'note': note,
            'max_uses': max_uses,
        }

        coupon = CouponViewSet().create_coupon_product(
            title=title,
            price=price,
            data=data
        )

        request = RequestFactory()
        request.site = self.site
        request.user = factories.UserFactory()
        request.COOKIES = {}

        self.basket = prepare_basket(request, coupon)

        self.response_data = CouponViewSet().create_order_for_invoice(self.basket, coupon_id=coupon.id, client=client)
        coupon.client = client

        return coupon
コード例 #47
0
ファイル: mixins.py プロジェクト: open-craft/ecommerce
    def create_coupon(self, benefit_type=Benefit.PERCENTAGE, benefit_value=100, catalog=None,
                      catalog_query=None, client=None, code='', course_seat_types=None, email_domains=None,
                      enterprise_customer=None, max_uses=None, note=None, partner=None, price=100, quantity=5,
                      title='Test coupon', voucher_type=Voucher.SINGLE_USE, course_catalog=None):
        """Helper method for creating a coupon.

        Arguments:
            benefit_type(str): The voucher benefit type
            benefit_value(int): The voucher benefit value
            catalog(Catalog): Catalog of courses for which the coupon applies
            catalog_query(str): Course query string
            client (BusinessClient):  Optional business client object
            code(str): Custom coupon code
            course_catalog (int): Course catalog id from Catalog Service
            course_seat_types(str): A string of comma-separated list of seat types
            enterprise_customer (str): Hex-encoded UUID for an Enterprise Customer object from the Enterprise app.
            email_domains(str): A comma seperated list of email domains
            max_uses (int): Number of Voucher max uses
            note (str): Coupon note.
            partner(Partner): Partner used for creating a catalog
            price(int): Price of the coupon
            quantity (int): Number of vouchers to be created and associated with the coupon
            title(str): Title of the coupon
            voucher_type (str): Voucher type

        Returns:
            coupon (Coupon)

        """
        if partner is None:
            partner = PartnerFactory(name='Tester')
        if client is None:
            client, __ = BusinessClient.objects.get_or_create(name='Test Client')
        if catalog is None and not (catalog_query and course_seat_types):
            catalog = Catalog.objects.create(partner=partner)
        if code is not '':
            quantity = 1

        coupon = create_coupon_product(
            benefit_type=benefit_type,
            benefit_value=benefit_value,
            catalog=catalog,
            catalog_query=catalog_query,
            category=self.category,
            code=code,
            course_catalog=course_catalog,
            course_seat_types=course_seat_types,
            email_domains=email_domains,
            end_datetime=datetime.datetime(2020, 1, 1),
            enterprise_customer=enterprise_customer,
            max_uses=max_uses,
            note=note,
            partner=partner,
            price=price,
            quantity=quantity,
            start_datetime=datetime.datetime(2015, 1, 1),
            title=title,
            voucher_type=voucher_type
        )

        request = RequestFactory()
        request.site = self.site
        request.user = factories.UserFactory()
        request.COOKIES = {}

        self.basket = prepare_basket(request, coupon)

        view = CouponViewSet()
        view.request = request

        self.response_data = view.create_order_for_invoice(self.basket, coupon_id=coupon.id, client=client)
        coupon.client = client

        return coupon
コード例 #48
0
ファイル: coupons.py プロジェクト: open-craft/ecommerce
    def create(self, request, *args, **kwargs):
        """Adds coupon to the user's basket.

        Expects request array to contain all the necessary data (listed out below).
        This information is then used to create a coupon product, add to a
        basket and create an order from it.

        Arguments:
            request (HttpRequest): With parameters title, client,
            stock_record_ids, start_date, end_date, code, benefit_type, benefit_value,
            voucher_type, quantity, price, category, note and invoice data in the body.

        Returns:
            200 if the order was created successfully; the basket ID is included in the response
                body along with the order ID and payment information.
            400 if a custom code is received that already exists,
                if a course mode is selected that is not supported.
            401 if an unauthenticated request is denied permission to access the endpoint.
            429 if the client has made requests at a rate exceeding that allowed by the configured rate limit.
            500 if an error occurs when attempting to create a coupon.
        """
        try:
            with transaction.atomic():
                try:
                    cleaned_voucher_data = self.clean_voucher_request_data(request)
                except ValidationError as error:
                    logger.exception('Failed to create coupon. %s', error.message)
                    return Response(error.message, status=error.code)

                try:
                    coupon_product = create_coupon_product(
                        benefit_type=cleaned_voucher_data['benefit_type'],
                        benefit_value=cleaned_voucher_data['benefit_value'],
                        catalog=cleaned_voucher_data['coupon_catalog'],
                        catalog_query=cleaned_voucher_data['catalog_query'],
                        category=cleaned_voucher_data['category'],
                        code=cleaned_voucher_data['code'],
                        course_catalog=cleaned_voucher_data['course_catalog'],
                        course_seat_types=cleaned_voucher_data['course_seat_types'],
                        email_domains=cleaned_voucher_data['email_domains'],
                        end_datetime=cleaned_voucher_data['end_datetime'],
                        enterprise_customer=cleaned_voucher_data['enterprise_customer'],
                        max_uses=cleaned_voucher_data['max_uses'],
                        note=cleaned_voucher_data['note'],
                        partner=cleaned_voucher_data['partner'],
                        price=cleaned_voucher_data['price'],
                        quantity=cleaned_voucher_data['quantity'],
                        start_datetime=cleaned_voucher_data['start_datetime'],
                        title=cleaned_voucher_data['title'],
                        voucher_type=cleaned_voucher_data['voucher_type'],
                    )
                except (KeyError, IntegrityError) as error:
                    return Response(str(error), status=status.HTTP_400_BAD_REQUEST)

                basket = prepare_basket(request, coupon_product)

                # Create an order now since payment is handled out of band via an invoice.
                client, __ = BusinessClient.objects.get_or_create(name=request.data.get('client'))
                invoice_data = self.create_update_data_dict(data=request.data, fields=Invoice.UPDATEABLE_INVOICE_FIELDS)
                response_data = self.create_order_for_invoice(
                    basket, coupon_id=coupon_product.id, client=client, invoice_data=invoice_data
                )

                return Response(response_data, status=status.HTTP_200_OK)
        except ValidationError as e:
            raise serializers.ValidationError(e.message)