示例#1
0
    def is_satisfied(self, offer, basket):  # pylint: disable=unused-argument
        """
        Determines if a user is eligible for an enterprise customer offer
        based on their association with the enterprise customer.

        Args:
            basket (Basket): Contains information about order line items, the current site,
                             and the user attempting to make the purchase.
        Returns:
            bool
        """
        if not basket.owner:
            # An anonymous user is never linked to any EnterpriseCustomer.
            return False

        try:
            learner_data = fetch_enterprise_learner_data(
                basket.site, basket.owner)['results'][0]
        except (ConnectionError, KeyError, SlumberHttpBaseException, Timeout):
            logger.exception(
                'Failed to retrieve enterprise learner data for site [%s] and user [%s].',
                basket.site.domain,
                basket.owner.username,
            )
            return False
        except IndexError:
            return False

        enterprise_customer = learner_data['enterprise_customer']
        if str(self.enterprise_customer_uuid) != enterprise_customer['uuid']:
            # Learner is not linked to the EnterpriseCustomer associated with this condition.
            return False

        course_run_ids = []
        for line in basket.all_lines():
            course = line.product.course
            if not course:
                # Basket contains products not related to a course_run.
                return False

            course_run_ids.append(course.id)

        if not catalog_contains_course_runs(
                basket.site,
                course_run_ids,
                self.enterprise_customer_uuid,
                enterprise_customer_catalog_uuid=self.
                enterprise_customer_catalog_uuid):
            # Basket contains course runs that do not exist in the EnterpriseCustomerCatalogs
            # associated with the EnterpriseCustomer.
            return False

        return True
示例#2
0
    def _assert_contains_course_runs(self, expected, course_run_ids, enterprise_customer_uuid,
                                     enterprise_customer_catalog_uuid):
        """
        Helper method to validate the response from the method `catalog_contains_course_runs`.
        """
        actual = enterprise_api.catalog_contains_course_runs(
            self.site,
            course_run_ids,
            enterprise_customer_uuid,
            enterprise_customer_catalog_uuid=enterprise_customer_catalog_uuid,
        )

        self.assertEqual(expected, actual)
示例#3
0
    def is_satisfied(self, offer, basket):  # pylint: disable=unused-argument
        """
        Determines if a user is eligible for an enterprise customer offer
        based on their association with the enterprise customer.

        It also filter out the offer if the `enterprise_customer_catalog_uuid`
        value set on the offer condition does not match with the basket catalog
        value when explicitly provided by the enterprise learner.

        Note: Currently there is no mechanism to prioritize or apply multiple
        offers that may apply as opposed to disqualifying offers if the
        catalog doesn't explicitly match.

        Arguments:
            basket (Basket): Contains information about order line items, the current site,
                             and the user attempting to make the purchase.
        Returns:
            bool
        """
        if not basket.owner:
            # An anonymous user is never linked to any EnterpriseCustomer.
            return False

        enterprise_customer = str(self.enterprise_customer_uuid)
        enterprise_catalog = str(self.enterprise_customer_catalog_uuid)
        username = basket.owner.username
        course_run_ids = []
        for line in basket.all_lines():
            course = line.product.course
            if not course:
                # Basket contains products not related to a course_run.
                # Only log for non-site offers to avoid noise.
                if offer.offer_type != ConditionalOffer.SITE:
                    logger.warning(
                        '[Code Redemption Failure] Unable to apply enterprise offer because '
                        'the Basket contains a product not related to a course_run. '
                        'User: %s, Offer: %s, Product: %s, Enterprise: %s, Catalog: %s',
                        username, offer.id, line.product.id,
                        enterprise_customer, enterprise_catalog)
                return False

            course_run_ids.append(course.id)

        courses_in_basket = ','.join(course_run_ids)
        learner_data = {}
        try:
            learner_data = fetch_enterprise_learner_data(
                basket.site, basket.owner)['results'][0]
        except (ConnectionError, KeyError, SlumberHttpBaseException,
                Timeout) as exc:
            logger.exception(
                '[Code Redemption Failure] Unable to apply enterprise offer because '
                'we failed to retrieve enterprise learner data for the user. '
                'User: %s, Offer: %s, Message: %s, Enterprise: %s, Catalog: %s, Courses: %s',
                username, offer.id, exc, enterprise_customer,
                enterprise_catalog, courses_in_basket)
            return False
        except IndexError:
            if offer.offer_type == ConditionalOffer.SITE:
                logger.debug(
                    'Unable to apply enterprise site offer %s because no learner data was returned for user %s',
                    offer.id, basket.owner)
                return False

        if (learner_data and 'enterprise_customer' in learner_data
                and enterprise_customer !=
                learner_data['enterprise_customer']['uuid']):
            # Learner is not linked to the EnterpriseCustomer associated with this condition.
            if offer.offer_type == ConditionalOffer.VOUCHER:
                logger.warning(
                    '[Code Redemption Failure] Unable to apply enterprise offer because Learner\'s '
                    'enterprise (%s) does not match this conditions\'s enterprise (%s). '
                    'User: %s, Offer: %s, Enterprise: %s, Catalog: %s, Courses: %s',
                    learner_data['enterprise_customer']['uuid'],
                    enterprise_customer, username, offer.id,
                    enterprise_customer, enterprise_catalog, courses_in_basket)
            return False

        # Verify that the current conditional offer is related to the provided
        # enterprise catalog, this will also filter out offers which don't
        # have `enterprise_customer_catalog_uuid` value set on the condition.
        catalog = self._get_enterprise_catalog_uuid_from_basket(basket)
        if catalog:
            if offer.condition.enterprise_customer_catalog_uuid != catalog:
                logger.warning(
                    'Unable to apply enterprise offer %s because '
                    'Enterprise catalog id on the basket (%s) '
                    'does not match the catalog for this condition (%s).',
                    offer.id, catalog,
                    offer.condition.enterprise_customer_catalog_uuid)
                return False

        try:
            catalog_contains_course = catalog_contains_course_runs(
                basket.site,
                course_run_ids,
                enterprise_customer,
                enterprise_customer_catalog_uuid=enterprise_catalog)
        except (ConnectionError, KeyError, SlumberHttpBaseException,
                Timeout) as exc:
            logger.exception(
                '[Code Redemption Failure] Unable to apply enterprise offer because '
                'we failed to check if course_runs exist in the catalog. '
                'User: %s, Offer: %s, Message: %s, Enterprise: %s, Catalog: %s, Courses: %s',
                username, offer.id, exc, enterprise_customer,
                enterprise_catalog, courses_in_basket)
            return False

        if not catalog_contains_course:
            # Basket contains course runs that do not exist in the EnterpriseCustomerCatalogs
            # associated with the EnterpriseCustomer.
            logger.warning(
                '[Code Redemption Failure] Unable to apply enterprise offer because '
                'Enterprise catalog does not contain the course(s) in this basket. '
                'User: %s, Offer: %s, Enterprise: %s, Catalog: %s, Courses: %s',
                username, offer.id, enterprise_customer, enterprise_catalog,
                courses_in_basket)
            return False

        return True
示例#4
0
    def is_satisfied(self, offer, basket):  # pylint: disable=unused-argument
        """
        Determines if a user is eligible for an enterprise customer offer
        based on their association with the enterprise customer.

        It also filter out the offer if the `enterprise_customer_catalog_uuid`
        value set on the offer condition does not match with the basket catalog
        value when explicitly provided by the enterprise learner.

        Note: Currently there is no mechanism to prioritize or apply multiple
        offers that may apply as opposed to disqualifying offers if the
        catalog doesn't explicitly match.

        Arguments:
            basket (Basket): Contains information about order line items, the current site,
                             and the user attempting to make the purchase.
        Returns:
            bool
        """
        if not basket.owner:
            # An anonymous user is never linked to any EnterpriseCustomer.
            return False

        if offer.offer_type == ConditionalOffer.VOUCHER:
            logger.info(
                'Skipping Voucher type enterprise conditional offer until we are ready to support it.'
            )
            return False

        try:
            learner_data = fetch_enterprise_learner_data(
                basket.site, basket.owner)['results'][0]
        except (ConnectionError, KeyError, SlumberHttpBaseException, Timeout):
            logger.exception(
                'Failed to retrieve enterprise learner data for site [%s] and user [%s].',
                basket.site.domain,
                basket.owner.username,
            )
            return False
        except IndexError:
            return False

        enterprise_customer = learner_data['enterprise_customer']
        if str(self.enterprise_customer_uuid) != enterprise_customer['uuid']:
            # Learner is not linked to the EnterpriseCustomer associated with this condition.
            return False

        course_run_ids = []
        for line in basket.all_lines():
            course = line.product.course
            if not course:
                # Basket contains products not related to a course_run.
                return False

            course_run_ids.append(course.id)

        # Verify that the current conditional offer is related to the provided
        # enterprise catalog, this will also filter out offers which don't
        # have `enterprise_customer_catalog_uuid` value set on the condition.
        catalog = self._get_enterprise_catalog_uuid_from_basket(basket)
        if catalog:
            if offer.condition.enterprise_customer_catalog_uuid != catalog:
                return False

        if not catalog_contains_course_runs(
                basket.site,
                course_run_ids,
                self.enterprise_customer_uuid,
                enterprise_customer_catalog_uuid=self.
                enterprise_customer_catalog_uuid):
            # Basket contains course runs that do not exist in the EnterpriseCustomerCatalogs
            # associated with the EnterpriseCustomer.
            return False

        return True
示例#5
0
    def is_satisfied(self, offer, basket):  # pylint: disable=unused-argument
        """
        Determines if a user is eligible for an enterprise customer offer
        based on their association with the enterprise customer.

        It also filter out the offer if the `enterprise_customer_catalog_uuid`
        value set on the offer condition does not match with the basket catalog
        value when explicitly provided by the enterprise learner.

        Note: Currently there is no mechanism to prioritize or apply multiple
        offers that may apply as opposed to disqualifying offers if the
        catalog doesn't explicitly match.

        Arguments:
            basket (Basket): Contains information about order line items, the current site,
                             and the user attempting to make the purchase.
        Returns:
            bool
        """
        if not basket.owner:
            # An anonymous user is never linked to any EnterpriseCustomer.
            return False

        if (offer.offer_type == ConditionalOffer.VOUCHER
                and not waffle.switch_is_active(
                    ENTERPRISE_OFFERS_FOR_COUPONS_SWITCH)):
            logger.info(
                'Skipping Voucher type enterprise conditional offer until we are ready to support it.'
            )
            return False

        learner_data = {}
        try:
            learner_data = fetch_enterprise_learner_data(
                basket.site, basket.owner)['results'][0]
        except (ConnectionError, KeyError, SlumberHttpBaseException, Timeout):
            logger.exception(
                'Failed to retrieve enterprise learner data for site [%s] and user [%s].',
                basket.site.domain,
                basket.owner.username,
            )
            return False
        except IndexError:
            if offer.offer_type == ConditionalOffer.SITE:
                logger.debug(
                    'Unable to apply enterprise site offer %s because no learner data was returned for user %s',
                    offer.id, basket.owner)
                return False

        if (learner_data and 'enterprise_customer' in learner_data
                and str(self.enterprise_customer_uuid) !=
                learner_data['enterprise_customer']['uuid']):
            # Learner is not linked to the EnterpriseCustomer associated with this condition.
            logger.debug(
                'Unable to apply enterprise offer %s because Learner\'s enterprise (%s)'
                'does not match this conditions\'s enterprise (%s).', offer.id,
                learner_data['enterprise_customer']['uuid'],
                str(self.enterprise_customer_uuid))
            return False

        course_run_ids = []
        for line in basket.all_lines():
            course = line.product.course
            if not course:
                # Basket contains products not related to a course_run.
                logger.warning(
                    'Unable to apply enterprise offer %s because '
                    'the Basket (#%s) contains a product (#%s) not related to a course_run.',
                    offer.id, basket.id, line.product.id)
                return False

            course_run_ids.append(course.id)

        # Verify that the current conditional offer is related to the provided
        # enterprise catalog, this will also filter out offers which don't
        # have `enterprise_customer_catalog_uuid` value set on the condition.
        catalog = self._get_enterprise_catalog_uuid_from_basket(basket)
        if catalog:
            if offer.condition.enterprise_customer_catalog_uuid != catalog:
                logger.warning(
                    'Unable to apply enterprise offer %s because '
                    'Enterprise catalog id on the basket (%s) '
                    'does not match the catalog for this condition (%s).',
                    offer.id, catalog,
                    offer.condition.enterprise_customer_catalog_uuid)
                return False

        if not catalog_contains_course_runs(
                basket.site,
                course_run_ids,
                self.enterprise_customer_uuid,
                enterprise_customer_catalog_uuid=self.
                enterprise_customer_catalog_uuid):
            # Basket contains course runs that do not exist in the EnterpriseCustomerCatalogs
            # associated with the EnterpriseCustomer.
            logger.warning(
                'Unable to apply enterprise offer %s because '
                'Enterprise catalog (%s) does not contain the course(s) (%s) in this basket.',
                offer.id, self.enterprise_customer_catalog_uuid,
                ','.join(course_run_ids))
            return False

        return True
示例#6
0
    def is_satisfied(self, offer, basket):  # pylint: disable=unused-argument
        """
        Determines if a user is eligible for an enterprise customer offer
        based on their association with the enterprise customer.

        It also filter out the offer if the `enterprise_customer_catalog_uuid`
        value set on the offer condition does not match with the basket catalog
        value when explicitly provided by the enterprise learner.

        Note: Currently there is no mechanism to prioritize or apply multiple
        offers that may apply as opposed to disqualifying offers if the
        catalog doesn't explicitly match.

        Arguments:
            basket (Basket): Contains information about order line items, the current site,
                             and the user attempting to make the purchase.
        Returns:
            bool
        """
        if not basket.owner:
            # An anonymous user is never linked to any EnterpriseCustomer.
            return False

        enterprise_in_condition = str(self.enterprise_customer_uuid)
        enterprise_catalog = str(self.enterprise_customer_catalog_uuid) if self.enterprise_customer_catalog_uuid \
            else None
        enterprise_name_in_condition = str(self.enterprise_customer_name)
        username = basket.owner.username
        course_run_ids = []
        for line in basket.all_lines():
            course = line.product.course
            if not course:
                # Basket contains products not related to a course_run.
                # Only log for non-site offers to avoid noise.
                if offer.offer_type != ConditionalOffer.SITE:
                    logger.warning(
                        '[Code Redemption Failure] Unable to apply enterprise offer because '
                        'the Basket contains a product not related to a course_run. '
                        'User: %s, Offer: %s, Product: %s, Enterprise: %s, Catalog: %s',
                        username, offer.id, line.product.id,
                        enterprise_in_condition, enterprise_catalog)
                return False

            course_run_ids.append(course.id)

        courses_in_basket = ','.join(course_run_ids)
        user_enterprise = get_enterprise_id_for_user(basket.site, basket.owner)
        if user_enterprise and enterprise_in_condition != user_enterprise:
            # Learner is not linked to the EnterpriseCustomer associated with this condition.
            if offer.offer_type == ConditionalOffer.VOUCHER:
                logger.warning(
                    '[Code Redemption Failure] Unable to apply enterprise offer because Learner\'s '
                    'enterprise (%s) does not match this conditions\'s enterprise (%s). '
                    'User: %s, Offer: %s, Enterprise: %s, Catalog: %s, Courses: %s',
                    user_enterprise, enterprise_in_condition, username,
                    offer.id, enterprise_in_condition, enterprise_catalog,
                    courses_in_basket)

                logger.info(
                    '[Code Redemption Issue] Linking learner with the enterprise in Condition. '
                    'User [%s], Enterprise [%s]', username,
                    enterprise_in_condition)
                get_or_create_enterprise_customer_user(
                    basket.site, enterprise_in_condition, username, False)
                msg = _(
                    'This coupon has been made available through {new_enterprise}. '
                    'To redeem this coupon, you must first logout. When you log back in, '
                    'please select {new_enterprise} as your enterprise '
                    'and try again.').format(
                        new_enterprise=enterprise_name_in_condition)
                messages.warning(
                    crum.get_current_request(),
                    msg,
                )

            return False

        # Verify that the current conditional offer is related to the provided
        # enterprise catalog, this will also filter out offers which don't
        # have `enterprise_customer_catalog_uuid` value set on the condition.
        catalog = self._get_enterprise_catalog_uuid_from_basket(basket)
        if catalog:
            if offer.condition.enterprise_customer_catalog_uuid != catalog:
                logger.warning(
                    'Unable to apply enterprise offer %s because '
                    'Enterprise catalog id on the basket (%s) '
                    'does not match the catalog for this condition (%s).',
                    offer.id, catalog,
                    offer.condition.enterprise_customer_catalog_uuid)
                return False

        try:
            catalog_contains_course = catalog_contains_course_runs(
                basket.site,
                course_run_ids,
                enterprise_in_condition,
                enterprise_customer_catalog_uuid=enterprise_catalog)
        except (ReqConnectionError, KeyError, SlumberHttpBaseException,
                Timeout) as exc:
            logger.exception(
                '[Code Redemption Failure] Unable to apply enterprise offer because '
                'we failed to check if course_runs exist in the catalog. '
                'User: %s, Offer: %s, Message: %s, Enterprise: %s, Catalog: %s, Courses: %s',
                username, offer.id, exc, enterprise_in_condition,
                enterprise_catalog, courses_in_basket)
            return False

        if not catalog_contains_course:
            # Basket contains course runs that do not exist in the EnterpriseCustomerCatalogs
            # associated with the EnterpriseCustomer.
            logger.warning(
                '[Code Redemption Failure] Unable to apply enterprise offer because '
                'Enterprise catalog does not contain the course(s) in this basket. '
                'User: %s, Offer: %s, Enterprise: %s, Catalog: %s, Courses: %s',
                username, offer.id, enterprise_in_condition,
                enterprise_catalog, courses_in_basket)
            return False

        if not is_offer_max_discount_available(basket, offer):
            logger.warning(
                '[Enterprise Offer Failure] Unable to apply enterprise offer because bookings limit is consumed.'
                'User: %s, Offer: %s, Enterprise: %s, Catalog: %s, Courses: %s, BookingsLimit: %s, TotalDiscount: %s',
                username,
                offer.id,
                enterprise_in_condition,
                enterprise_catalog,
                courses_in_basket,
                offer.max_discount,
                offer.total_discount,
            )
            return False

        if not is_offer_max_user_discount_available(basket, offer):
            logger.warning(
                '[Enterprise Offer Failure] Unable to apply enterprise offer because user bookings limit is consumed.'
                'User: %s, Offer: %s, Enterprise: %s, Catalog: %s, Courses: %s, UserBookingsLimit: %s',
                username, offer.id, enterprise_in_condition,
                enterprise_catalog, courses_in_basket, offer.max_user_discount)
            return False

        return True