Commit 64d8a379 by zubair-arbi

ENT-1052 apply enterprise offers by provided catalog uuid

parent 979dc88c
......@@ -8,9 +8,12 @@ from slumber.exceptions import SlumberHttpBaseException
from ecommerce.enterprise.api import catalog_contains_course_runs, fetch_enterprise_learner_data
from ecommerce.enterprise.constants import ENTERPRISE_OFFERS_SWITCH
from ecommerce.extensions.basket.utils import ENTERPRISE_CATALOG_ATTRIBUTE_TYPE
from ecommerce.extensions.offer.decorators import check_condition_applicability
from ecommerce.extensions.offer.mixins import ConditionWithoutRangeMixin, SingleItemConsumptionConditionMixin
BasketAttribute = get_model('basket', 'BasketAttribute')
BasketAttributeType = get_model('basket', 'BasketAttributeType')
Condition = get_model('offer', 'Condition')
logger = logging.getLogger(__name__)
......@@ -30,6 +33,9 @@ class EnterpriseCustomerCondition(ConditionWithoutRangeMixin, SingleItemConsumpt
Determines if a user is eligible for an enterprise customer offer
based on their association with the enterprise customer.
It also verifies the catalog `catalog` on the
offer with the catalog on the basket when provided.
Args:
basket (Basket): Contains information about order line items, the current site,
and the user attempting to make the purchase.
......@@ -66,6 +72,13 @@ class EnterpriseCustomerCondition(ConditionWithoutRangeMixin, SingleItemConsumpt
course_run_ids.append(course.id)
# Verify that the current conditional offer is related to the provided
# enterprise catalog
catalog = self._get_enterprise_catalog_uuid_from_basket(basket)
if catalog:
if str(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
......@@ -73,3 +86,31 @@ class EnterpriseCustomerCondition(ConditionWithoutRangeMixin, SingleItemConsumpt
return False
return True
@staticmethod
def _get_enterprise_catalog_uuid_from_basket(basket):
"""
Helper method for fetching enterprise catalog UUID from basket.
Arguments:
basket (Basket): The provided basket can be either temporary (just
for calculating discounts) or an actual one to buy a product.
"""
# For temporary basket try to get `catalog` from request
catalog = basket.strategy.request.GET.get(
'catalog'
) if basket.strategy.request else None
if not catalog:
# For actual baskets get `catalog` from basket attribute
enterprise_catalog_attribute, __ = BasketAttributeType.objects.get_or_create(
name=ENTERPRISE_CATALOG_ATTRIBUTE_TYPE
)
enterprise_customer_catalog = BasketAttribute.objects.filter(
basket=basket,
attribute_type=enterprise_catalog_attribute,
).first()
if enterprise_customer_catalog:
catalog = enterprise_customer_catalog.value_text
return catalog
from decimal import Decimal
from uuid import uuid4
import httpretty
from oscar.core.loading import get_model
......@@ -7,6 +8,7 @@ from waffle.models import Switch
from ecommerce.courses.tests.factories import CourseFactory
from ecommerce.enterprise.constants import ENTERPRISE_OFFERS_SWITCH
from ecommerce.enterprise.tests.mixins import EnterpriseServiceMockMixin
from ecommerce.extensions.basket.utils import basket_add_enterprise_catalog_attribute
from ecommerce.extensions.catalogue.tests.mixins import DiscoveryTestMixin
from ecommerce.extensions.test import factories
from ecommerce.tests.factories import ProductFactory, SiteConfigurationFactory
......@@ -50,6 +52,61 @@ class EnterpriseCustomerConditionTests(EnterpriseServiceMockMixin, DiscoveryTest
)
self.assertTrue(self.condition.is_satisfied(offer, basket))
def _check_condition_is_satisfied(self, offer, basket, is_satisfied):
"""
Helper method to verify that conditional offer is valid for provided basket.
"""
basket.add_product(self.course_run.seat_products[0])
self.mock_enterprise_learner_api(
learner_id=self.user.id,
enterprise_customer_uuid=str(self.condition.enterprise_customer_uuid),
course_run_id=self.course_run.id,
)
self.mock_catalog_contains_course_runs(
[self.course_run.id],
self.condition.enterprise_customer_uuid,
enterprise_customer_catalog_uuid=self.condition.enterprise_customer_catalog_uuid,
)
assert is_satisfied == self.condition.is_satisfied(offer, basket)
@httpretty.activate
def test_is_satisfied_true_for_enterprise_catalog_in_get_request(self):
"""
Ensure that condition returns true for valid enterprise catalog uuid in GET request.
"""
offer = factories.EnterpriseOfferFactory(site=self.site, condition=self.condition)
enterprise_catalog_uuid = str(self.condition.enterprise_customer_catalog_uuid)
basket = factories.BasketFactory(site=self.site, owner=self.user)
basket.strategy.request = self.request
basket.strategy.request.GET = {'catalog': enterprise_catalog_uuid}
self._check_condition_is_satisfied(offer, basket, is_satisfied=True)
@httpretty.activate
def test_is_satisfied_true_for_enterprise_catalog_in_basket_attribute(self):
"""
Ensure that condition returns true for valid enterprise catalog uuid in basket attribute.
"""
offer = factories.EnterpriseOfferFactory(site=self.site, condition=self.condition)
enterprise_catalog_uuid = str(self.condition.enterprise_customer_catalog_uuid)
basket = factories.BasketFactory(site=self.site, owner=self.user)
request_data = {'catalog': enterprise_catalog_uuid}
basket_add_enterprise_catalog_attribute(basket, request_data)
self._check_condition_is_satisfied(offer, basket, is_satisfied=True)
@httpretty.activate
def test_is_satisfied_false_for_invalid_enterprise_catalog(self):
"""
Ensure the condition returns false if provide enterprise catalog UUID.
"""
offer = factories.EnterpriseOfferFactory(site=self.site, condition=self.condition)
invalid_enterprise_catalog_uuid = str(uuid4())
basket = factories.BasketFactory(site=self.site, owner=self.user)
basket.strategy.request = self.request
basket.strategy.request.GET = {'catalog': invalid_enterprise_catalog_uuid}
self._check_condition_is_satisfied(offer, basket, is_satisfied=False)
assert invalid_enterprise_catalog_uuid != offer.condition.enterprise_customer_catalog_uuid
@httpretty.activate
def test_is_satisfied_for_anonymous_user(self):
""" Ensure the condition returns false for an anonymous user. """
......
......@@ -350,7 +350,7 @@ class BasketCalculateView(generics.GenericAPIView):
# This is to avoid merging this temporary basket with a real user basket.
with transaction.atomic():
basket = Basket(owner=user, site=request.site)
basket.strategy = Selector().strategy(user=user)
basket.strategy = Selector().strategy(user=user, request=request)
for product in products:
basket.add_product(product, 1)
......
import datetime
import json
from uuid import uuid4
import ddt
import httpretty
......@@ -16,6 +17,7 @@ from ecommerce.courses.tests.factories import CourseFactory
from ecommerce.entitlements.utils import create_or_update_course_entitlement
from ecommerce.extensions.basket.tests.mixins import BasketMixin
from ecommerce.extensions.basket.utils import (
ENTERPRISE_CATALOG_ATTRIBUTE_TYPE,
add_utm_params_to_url,
attribute_cookie_data,
get_basket_switch_data,
......@@ -417,6 +419,37 @@ class BasketUtilsTests(DiscoveryTestMixin, BasketMixin, TestCase):
# Verify that no exception is raised when no basket attribute exists fitting the delete statement parameters
prepare_basket(request, [product])
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 not catalog in url
request.GET = {}
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
with self.assertRaises(BasketAttribute.DoesNotExist):
BasketAttribute.objects.get(
basket=basket,
attribute_type__name=ENTERPRISE_CATALOG_ATTRIBUTE_TYPE
)
def test_basket_switch_data(self):
"""Verify the correct basket switch data (single vs. multi quantity) is retrieved."""
__, seat, enrollment_code = self.prepare_course_seat_and_enrollment_code()
......
......@@ -3,6 +3,7 @@ import json
import logging
from urllib import unquote, urlencode
import newrelic.agent
import pytz
from django.conf import settings
from django.contrib import messages
......@@ -22,6 +23,7 @@ BasketAttribute = get_model('basket', 'BasketAttribute')
BasketAttributeType = get_model('basket', 'BasketAttributeType')
BUNDLE = 'bundle_identifier'
ORGANIZATION_ATTRIBUTE_TYPE = 'organization'
ENTERPRISE_CATALOG_ATTRIBUTE_TYPE = 'enterprise_catalog_uuid'
StockRecord = get_model('partner', 'StockRecord')
OrderLine = get_model('order', 'Line')
Refund = get_model('refund', 'Refund')
......@@ -59,6 +61,7 @@ def prepare_basket(request, products, voucher=None):
basket (Basket): Contains the product to be redeemed and the Voucher applied.
"""
basket = Basket.get_basket(request.user, request.site)
basket_add_enterprise_catalog_attribute(basket, request.GET)
basket.flush()
basket.save()
basket_addition = get_class('basket.signals', 'basket_addition')
......@@ -267,6 +270,35 @@ def basket_add_organization_attribute(basket, request_data):
)
@newrelic.agent.function_trace()
def basket_add_enterprise_catalog_attribute(basket, request_data):
"""
Add enterprise catalog UUID attribute on basket, if the catalog UUID value
is provided in the request.
Arguments:
basket(Basket): order basket
request_data (dict): HttpRequest data
"""
# Value of enterprise catalog UUID is being passed as `catalog` from
# basket page
enterprise_catalog_uuid = request_data.get('catalog') if request_data else None
enterprise_catalog_attribute, __ = BasketAttributeType.objects.get_or_create(
name=ENTERPRISE_CATALOG_ATTRIBUTE_TYPE
)
if enterprise_catalog_uuid:
BasketAttribute.objects.update_or_create(
basket=basket,
attribute_type=enterprise_catalog_attribute,
defaults={
'value_text': enterprise_catalog_uuid.strip()}
)
else:
# Remove the enterprise catalog attribute for future update in basket
BasketAttribute.objects.filter(basket=basket, attribute_type=enterprise_catalog_attribute).delete()
def _set_basket_bundle_status(bundle, basket):
"""
Sets the basket's bundle status
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment