コード例 #1
0
    def test_get_entitlement_voucher_with_invalid_entitlement_id(self):
        """
        Verify that method "get_entitlement_voucher" logs exception if there
        is no coupon against the provided entitlement id in the enterprise
        learner API response.
        """
        non_existing_coupon_id = 99
        self.mock_access_token_response()
        self.mock_enterprise_learner_api(
            catalog_id=non_existing_coupon_id, entitlement_id=non_existing_coupon_id
        )
        self.mock_enterprise_learner_entitlements_api(entitlement_id=non_existing_coupon_id)
        self.mock_catalog_contains_endpoint(
            discovery_api_url=self.site_configuration.discovery_api_url, catalog_id=non_existing_coupon_id,
            course_run_ids=[self.course.id]
        )

        logger_name = 'ecommerce.enterprise.entitlements'
        with LogCapture(logger_name) as logger:
            entitlement_voucher = get_entitlement_voucher(self.request, self.course.products.first())
            self._assert_num_requests(4)

            logger.check(
                (
                    logger_name,
                    'ERROR',
                    'There was an error getting coupon product with the entitlement id %s' % non_existing_coupon_id
                )
            )
            self.assertIsNone(entitlement_voucher)
コード例 #2
0
    def test_get_entitlement_voucher_with_invalid_entitlement_id(self):
        """
        Verify that method "get_entitlement_voucher" logs exception if there
        is no coupon against the provided entitlement id in the enterprise
        learner API response.
        """
        non_existing_coupon_id = 99
        self.mock_enterprise_learner_api(catalog_id=non_existing_coupon_id,
                                         entitlement_id=non_existing_coupon_id)

        catalog_query = '*:*'
        self.mock_course_discovery_api_for_catalog_by_resource_id(
            catalog_id=non_existing_coupon_id, catalog_query=catalog_query)
        self.mock_dynamic_catalog_contains_api(query=catalog_query,
                                               course_run_ids=[self.course.id])

        logger_name = 'ecommerce.enterprise.entitlements'
        with LogCapture(logger_name) as logger:
            entitlement_voucher = get_entitlement_voucher(
                self.request, self.course.products.first())
            self._assert_num_requests(3)

            logger.check((
                logger_name, 'ERROR',
                'There was an error getting coupon product with the entitlement id %s'
                % non_existing_coupon_id))
            self.assertIsNone(entitlement_voucher)
コード例 #3
0
    def test_get_entitlement_voucher_with_enterprise_feature_disabled(self):
        """
        Verify that method "get_entitlement_voucher" doesn't call the
        enterprise service API and returns no voucher if the enterprise
        feature is disabled.
        """
        self.mock_enterprise_learner_api()
        toggle_switch(settings.ENABLE_ENTERPRISE_ON_RUNTIME_SWITCH, False)

        entitlement_voucher = get_entitlement_voucher(self.request, self.course.products.first())
        self._assert_num_requests(0)
        self.assertIsNone(entitlement_voucher)
コード例 #4
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)
コード例 #5
0
    def test_get_entitlement_voucher_with_enterprise_feature_disabled(self):
        """
        Verify that method "get_entitlement_voucher" doesn't call the
        enterprise service API and returns no voucher if the enterprise
        feature is disabled.
        """
        self.mock_enterprise_learner_api()
        self.mock_enterprise_learner_entitlements_api()
        toggle_switch(settings.ENABLE_ENTERPRISE_ON_RUNTIME_SWITCH, False)

        entitlement_voucher = get_entitlement_voucher(self.request, self.course.products.first())
        self._assert_num_requests(0)
        self.assertIsNone(entitlement_voucher)
コード例 #6
0
    def test_get_entitlement_voucher_with_enterprise_feature_enabled(self):
        """
        Verify that method "get_entitlement_voucher" returns a voucher if
        the enterprise feature is enabled.
        """
        coupon = self.create_coupon(catalog=self.catalog)
        expected_voucher = coupon.attr.coupon_vouchers.vouchers.first()

        self.mock_enterprise_learner_api(entitlement_id=coupon.id)

        entitlement_voucher = get_entitlement_voucher(self.request, self.course.products.first())
        self._assert_num_requests(1)
        self.assertEqual(expected_voucher, entitlement_voucher)
コード例 #7
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)
コード例 #8
0
    def test_get_entitlement_voucher_with_enterprise_feature_enabled(self):
        """
        Verify that method "get_entitlement_voucher" returns a voucher if
        the enterprise feature is enabled.
        """
        coupon = self.create_coupon(catalog=self.catalog)
        expected_voucher = coupon.attr.coupon_vouchers.vouchers.first()

        catalog_query = '*:*'
        self.mock_enterprise_learner_api(entitlement_id=coupon.id)
        self.mock_enterprise_learner_entitlements_api(entitlement_id=coupon.id)
        self.mock_course_discovery_api_for_catalog_by_resource_id(catalog_query=catalog_query)
        self.mock_dynamic_catalog_contains_api(query=catalog_query, course_run_ids=[self.course.id])

        entitlement_voucher = get_entitlement_voucher(self.request, self.course.products.first())
        self._assert_num_requests(4)
        self.assertEqual(expected_voucher, entitlement_voucher)
コード例 #9
0
    def test_get_entitlement_voucher_with_enterprise_feature_enabled(self):
        """
        Verify that method "get_entitlement_voucher" returns a voucher if
        the enterprise feature is enabled.
        """
        self.mock_access_token_response()
        coupon = self.create_coupon(catalog=self.catalog)
        expected_voucher = coupon.attr.coupon_vouchers.vouchers.first()

        enterprise_catalog_id = 1
        self.mock_enterprise_learner_api(entitlement_id=coupon.id)
        self.mock_enterprise_learner_entitlements_api(entitlement_id=coupon.id)
        self.mock_catalog_contains_endpoint(
            discovery_api_url=self.site_configuration.discovery_api_url, catalog_id=enterprise_catalog_id,
            course_run_ids=[self.course.id]
        )

        entitlement_voucher = get_entitlement_voucher(self.request, self.course.products.first())
        self._assert_num_requests(4)
        self.assertEqual(expected_voucher, entitlement_voucher)
コード例 #10
0
    def test_get_entitlement_voucher_with_invalid_entitlement_id(self):
        """
        Verify that method "get_entitlement_voucher" logs exception if there
        is no coupon against the provided entitlement id in the enterprise
        learner API response.
        """
        non_existing_coupon_id = 99
        self.mock_enterprise_learner_api(entitlement_id=non_existing_coupon_id)

        logger_name = 'ecommerce.enterprise.entitlements'
        with LogCapture(logger_name) as logger:
            entitlement_voucher = get_entitlement_voucher(self.request, self.course.products.first())
            self._assert_num_requests(1)

            logger.check(
                (
                    logger_name,
                    'ERROR',
                    'There was an error getting coupon product with the entitlement id %s' % non_existing_coupon_id
                )
            )
            self.assertIsNone(entitlement_voucher)
コード例 #11
0
ファイル: baskets.py プロジェクト: jpmateo022/ecommerce
    def get(self, request):  # pylint: disable=too-many-statements
        """ Calculate basket totals given a list of sku's

        Create a temporary basket add the sku's and apply an optional voucher code.
        Then calculate the total price less discounts. If a voucher code is not
        provided apply a voucher in the Enterprise entitlements available
        to the user.

        Query Params:
            sku (string): A list of sku(s) to calculate
            code (string): Optional voucher code to apply to the basket.
            username (string): Optional username of a user for which to calculate the basket.

        Returns:
            JSON: {
                    'total_incl_tax_excl_discounts': basket.total_incl_tax_excl_discounts,
                    'total_incl_tax': basket.total_incl_tax,
                    'currency': basket.currency
                }
        """
        RequestCache.set(TEMPORARY_BASKET_CACHE_KEY,
                         True)  # TODO: LEARNER 5463

        partner = get_partner_for_site(request)
        skus = request.GET.getlist('sku')
        if not skus:
            return HttpResponseBadRequest(_('No SKUs provided.'))
        skus.sort()

        code = request.GET.get('code', None)
        try:
            voucher = Voucher.objects.get(code=code) if code else None
        except Voucher.DoesNotExist:
            voucher = None

        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)))

        # If there is only one product apply an Enterprise entitlement voucher
        if not voucher and len(products) == 1:
            voucher = get_entitlement_voucher(request, products[0])

        basket_owner = request.user

        requested_username = request.GET.get('username', default='')
        is_anonymous = request.GET.get('is_anonymous',
                                       'false').lower() == 'true'

        use_default_basket = is_anonymous

        # validate query parameters
        if requested_username and is_anonymous:
            return HttpResponseBadRequest(
                _('Provide username or is_anonymous query param, but not both')
            )
        elif not requested_username and not is_anonymous:
            logger.warning(
                "Request to Basket Calculate must supply either username or is_anonymous query"
                " param. Requesting user=%s. Future versions of this API will treat this "
                "WARNING as an ERROR and raise an exception.",
                basket_owner.username)
            requested_username = request.user.username

        # If a username is passed in, validate that the user has staff access or is the same user.
        if requested_username:
            if basket_owner.username.lower() == requested_username.lower():
                pass
            elif basket_owner.is_staff:
                try:
                    basket_owner = User.objects.get(
                        username=requested_username)
                except User.DoesNotExist:
                    # This case represents a user who is logged in to marketing, but
                    # doesn't yet have an account in ecommerce. These users have
                    # never purchased before.
                    use_default_basket = True
            else:
                return HttpResponseForbidden('Unauthorized user credentials')

        if basket_owner.username == self.MARKETING_USER and not use_default_basket:
            # For legacy requests that predate is_anonymous parameter, we will calculate
            # an anonymous basket if the calculated user is the marketing user.
            # TODO: LEARNER-5057: Remove this special case for the marketing user
            # once logs show no more requests with no parameters (see above).
            use_default_basket = True

        if use_default_basket:
            basket_owner = None

        cache_key = None
        if use_default_basket:
            # For an anonymous user we can directly get the cached price, because
            # there can't be any enrollments or entitlements.
            cache_key = get_cache_key(site_comain=request.site,
                                      resource_name='calculate',
                                      skus=skus)
            cached_response = TieredCache.get_cached_response(cache_key)
            if cached_response.is_hit:
                return Response(cached_response.value)

        if waffle.flag_is_active(
                request,
                "disable_calculate_temporary_basket_atomic_transaction"):
            response = self._calculate_temporary_basket(
                basket_owner, request, products, voucher, skus, code)
        else:
            response = self._calculate_temporary_basket_atomic(
                basket_owner, request, products, voucher, skus, code)

        if response and use_default_basket:
            TieredCache.set_all_tiers(
                cache_key, response,
                settings.ANONYMOUS_BASKET_CALCULATE_CACHE_TIMEOUT)

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

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

        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 not consent_failed and voucher is None:
            # Find and apply the enterprise entitlement on the learner basket. First, check two things:
            # 1. We don't already have an existing voucher parsed from a URL parameter
            # 2. The `consent_failed` URL parameter is falsey, or missing, meaning that we haven't already
            # attempted to apply an Enterprise voucher at least once, but the user rejected consent. Failing
            # to make that check would result in the user being repeatedly prompted to grant consent for the
            # same coupon they already declined consent on.
            voucher = get_entitlement_voucher(request, product)
            if voucher is not None:
                params = urlencode(
                    OrderedDict([
                        ('code', voucher.code),
                        ('sku', sku),
                        # This view does not handle getting data sharing consent. However, the coupon redemption
                        # view does. By adding the `failure_url` parameter, we're informing that view that, in the
                        # event required consent for a coupon can't be collected, the user ought to be directed
                        # back to this single-item basket view, with the `consent_failed` parameter applied so that
                        # we know not to try to apply the enterprise coupon again.
                        (
                            'failure_url',
                            request.build_absolute_uri(
                                '{path}?{params}'.format(
                                    path=reverse('basket:single-item'),
                                    params=urlencode(
                                        OrderedDict([
                                            (CONSENT_FAILED_PARAM, True),
                                            ('sku', sku),
                                        ])))),
                        ),
                    ]))
                return HttpResponseRedirect('{path}?{params}'.format(
                    path=reverse('coupons:redeem'), params=params))

        # 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.course.name)
            return render(request, 'edx/error.html', {'error': msg})
        return HttpResponseRedirect(reverse('basket:summary'), status=303)
コード例 #13
0
ファイル: baskets.py プロジェクト: zerojuls/ecommerce
    def get(self, request):
        """ Calculate basket totals given a list of sku's

        Create a temporary basket add the sku's and apply an optional voucher code.
        Then calculate the total price less discounts. If a voucher code is not
        provided apply a voucher in the Enterprise entitlements available
        to the user.

        Arguments:
            sku (string): A list of sku(s) to calculate
            code (string): Optional voucher code to apply to the basket.
            username (string): Optional username of a user for which to caclulate the basket.

        Returns:
            JSON: {
                    'total_incl_tax_excl_discounts': basket.total_incl_tax_excl_discounts,
                    'total_incl_tax': basket.total_incl_tax,
                    'currency': basket.currency
                }
        """
        partner = get_partner_for_site(request)
        skus = request.GET.getlist('sku')
        if not skus:
            return HttpResponseBadRequest(_('No SKUs provided.'))

        code = request.GET.get('code', None)
        try:
            voucher = Voucher.objects.get(code=code) if code else None
        except Voucher.DoesNotExist:
            voucher = None

        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)))

        # If there is only one product apply an Enterprise entitlement voucher
        if not voucher and len(products) == 1:
            voucher = get_entitlement_voucher(request, products[0])

        username = request.GET.get('username', default='')
        user = request.user
        # If a username is passed in, validate that the user has staff access or is the same user.
        if username:
            if user.is_staff or (user.username.lower() == username.lower()):
                try:
                    user = User.objects.get(username=username)
                except User.DoesNotExist:
                    logger.debug('Request username: [%s] does not exist',
                                 username)
            else:
                return HttpResponseForbidden('Unauthorized user credentials')

        # We wrap this in an atomic operation so we never commit this to the db.
        # This is to avoid merging this temporary basket with a real user basket.
        try:
            with transaction.atomic():
                basket = Basket(owner=user, site=request.site)
                basket.strategy = Selector().strategy(user=user)

                for product in products:
                    basket.add_product(product, 1)

                if voucher:
                    basket.vouchers.add(voucher)

                # Calculate any discounts on the basket.
                Applicator().apply(basket, user=user, request=request)

                discounts = []
                if basket.offer_discounts:
                    discounts = basket.offer_discounts
                if basket.voucher_discounts:
                    discounts.extend(basket.voucher_discounts)

                response = {
                    'total_incl_tax_excl_discounts':
                    basket.total_incl_tax_excl_discounts,
                    'total_incl_tax': basket.total_incl_tax,
                    'currency': basket.currency
                }
                raise api_exceptions.TemporaryBasketException
        except api_exceptions.TemporaryBasketException:
            pass
        except:  # pylint: disable=bare-except
            logger.exception(
                'Failed to calculate basket discount for SKUs [%s] and voucher [%s].',
                skus, code)
            raise

        return Response(response)
コード例 #14
0
    def get(self, request):
        """ Calculate basket totals given a list of sku's

        Create a temporary basket add the sku's and apply an optional voucher code.
        Then calculate the total price less discounts. If a voucher code is not
        provided apply a voucher in the Enterprise entitlements available
        to the user.

        Arguments:
            sku (string): A list of sku(s) to calculate
            code (string): Optional voucher code to apply to the basket.
            username (string): Optional username of a user for which to caclulate the basket.

        Returns:
            JSON: {
                    'total_incl_tax_excl_discounts': basket.total_incl_tax_excl_discounts,
                    'total_incl_tax': basket.total_incl_tax,
                    'currency': basket.currency
                }
        """
        partner = get_partner_for_site(request)
        skus = request.GET.getlist('sku')
        if not skus:
            return HttpResponseBadRequest(_('No SKUs provided.'))

        code = request.GET.get('code', None)
        try:
            voucher = Voucher.objects.get(code=code) if code else None
        except Voucher.DoesNotExist:
            voucher = None

        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)))

        # If there is only one product apply an Enterprise entitlement voucher
        if not voucher and len(products) == 1:
            voucher = get_entitlement_voucher(request, products[0])

        username = request.GET.get('username', default='')
        user = request.user

        # True if this request was made by the marketing user, and does not include a username
        # query param, and thus is calculating the non-logged in (anonymous) price.
        # Note: We need to verify separately that all calls without a username query param
        # can be treated in this same way.
        is_marketing_anonymous_request = False

        # If a username is passed in, validate that the user has staff access or is the same user.
        if username:
            if user.is_staff or (user.username.lower() == username.lower()):
                try:
                    user = User.objects.get(username=username)
                except User.DoesNotExist:
                    logger.debug('Request username: [%s] does not exist',
                                 username)
            else:
                return HttpResponseForbidden('Unauthorized user credentials')
        elif user.username == self.MARKETING_USER:
            is_marketing_anonymous_request = True

        cache_key = None
        # Since we know we can't have any enrollments or entitlements, we can directly get
        # the cached price.
        if is_marketing_anonymous_request:
            cache_key = get_cache_key(site_comain=request.site,
                                      resource_name='calculate',
                                      skus=skus)
            if waffle.flag_is_active(
                    request, "use_cached_basket_calculate_for_marketing_user"):
                basket_calculate_results = cache.get(cache_key)
                if basket_calculate_results:
                    return Response(basket_calculate_results)

        response = self._calculate_basket(user, request, products, voucher,
                                          skus, code)

        if response and is_marketing_anonymous_request:
            cache.set(cache_key, response,
                      settings.ANONYMOUS_BASKET_CALCULATE_CACHE_TIMEOUT)

        return Response(response)