Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
ecommerce
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
ecommerce
Commits
64d8a379
Commit
64d8a379
authored
Jul 06, 2018
by
zubair-arbi
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ENT-1052 apply enterprise offers by provided catalog uuid
parent
979dc88c
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
164 additions
and
1 deletions
+164
-1
ecommerce/enterprise/conditions.py
+41
-0
ecommerce/enterprise/tests/test_conditions.py
+57
-0
ecommerce/extensions/api/v2/views/baskets.py
+1
-1
ecommerce/extensions/basket/tests/test_utils.py
+33
-0
ecommerce/extensions/basket/utils.py
+32
-0
No files found.
ecommerce/enterprise/conditions.py
View file @
64d8a379
...
@@ -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 `catalog` 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
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
,
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,31 @@ class EnterpriseCustomerCondition(ConditionWithoutRangeMixin, SingleItemConsumpt
...
@@ -73,3 +86,31 @@ 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 `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
ecommerce/enterprise/tests/test_conditions.py
View file @
64d8a379
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
=
{
'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
@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. """
...
...
ecommerce/extensions/api/v2/views/baskets.py
View file @
64d8a379
...
@@ -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
)
...
...
ecommerce/extensions/basket/tests/test_utils.py
View file @
64d8a379
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,37 @@ class BasketUtilsTests(DiscoveryTestMixin, BasketMixin, TestCase):
...
@@ -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
# 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
=
{
'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
):
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
()
...
...
ecommerce/extensions/basket/utils.py
View file @
64d8a379
...
@@ -3,6 +3,7 @@ import json
...
@@ -3,6 +3,7 @@ import json
import
logging
import
logging
from
urllib
import
unquote
,
urlencode
from
urllib
import
unquote
,
urlencode
import
newrelic.agent
import
pytz
import
pytz
from
django.conf
import
settings
from
django.conf
import
settings
from
django.contrib
import
messages
from
django.contrib
import
messages
...
@@ -22,6 +23,7 @@ BasketAttribute = get_model('basket', 'BasketAttribute')
...
@@ -22,6 +23,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'
)
...
@@ -59,6 +61,7 @@ def prepare_basket(request, products, voucher=None):
...
@@ -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): 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'
)
...
@@ -267,6 +270,35 @@ def basket_add_organization_attribute(basket, request_data):
...
@@ -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
):
def
_set_basket_bundle_status
(
bundle
,
basket
):
"""
"""
Sets the basket's bundle status
Sets the basket's bundle status
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment