Exemplo n.º 1
0
 def _verify_enterprise_needs(self, voucher, code, stock_record):
     if get_enterprise_customer_from_voucher(self.request.site, voucher) is not None:
         # The below lines only apply if the voucher that was entered is attached
         # to an EnterpriseCustomer. If that's the case, then rather than following
         # the standard redemption flow, we kick the user out to the `redeem` flow.
         # This flow will handle any additional information that needs to be gathered
         # due to the fact that the voucher is attached to an Enterprise Customer.
         params = urllib.parse.urlencode(
             OrderedDict([
                 ('code', code),
                 ('sku', stock_record.partner_sku),
                 ('failure_url', self.request.build_absolute_uri(
                     '{path}?{params}'.format(
                         path=reverse('basket:summary'),
                         params=urllib.parse.urlencode(
                             {
                                 CONSENT_FAILED_PARAM: code
                             }
                         )
                     )
                 ))
             ])
         )
         redirect_response = HttpResponseRedirect(
             self.request.build_absolute_uri(
                 '{path}?{params}'.format(
                     path=reverse('coupons:redeem'),
                     params=params
                 )
             )
         )
         raise RedirectException(response=redirect_response)
Exemplo n.º 2
0
    def form_valid(self, form):
        code = form.cleaned_data['code']
        if self.request.basket.is_empty:
            return redirect_to_referrer(self.request, 'basket:summary')
        if self.request.basket.contains_voucher(code):
            messages.error(
                self.request,
                _("You have already added coupon code '{code}' to your basket.").format(code=code)
            )
        else:
            try:
                voucher = self.voucher_model._default_manager.get(code=code)  # pylint: disable=protected-access
            except self.voucher_model.DoesNotExist:
                messages.error(
                    self.request,
                    _("Coupon code '{code}' does not exist.").format(code=code)
                )
            else:
                basket_lines = self.request.basket.all_lines()

                # TODO: for multiline baskets, select the StockRecord for the product associated
                # specifically with the code that was submitted.
                stock_record = basket_lines[0].stockrecord

                offer = voucher.best_offer
                product = stock_record.product
                email_confirmation_response = render_email_confirmation_if_required(self.request, offer, product)
                if email_confirmation_response:
                    return email_confirmation_response

                if get_enterprise_customer_from_voucher(self.request.site, voucher) is not None:
                    # The below lines only apply if the voucher that was entered is attached
                    # to an EnterpriseCustomer. If that's the case, then rather than following
                    # the standard redemption flow, we kick the user out to the `redeem` flow.
                    # This flow will handle any additional information that needs to be gathered
                    # due to the fact that the voucher is attached to an Enterprise Customer.
                    params = urlencode(
                        {
                            'code': code,
                            'sku': stock_record.partner_sku,
                            'failure_url': self.request.build_absolute_uri(
                                '{path}?{params}'.format(
                                    path=reverse('basket:summary'),
                                    params=urlencode(
                                        {
                                            CONSENT_FAILED_PARAM: code
                                        }
                                    )
                                )
                            ),
                        }
                    )
                    return HttpResponseRedirect(
                        '{path}?{params}'.format(
                            path=reverse('coupons:redeem'),
                            params=params
                        )
                    )
                self.apply_voucher_to_basket(voucher)
        return redirect_to_referrer(self.request, 'basket:summary')
Exemplo n.º 3
0
    def form_valid(self, form):
        code = form.cleaned_data['code']
        error_message = self.check_for_empty_basket_error(code)
        if error_message:
            return redirect_to_referrer(self.request, 'basket:summary')

        error_message = self.check_for_already_applied_voucher_error(code)
        if error_message:
            messages.error(self.request, error_message['user_message'])
            return redirect_to_referrer(self.request, 'basket:summary')

        voucher, error_message = self.get_voucher_from_code(code)
        if error_message:
            messages.error(self.request, error_message['user_message'])
            return redirect_to_referrer(self.request, 'basket:summary')

        basket_lines = self.request.basket.all_lines()

        # TODO: for multiline baskets, select the StockRecord for the product associated
        # specifically with the code that was submitted.
        stock_record = basket_lines[0].stockrecord

        offer = voucher.best_offer
        product = stock_record.product
        # TODO: ARCH-955: share this with VoucherAddApiView
        email_confirmation_response = render_email_confirmation_if_required(
            self.request, offer, product)
        if email_confirmation_response:
            return email_confirmation_response

        # TODO: how do we deal with this and the A/B experiment?
        # TODO: ARCH-956: share this with VoucherAddApiView
        if get_enterprise_customer_from_voucher(self.request.site,
                                                voucher) is not None:
            # The below lines only apply if the voucher that was entered is attached
            # to an EnterpriseCustomer. If that's the case, then rather than following
            # the standard redemption flow, we kick the user out to the `redeem` flow.
            # This flow will handle any additional information that needs to be gathered
            # due to the fact that the voucher is attached to an Enterprise Customer.
            params = urlencode({
                'code':
                code,
                'sku':
                stock_record.partner_sku,
                'failure_url':
                self.request.build_absolute_uri('{path}?{params}'.format(
                    path=reverse('basket:summary'),
                    params=urlencode({CONSENT_FAILED_PARAM: code}))),
            })
            return HttpResponseRedirect('{path}?{params}'.format(
                path=reverse('coupons:redeem'), params=params))

        message = self.apply_voucher_to_basket(voucher)
        self._set_flash_messages(message)
        return redirect_to_referrer(self.request, 'basket:summary')
Exemplo n.º 4
0
def enterprise_customer_for_voucher(context, voucher):
    """
    Retrieve enterprise customer associated with the given voucher.

    Raises:
        EnterpriseDoesNotExist: Voucher is not associated with any enterprise customer.
    """
    if voucher and context and 'request' in context:
        request = context['request']
    else:
        return None

    try:
        return utils.get_enterprise_customer_from_voucher(request.site, voucher)
    except EnterpriseDoesNotExist:
        return None
Exemplo n.º 5
0
    def get(self, request):  # pylint: disable=too-many-statements
        """
        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')
        failure_url = request.GET.get('failure_url')
        site_configuration = request.site.siteconfiguration

        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:
            logger.warning(
                '[Code Redemption Failure] The voucher is not valid for this product. '
                'User: %s, Product: %s, Code: %s, Message: %s',
                request.user.username, product.id, voucher.code, msg)
            return render(request, template_name, {'error': msg})

        offer = voucher.best_offer
        if not offer.is_email_valid(request.user.email):
            logger.warning(
                '[Code Redemption Failure] Unable to apply offer because the user\'s email '
                'does not meet the domain requirements. '
                'User: %s, Offer: %s, Code: %s', request.user.username,
                offer.id, voucher.code)
            return render(
                request, template_name,
                {'error': _('You are not eligible to use this coupon.')})

        email_confirmation_response = get_redirect_to_email_confirmation_if_required(
            request, offer, product)
        if email_confirmation_response:
            return email_confirmation_response

        try:
            enterprise_customer = get_enterprise_customer_from_voucher(
                request.site, voucher)
        except EnterpriseDoesNotExist as e:
            # If an EnterpriseException is caught while pulling the EnterpriseCustomer, that means there's no
            # corresponding EnterpriseCustomer in the Enterprise service (which should never happen).
            logger.exception(six.text_type(e))
            return render(
                request, template_name, {
                    'error':
                    _('Couldn\'t find a matching Enterprise Customer for this coupon.'
                      )
                })

        if enterprise_customer and product.is_course_entitlement_product:
            return render(
                request, template_name, {
                    'error':
                    _('This coupon code is not valid for entitlement course product. Try a different course.'
                      )
                })

        if enterprise_customer is not None and enterprise_customer_user_needs_consent(
                request.site,
                enterprise_customer['id'],
                product.course.id,
                request.user.username,
        ):
            consent_token = get_enterprise_customer_data_sharing_consent_token(
                request.user.access_token, product.course.id,
                enterprise_customer['id'])
            received_consent_token = request.GET.get('consent_token')
            if received_consent_token:
                # If the consent token is set, then the user is returning from the consent view. Render out an error
                # if the computed token doesn't match the one received from the redirect URL.
                if received_consent_token != consent_token:
                    logger.warning(
                        '[Code Redemption Failure] Unable to complete code redemption because of '
                        'invalid consent. User: %s, Offer: %s, Code: %s',
                        request.user.username, offer.id, voucher.code)
                    return render(request, template_name, {
                        'error':
                        _('Invalid data sharing consent token provided.')
                    })
            else:
                # The user hasn't been redirected to the interstitial consent view to collect consent, so
                # redirect them now.
                redirect_url = get_enterprise_course_consent_url(
                    request.site,
                    code,
                    sku,
                    consent_token,
                    product.course.id,
                    enterprise_customer['id'],
                    failure_url=failure_url)
                return HttpResponseRedirect(redirect_url)

        try:
            basket = prepare_basket(request, [product], voucher)
        except AlreadyPlacedOrderException:
            msg = _('You have already purchased {course} seat.').format(
                course=product.course.name)
            return render(request, template_name, {'error': msg})

        if basket.total_excl_tax == 0:
            try:
                order = self.place_free_order(basket)
                return HttpResponseRedirect(
                    get_receipt_page_url(
                        site_configuration,
                        order.number,
                        disable_back_button=True,
                    ), )
            except:  # pylint: disable=bare-except
                logger.exception(
                    'Failed to create a free order for basket [%d]', basket.id)
                return absolute_redirect(self.request, 'checkout:error')

        if enterprise_customer:
            if is_voucher_applied(basket, voucher):
                message = _(
                    'A discount has been applied, courtesy of {enterprise_customer_name}.'
                ).format(
                    enterprise_customer_name=enterprise_customer.get('name'))
                messages.info(self.request, message)
            else:
                # Display a generic message to the user if a condition-specific
                # message has not already been added by an unsatified Condition class.
                if not messages.get_messages(self.request):
                    messages.warning(
                        self.request,
                        _('This coupon code is not valid for this course. Try a different course.'
                          ))
                self.request.basket.vouchers.remove(voucher)

        # The coupon_redeem_redirect query param is used to communicate to the Payment MFE that it may redirect
        # and should not display the payment form before making that determination.
        # TODO: It would be cleaner if the user could be redirected to their final destination up front.
        redirect_url = get_payment_microfrontend_or_basket_url(
            self.request) + "?coupon_redeem_redirect=1"
        return HttpResponseRedirect(redirect_url)
Exemplo n.º 6
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_error.html'
        code = request.GET.get('code')
        sku = request.GET.get('sku')
        failure_url = request.GET.get('failure_url')
        site_configuration = request.site.siteconfiguration

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

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

        email_confirmation_response = render_email_confirmation_if_required(
            request, offer, product)
        if email_confirmation_response:
            return email_confirmation_response

        try:
            enterprise_customer = get_enterprise_customer_from_voucher(
                request.site, voucher)
        except EnterpriseDoesNotExist as e:
            # If an EnterpriseException is caught while pulling the EnterpriseCustomer, that means there's no
            # corresponding EnterpriseCustomer in the Enterprise service (which should never happen).
            logger.exception(e.message)
            return render(
                request, template_name, {
                    'error':
                    _('Couldn\'t find a matching Enterprise Customer for this coupon.'
                      )
                })

        if enterprise_customer is not None and enterprise_customer_user_needs_consent(
                request.site,
                enterprise_customer['id'],
                product.course.id,
                request.user.username,
        ):
            consent_token = get_enterprise_customer_data_sharing_consent_token(
                request.user.access_token, product.course.id,
                enterprise_customer['id'])
            received_consent_token = request.GET.get('consent_token')
            if received_consent_token:
                # If the consent token is set, then the user is returning from the consent view. Render out an error
                # if the computed token doesn't match the one received from the redirect URL.
                if received_consent_token != consent_token:
                    return render(request, template_name, {
                        'error':
                        _('Invalid data sharing consent token provided.')
                    })
            else:
                # The user hasn't been redirected to the interstitial consent view to collect consent, so
                # redirect them now.
                redirect_url = get_enterprise_course_consent_url(
                    request.site,
                    code,
                    sku,
                    consent_token,
                    product.course.id,
                    enterprise_customer['id'],
                    failure_url=failure_url)
                return HttpResponseRedirect(redirect_url)

        try:
            basket = prepare_basket(request, [product], voucher)
        except AlreadyPlacedOrderException:
            msg = _('You have already purchased {course} seat.').format(
                course=product.course.name)
            return render(request, template_name, {'error': msg})

        if basket.total_excl_tax == 0:
            try:
                order = self.place_free_order(basket)
                return HttpResponseRedirect(
                    get_receipt_page_url(site_configuration, order.number))
            except:  # pylint: disable=bare-except
                logger.exception(
                    'Failed to create a free order for basket [%d]', basket.id)
                return HttpResponseRedirect(reverse('checkout:error'))

        if enterprise_customer:
            if is_voucher_applied(basket, voucher):
                message = _(
                    'A discount has been applied, courtesy of {enterprise_customer_name}.'
                ).format(
                    enterprise_customer_name=enterprise_customer.get('name'))
                message = '<i class="fa fa-info-circle"></i> {}'.format(
                    message)
                messages.info(self.request, message, extra_tags='safe')
            else:
                messages.warning(
                    self.request,
                    _('This coupon code is not valid for this course. Try a different course.'
                      ))
                self.request.basket.vouchers.remove(voucher)

        return HttpResponseRedirect(reverse('basket:summary'))