Commit 2bbb1dc9 by zubair-arbi Committed by Zubair Afzal

Revert "Revert "ENT-1052 add discount by enterprise catalog""

This reverts commit a2ce5741.
parent 6cad89d2
...@@ -8,9 +8,12 @@ from slumber.exceptions import SlumberHttpBaseException ...@@ -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.api import catalog_contains_course_runs, fetch_enterprise_learner_data
from ecommerce.enterprise.constants import ENTERPRISE_OFFERS_SWITCH 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.decorators import check_condition_applicability
from ecommerce.extensions.offer.mixins import ConditionWithoutRangeMixin, SingleItemConsumptionConditionMixin from ecommerce.extensions.offer.mixins import ConditionWithoutRangeMixin, SingleItemConsumptionConditionMixin
BasketAttribute = get_model('basket', 'BasketAttribute')
BasketAttributeType = get_model('basket', 'BasketAttributeType')
Condition = get_model('offer', 'Condition') Condition = get_model('offer', 'Condition')
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -30,6 +33,9 @@ class EnterpriseCustomerCondition(ConditionWithoutRangeMixin, SingleItemConsumpt ...@@ -30,6 +33,9 @@ class EnterpriseCustomerCondition(ConditionWithoutRangeMixin, SingleItemConsumpt
Determines if a user is eligible for an enterprise customer offer Determines if a user is eligible for an enterprise customer offer
based on their association with the enterprise customer. based on their association with the enterprise customer.
It also verifies the catalog `enterprise_customer_catalog_uuid` on the
offer with the catalog on the basket when provided.
Args: Args:
basket (Basket): Contains information about order line items, the current site, basket (Basket): Contains information about order line items, the current site,
and the user attempting to make the purchase. and the user attempting to make the purchase.
...@@ -66,6 +72,13 @@ class EnterpriseCustomerCondition(ConditionWithoutRangeMixin, SingleItemConsumpt ...@@ -66,6 +72,13 @@ class EnterpriseCustomerCondition(ConditionWithoutRangeMixin, SingleItemConsumpt
course_run_ids.append(course.id) course_run_ids.append(course.id)
# Verify that the current conditional offer is related to the provided
# enterprise catalog
enterprise_customer_catalog_uuid = self._get_enterprise_catalog_uuid_from_basket(basket)
if enterprise_customer_catalog_uuid:
if str(offer.condition.enterprise_customer_catalog_uuid) != enterprise_customer_catalog_uuid:
return False
if not catalog_contains_course_runs(basket.site, course_run_ids, self.enterprise_customer_uuid, if not catalog_contains_course_runs(basket.site, course_run_ids, self.enterprise_customer_uuid,
enterprise_customer_catalog_uuid=self.enterprise_customer_catalog_uuid): enterprise_customer_catalog_uuid=self.enterprise_customer_catalog_uuid):
# Basket contains course runs that do not exist in the EnterpriseCustomerCatalogs # Basket contains course runs that do not exist in the EnterpriseCustomerCatalogs
...@@ -73,3 +86,33 @@ class EnterpriseCustomerCondition(ConditionWithoutRangeMixin, SingleItemConsumpt ...@@ -73,3 +86,33 @@ class EnterpriseCustomerCondition(ConditionWithoutRangeMixin, SingleItemConsumpt
return False return False
return True 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 `enterprise_customer_catalog_uuid`
# from request
enterprise_customer_catalog_uuid = basket.strategy.request.GET.get(
'enterprise_customer_catalog_uuid'
) if basket.strategy.request else None
if not enterprise_customer_catalog_uuid:
# For actual baskets get `enterprise_customer_catalog_uuid` 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:
enterprise_customer_catalog_uuid = enterprise_customer_catalog.value_text
return enterprise_customer_catalog_uuid
from decimal import Decimal from decimal import Decimal
from uuid import uuid4
import httpretty import httpretty
from oscar.core.loading import get_model from oscar.core.loading import get_model
...@@ -7,6 +8,7 @@ from waffle.models import Switch ...@@ -7,6 +8,7 @@ from waffle.models import Switch
from ecommerce.courses.tests.factories import CourseFactory from ecommerce.courses.tests.factories import CourseFactory
from ecommerce.enterprise.constants import ENTERPRISE_OFFERS_SWITCH from ecommerce.enterprise.constants import ENTERPRISE_OFFERS_SWITCH
from ecommerce.enterprise.tests.mixins import EnterpriseServiceMockMixin 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.catalogue.tests.mixins import DiscoveryTestMixin
from ecommerce.extensions.test import factories from ecommerce.extensions.test import factories
from ecommerce.tests.factories import ProductFactory, SiteConfigurationFactory from ecommerce.tests.factories import ProductFactory, SiteConfigurationFactory
...@@ -50,6 +52,61 @@ class EnterpriseCustomerConditionTests(EnterpriseServiceMockMixin, DiscoveryTest ...@@ -50,6 +52,61 @@ class EnterpriseCustomerConditionTests(EnterpriseServiceMockMixin, DiscoveryTest
) )
self.assertTrue(self.condition.is_satisfied(offer, basket)) 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 = {'enterprise_customer_catalog_uuid': 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 = {'enterprise_customer_catalog_uuid': 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 = {'enterprise_customer_catalog_uuid': 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 @httpretty.activate
def test_is_satisfied_for_anonymous_user(self): def test_is_satisfied_for_anonymous_user(self):
""" Ensure the condition returns false for an anonymous user. """ """ Ensure the condition returns false for an anonymous user. """
......
...@@ -350,7 +350,7 @@ class BasketCalculateView(generics.GenericAPIView): ...@@ -350,7 +350,7 @@ class BasketCalculateView(generics.GenericAPIView):
# This is to avoid merging this temporary basket with a real user basket. # This is to avoid merging this temporary basket with a real user basket.
with transaction.atomic(): with transaction.atomic():
basket = Basket(owner=user, site=request.site) 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: for product in products:
basket.add_product(product, 1) basket.add_product(product, 1)
......
import datetime import datetime
import json import json
from uuid import uuid4
import ddt import ddt
import httpretty import httpretty
...@@ -16,6 +17,7 @@ from ecommerce.courses.tests.factories import CourseFactory ...@@ -16,6 +17,7 @@ from ecommerce.courses.tests.factories import CourseFactory
from ecommerce.entitlements.utils import create_or_update_course_entitlement from ecommerce.entitlements.utils import create_or_update_course_entitlement
from ecommerce.extensions.basket.tests.mixins import BasketMixin from ecommerce.extensions.basket.tests.mixins import BasketMixin
from ecommerce.extensions.basket.utils import ( from ecommerce.extensions.basket.utils import (
ENTERPRISE_CATALOG_ATTRIBUTE_TYPE,
add_utm_params_to_url, add_utm_params_to_url,
attribute_cookie_data, attribute_cookie_data,
get_basket_switch_data, get_basket_switch_data,
...@@ -417,6 +419,24 @@ class BasketUtilsTests(DiscoveryTestMixin, BasketMixin, TestCase): ...@@ -417,6 +419,24 @@ class BasketUtilsTests(DiscoveryTestMixin, BasketMixin, TestCase):
# Verify that no exception is raised when no basket attribute exists fitting the delete statement parameters # Verify that no exception is raised when no basket attribute exists fitting the delete statement parameters
prepare_basket(request, [product]) 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 = {'enterprise_customer_catalog_uuid': 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
def test_basket_switch_data(self): def test_basket_switch_data(self):
"""Verify the correct basket switch data (single vs. multi quantity) is retrieved.""" """Verify the correct basket switch data (single vs. multi quantity) is retrieved."""
__, seat, enrollment_code = self.prepare_course_seat_and_enrollment_code() __, seat, enrollment_code = self.prepare_course_seat_and_enrollment_code()
......
...@@ -24,6 +24,7 @@ BasketAttribute = get_model('basket', 'BasketAttribute') ...@@ -24,6 +24,7 @@ BasketAttribute = get_model('basket', 'BasketAttribute')
BasketAttributeType = get_model('basket', 'BasketAttributeType') BasketAttributeType = get_model('basket', 'BasketAttributeType')
BUNDLE = 'bundle_identifier' BUNDLE = 'bundle_identifier'
ORGANIZATION_ATTRIBUTE_TYPE = 'organization' ORGANIZATION_ATTRIBUTE_TYPE = 'organization'
ENTERPRISE_CATALOG_ATTRIBUTE_TYPE = 'enterprise_catalog_uuid'
StockRecord = get_model('partner', 'StockRecord') StockRecord = get_model('partner', 'StockRecord')
OrderLine = get_model('order', 'Line') OrderLine = get_model('order', 'Line')
Refund = get_model('refund', 'Refund') Refund = get_model('refund', 'Refund')
...@@ -63,6 +64,7 @@ def prepare_basket(request, products, voucher=None): ...@@ -63,6 +64,7 @@ def prepare_basket(request, products, voucher=None):
basket (Basket): Contains the product to be redeemed and the Voucher applied. basket (Basket): Contains the product to be redeemed and the Voucher applied.
""" """
basket = Basket.get_basket(request.user, request.site) basket = Basket.get_basket(request.user, request.site)
basket_add_enterprise_catalog_attribute(basket, request.GET)
basket.flush() basket.flush()
basket.save() basket.save()
basket_addition = get_class('basket.signals', 'basket_addition') basket_addition = get_class('basket.signals', 'basket_addition')
...@@ -310,6 +312,32 @@ def basket_add_organization_attribute(basket, request_data): ...@@ -310,6 +312,32 @@ def basket_add_organization_attribute(basket, request_data):
@newrelic.agent.function_trace() @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
# `enterprise_customer_catalog_uuid` from basket page
enterprise_catalog_uuid = request_data.get('enterprise_customer_catalog_uuid') if request_data else None
if enterprise_catalog_uuid:
enterprise_catalog_attribute, __ = BasketAttributeType.objects.get_or_create(
name=ENTERPRISE_CATALOG_ATTRIBUTE_TYPE
)
BasketAttribute.objects.update_or_create(
basket=basket,
attribute_type=enterprise_catalog_attribute,
defaults={'value_text': enterprise_catalog_uuid.strip()}
)
@newrelic.agent.function_trace()
def _set_basket_bundle_status(bundle, basket): def _set_basket_bundle_status(bundle, basket):
""" """
Sets the basket's bundle status 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