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
da2db893
Commit
da2db893
authored
Feb 24, 2017
by
Vedran Karacic
Committed by
Vedran Karačić
Feb 28, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Change product class name strings with constants
parent
0d860ceb
Hide whitespace changes
Inline
Side-by-side
Showing
28 changed files
with
115 additions
and
80 deletions
+115
-80
ecommerce/core/constants.py
+3
-0
ecommerce/coupons/tests/mixins.py
+6
-2
ecommerce/courses/models.py
+4
-3
ecommerce/enterprise/entitlements.py
+4
-1
ecommerce/extensions/api/serializers.py
+2
-2
ecommerce/extensions/api/v2/tests/views/test_courses.py
+2
-2
ecommerce/extensions/api/v2/tests/views/test_products.py
+2
-1
ecommerce/extensions/api/v2/tests/views/test_publication.py
+5
-5
ecommerce/extensions/api/v2/views/coupons.py
+2
-1
ecommerce/extensions/basket/utils.py
+3
-5
ecommerce/extensions/basket/views.py
+6
-8
ecommerce/extensions/catalogue/migrations/0002_auto_20150223_1052.py
+6
-3
ecommerce/extensions/catalogue/migrations/0006_credit_provider_attr.py
+3
-6
ecommerce/extensions/catalogue/migrations/0009_credit_hours_attr.py
+3
-1
ecommerce/extensions/catalogue/migrations/0013_coupon_product_class.py
+6
-3
ecommerce/extensions/catalogue/migrations/0016_coupon_note_attribute.py
+5
-2
ecommerce/extensions/catalogue/migrations/0017_enrollment_code_product_class.py
+2
-1
ecommerce/extensions/catalogue/models.py
+17
-1
ecommerce/extensions/catalogue/tests/mixins.py
+8
-3
ecommerce/extensions/catalogue/tests/test_coupons.py
+2
-1
ecommerce/extensions/catalogue/utils.py
+6
-8
ecommerce/extensions/checkout/signals.py
+1
-1
ecommerce/extensions/fulfillment/modules.py
+4
-4
ecommerce/extensions/fulfillment/tests/test_modules.py
+5
-3
ecommerce/extensions/order/models.py
+1
-2
ecommerce/extensions/order/tests/test_models.py
+2
-2
ecommerce/extensions/partner/strategy.py
+3
-1
ecommerce/sailthru/signals.py
+2
-8
No files found.
ecommerce/core/constants.py
View file @
da2db893
...
...
@@ -10,6 +10,9 @@ COURSE_ID_PATTERN = r'(?P<course_id>{})'.format(COURSE_ID_REGEX)
# Seat constants
SEAT_PRODUCT_CLASS_NAME
=
'Seat'
# Coupon constant
COUPON_PRODUCT_CLASS_NAME
=
'Coupon'
# Enrollment Code constants
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
=
'Enrollment Code'
ENROLLMENT_CODE_SWITCH
=
'create_enrollment_codes'
...
...
ecommerce/coupons/tests/mixins.py
View file @
da2db893
...
...
@@ -5,8 +5,10 @@ import httpretty
from
django.conf
import
settings
from
django.core.cache
import
cache
from
django.test
import
RequestFactory
from
oscar.core.utils
import
slugify
from
oscar.test
import
factories
from
ecommerce.core.constants
import
COUPON_PRODUCT_CLASS_NAME
from
ecommerce.core.models
import
BusinessClient
from
ecommerce.extensions.catalogue.utils
import
create_coupon_product
from
ecommerce.extensions.api.v2.views.coupons
import
CouponViewSet
...
...
@@ -187,8 +189,10 @@ class CouponMixin(object):
@property
def
coupon_product_class
(
self
):
defaults
=
{
'requires_shipping'
:
False
,
'track_stock'
:
False
,
'name'
:
'Coupon'
}
pc
,
created
=
ProductClass
.
objects
.
get_or_create
(
name
=
'Coupon'
,
slug
=
'coupon'
,
defaults
=
defaults
)
defaults
=
{
'requires_shipping'
:
False
,
'track_stock'
:
False
,
'name'
:
COUPON_PRODUCT_CLASS_NAME
}
pc
,
created
=
ProductClass
.
objects
.
get_or_create
(
name
=
COUPON_PRODUCT_CLASS_NAME
,
slug
=
slugify
(
COUPON_PRODUCT_CLASS_NAME
),
defaults
=
defaults
)
if
created
:
factories
.
ProductAttributeFactory
(
...
...
ecommerce/courses/models.py
View file @
da2db893
...
...
@@ -12,7 +12,8 @@ import waffle
from
ecommerce.core.constants
import
(
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
,
ENROLLMENT_CODE_SEAT_TYPES
,
ENROLLMENT_CODE_SWITCH
ENROLLMENT_CODE_SWITCH
,
SEAT_PRODUCT_CLASS_NAME
)
from
ecommerce.courses.publishers
import
LMSPublisher
from
ecommerce.extensions.catalogue.utils
import
generate_sku
...
...
@@ -45,7 +46,7 @@ class Course(models.Model):
parent
,
created
=
self
.
products
.
get_or_create
(
course
=
self
,
structure
=
Product
.
PARENT
,
product_class
=
ProductClass
.
objects
.
get
(
slug
=
'seat'
),
product_class
=
ProductClass
.
objects
.
get
(
name
=
SEAT_PRODUCT_CLASS_NAME
),
)
ProductCategory
.
objects
.
get_or_create
(
category
=
Category
.
objects
.
get
(
name
=
'Seats'
),
product
=
parent
)
parent
.
title
=
'Seat in {}'
.
format
(
self
.
name
)
...
...
@@ -100,7 +101,7 @@ class Course(models.Model):
@property
def
parent_seat_product
(
self
):
""" Returns the course seat parent Product. """
return
self
.
products
.
get
(
product_class__
slug
=
'seat'
,
structure
=
Product
.
PARENT
)
return
self
.
products
.
get
(
product_class__
name
=
SEAT_PRODUCT_CLASS_NAME
,
structure
=
Product
.
PARENT
)
@property
def
seat_products
(
self
):
...
...
ecommerce/enterprise/entitlements.py
View file @
da2db893
...
...
@@ -16,6 +16,7 @@ from oscar.core.loading import get_model
from
requests.exceptions
import
ConnectionError
,
Timeout
from
slumber.exceptions
import
SlumberBaseException
from
ecommerce.core.constants
import
COUPON_PRODUCT_CLASS_NAME
from
ecommerce.coupons.views
import
voucher_is_valid
from
ecommerce.courses.utils
import
get_course_catalogs
from
ecommerce.enterprise.utils
import
is_enterprise_feature_enabled
...
...
@@ -72,7 +73,9 @@ def get_course_vouchers_for_learner(site, user, course_id):
vouchers
=
[]
for
entitlement
in
entitlements
:
try
:
coupon_product
=
Product
.
objects
.
filter
(
product_class__name
=
'Coupon'
)
.
get
(
id
=
entitlement
)
coupon_product
=
Product
.
objects
.
filter
(
product_class__name
=
COUPON_PRODUCT_CLASS_NAME
)
.
get
(
id
=
entitlement
)
except
Product
.
DoesNotExist
:
logger
.
exception
(
'There was an error getting coupon product with the entitlement id
%
s'
,
entitlement
)
return
None
...
...
ecommerce/extensions/api/serializers.py
View file @
da2db893
...
...
@@ -14,7 +14,7 @@ from rest_framework import serializers
from
rest_framework.reverse
import
reverse
import
waffle
from
ecommerce.core.constants
import
ISO_8601_FORMAT
,
COURSE_ID_REGEX
from
ecommerce.core.constants
import
COURSE_ID_REGEX
,
ISO_8601_FORMAT
,
SEAT_PRODUCT_CLASS_NAME
from
ecommerce.core.models
import
Site
,
SiteConfiguration
from
ecommerce.core.url_utils
import
get_ecommerce_url
from
ecommerce.courses.models
import
Course
...
...
@@ -329,7 +329,7 @@ class AtomicPublicationSerializer(serializers.Serializer): # pylint: disable=ab
for
product
in
products
:
# Verify that each product is intended to be a Seat.
product_class
=
product
.
get
(
'product_class'
)
if
product_class
!=
'Seat'
:
if
product_class
!=
SEAT_PRODUCT_CLASS_NAME
:
raise
serializers
.
ValidationError
(
_
(
u"Invalid product class [{product_class}] requested."
.
format
(
product_class
=
product_class
))
)
...
...
ecommerce/extensions/api/v2/tests/views/test_courses.py
View file @
da2db893
...
...
@@ -9,7 +9,7 @@ from django.contrib.auth import get_user_model
from
django.core.urlresolvers
import
reverse
from
oscar.core.loading
import
get_model
,
get_class
from
ecommerce.core.constants
import
ISO_8601_FORMAT
from
ecommerce.core.constants
import
ISO_8601_FORMAT
,
SEAT_PRODUCT_CLASS_NAME
from
ecommerce.core.tests
import
toggle_switch
from
ecommerce.courses.models
import
Course
from
ecommerce.courses.publishers
import
LMSPublisher
...
...
@@ -125,7 +125,7 @@ class CourseViewSetTests(ProductSerializerMixin, CourseCatalogTestMixin, TestCas
self
.
assertEqual
(
course
.
products
.
count
(),
1
)
# Validate the parent seat
seat_product_class
=
ProductClass
.
objects
.
get
(
slug
=
'seat'
)
seat_product_class
=
ProductClass
.
objects
.
get
(
name
=
SEAT_PRODUCT_CLASS_NAME
)
parent
=
course
.
parent_seat_product
self
.
assertEqual
(
parent
.
structure
,
Product
.
PARENT
)
self
.
assertEqual
(
parent
.
title
,
'Seat in Test Course'
)
...
...
ecommerce/extensions/api/v2/tests/views/test_products.py
View file @
da2db893
...
...
@@ -8,6 +8,7 @@ from django.core.urlresolvers import reverse
from
django.test
import
RequestFactory
from
oscar.core.loading
import
get_model
from
ecommerce.core.constants
import
COUPON_PRODUCT_CLASS_NAME
from
ecommerce.coupons.tests.mixins
import
CouponMixin
from
ecommerce.courses.models
import
Course
from
ecommerce.extensions.api.serializers
import
ProductSerializer
...
...
@@ -171,4 +172,4 @@ class ProductViewSetCouponTests(CouponMixin, ProductViewSetBase):
self
.
assertEqual
(
response
.
status_code
,
200
)
response_data
=
json
.
loads
(
response
.
content
)
self
.
assertEqual
(
response_data
[
'count'
],
1
)
self
.
assertEqual
(
response_data
[
'results'
][
0
][
'product_class'
],
'Coupon'
)
self
.
assertEqual
(
response_data
[
'results'
][
0
][
'product_class'
],
COUPON_PRODUCT_CLASS_NAME
)
ecommerce/extensions/api/v2/tests/views/test_publication.py
View file @
da2db893
...
...
@@ -7,7 +7,7 @@ from django.core.urlresolvers import reverse
import
mock
import
pytz
from
ecommerce.core.constants
import
ISO_8601_FORMAT
from
ecommerce.core.constants
import
ISO_8601_FORMAT
,
SEAT_PRODUCT_CLASS_NAME
from
ecommerce.core.tests
import
toggle_switch
from
ecommerce.courses.models
import
Course
from
ecommerce.courses.publishers
import
LMSPublisher
...
...
@@ -33,7 +33,7 @@ class AtomicPublicationTests(CourseCatalogTestMixin, TestCase):
'verification_deadline'
:
EXPIRES_STRING
,
'products'
:
[
{
'product_class'
:
'Seat'
,
'product_class'
:
SEAT_PRODUCT_CLASS_NAME
,
'expires'
:
None
,
'price'
:
0.00
,
'attribute_values'
:
[
...
...
@@ -52,7 +52,7 @@ class AtomicPublicationTests(CourseCatalogTestMixin, TestCase):
}
},
{
'product_class'
:
'Seat'
,
'product_class'
:
SEAT_PRODUCT_CLASS_NAME
,
'expires'
:
None
,
'price'
:
0.00
,
'attribute_values'
:
[
...
...
@@ -75,7 +75,7 @@ class AtomicPublicationTests(CourseCatalogTestMixin, TestCase):
}
},
{
'product_class'
:
'Seat'
,
'product_class'
:
SEAT_PRODUCT_CLASS_NAME
,
'expires'
:
EXPIRES_STRING
,
'price'
:
10.00
,
'attribute_values'
:
[
...
...
@@ -98,7 +98,7 @@ class AtomicPublicationTests(CourseCatalogTestMixin, TestCase):
}
},
{
'product_class'
:
'Seat'
,
'product_class'
:
SEAT_PRODUCT_CLASS_NAME
,
'expires'
:
EXPIRES_STRING
,
'price'
:
100.00
,
'attribute_values'
:
[
...
...
ecommerce/extensions/api/v2/views/coupons.py
View file @
da2db893
...
...
@@ -12,6 +12,7 @@ from rest_framework import filters, generics, serializers, status, viewsets
from
rest_framework.permissions
import
IsAdminUser
,
IsAuthenticated
from
rest_framework.response
import
Response
from
ecommerce.core.constants
import
COUPON_PRODUCT_CLASS_NAME
from
ecommerce.core.models
import
BusinessClient
from
ecommerce.core.utils
import
log_message_and_raise_validation_error
from
ecommerce.coupons.utils
import
prepare_course_seat_types
...
...
@@ -42,7 +43,7 @@ Voucher = get_model('voucher', 'Voucher')
class
CouponViewSet
(
EdxOrderPlacementMixin
,
viewsets
.
ModelViewSet
):
""" Coupon resource. """
queryset
=
Product
.
objects
.
filter
(
product_class__name
=
'Coupon'
)
queryset
=
Product
.
objects
.
filter
(
product_class__name
=
COUPON_PRODUCT_CLASS_NAME
)
permission_classes
=
(
IsAuthenticated
,
IsAdminUser
)
filter_backends
=
(
filters
.
DjangoFilterBackend
,)
filter_class
=
ProductFilter
...
...
ecommerce/extensions/basket/utils.py
View file @
da2db893
...
...
@@ -8,7 +8,6 @@ from django.utils.translation import ugettext_lazy as _
from
oscar.core.loading
import
get_class
,
get_model
import
pytz
from
ecommerce.core.constants
import
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
,
SEAT_PRODUCT_CLASS_NAME
from
ecommerce.referrals.models
import
Referral
Applicator
=
get_class
(
'offer.utils'
,
'Applicator'
)
...
...
@@ -38,7 +37,7 @@ def prepare_basket(request, product, voucher=None):
basket
=
Basket
.
get_basket
(
request
.
user
,
request
.
site
)
basket
.
flush
()
basket
.
add_product
(
product
,
1
)
if
product
.
get_product_class
()
.
name
==
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
:
if
product
.
is_enrollment_code_product
:
basket
.
clear_vouchers
()
elif
voucher
:
basket
.
clear_vouchers
()
...
...
@@ -56,14 +55,13 @@ def prepare_basket(request, product, voucher=None):
def
get_basket_switch_data
(
product
):
product_class_name
=
product
.
get_product_class
()
.
name
structure
=
product
.
structure
switch_link_text
=
None
if
product
_class_name
==
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
:
if
product
.
is_enrollment_code_product
:
switch_link_text
=
_
(
'Click here to just purchase an enrollment for yourself'
)
structure
=
'child'
elif
product
_class_name
==
SEAT_PRODUCT_CLASS_NAME
:
elif
product
.
is_seat_product
:
switch_link_text
=
_
(
'Click here to purchase multiple seats in this course'
)
structure
=
'standalone'
...
...
ecommerce/extensions/basket/views.py
View file @
da2db893
...
...
@@ -13,7 +13,6 @@ from oscar.apps.basket.views import VoucherAddView as BaseVoucherAddView, Vouche
from
requests.exceptions
import
ConnectionError
,
Timeout
from
slumber.exceptions
import
SlumberBaseException
from
ecommerce.core.constants
import
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
,
SEAT_PRODUCT_CLASS_NAME
from
ecommerce.core.exceptions
import
SiteConfigurationError
from
ecommerce.core.url_utils
import
get_lms_url
from
ecommerce.courses.utils
import
get_certificate_type_display_value
,
get_course_info_from_catalog
,
mode_for_seat
...
...
@@ -65,7 +64,7 @@ class BasketSingleItemView(View):
# If the product is not an Enrollment Code, we check to see if the user is already
# enrolled to prevent double-enrollment and/or accidental coupon usage
if
product
.
get_product_class
()
.
name
!=
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
:
if
not
product
.
is_enrollment_code_product
:
try
:
if
request
.
user
.
is_user_already_enrolled
(
request
,
product
):
logger
.
warning
(
...
...
@@ -96,9 +95,9 @@ class BasketSummaryView(BasketView):
Return the seat type based on the product class
"""
seat_type
=
None
if
product
.
get_product_class
()
.
name
==
SEAT_PRODUCT_CLASS_NAME
:
if
product
.
is_seat_product
:
seat_type
=
get_certificate_type_display_value
(
product
.
attr
.
certificate_type
)
elif
product
.
get_product_class
()
.
name
==
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
:
elif
product
.
is_enrollment_code_product
:
seat_type
=
get_certificate_type_display_value
(
product
.
attr
.
seat_type
)
return
seat_type
...
...
@@ -174,8 +173,7 @@ class BasketSummaryView(BasketView):
switch_link_text
=
partner_sku
=
order_details_msg
=
None
for
line
in
lines
:
product_class_name
=
line
.
product
.
get_product_class
()
.
name
if
product_class_name
==
'Seat'
:
if
line
.
product
.
is_seat_product
:
line_data
=
self
.
_get_course_data
(
line
.
product
)
certificate_type
=
line
.
product
.
attr
.
certificate_type
...
...
@@ -193,7 +191,7 @@ class BasketSummaryView(BasketView):
order_details_msg
=
_
(
'You will be automatically enrolled in the course upon completing your order.'
)
elif
product_class_name
==
'Enrollment Code'
:
elif
line
.
product
.
is_enrollment_code_product
:
line_data
=
self
.
_get_course_data
(
line
.
product
)
show_voucher_form
=
False
order_details_msg
=
_
(
...
...
@@ -219,7 +217,7 @@ class BasketSummaryView(BasketView):
line_data
.
update
({
'benefit_value'
:
benefit_value
,
'enrollment_code'
:
line
.
product
.
get_product_class
()
.
name
==
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
,
'enrollment_code'
:
line
.
product
.
is_enrollment_code_product
,
'line'
:
line
,
'seat_type'
:
self
.
_determine_seat_type
(
line
.
product
),
})
...
...
ecommerce/extensions/catalogue/migrations/0002_auto_20150223_1052.py
View file @
da2db893
...
...
@@ -2,6 +2,9 @@
from
__future__
import
unicode_literals
from
django.db
import
models
,
migrations
from
oscar.core.utils
import
slugify
from
ecommerce.core.constants
import
SEAT_PRODUCT_CLASS_NAME
def
create_catalog
(
apps
,
schema_editor
):
...
...
@@ -20,8 +23,8 @@ def create_catalog(apps, schema_editor):
seat
=
ProductClass
.
objects
.
create
(
track_stock
=
False
,
requires_shipping
=
False
,
name
=
'Seat'
,
slug
=
'seat'
name
=
SEAT_PRODUCT_CLASS_NAME
,
slug
=
slugify
(
SEAT_PRODUCT_CLASS_NAME
)
)
# Create product attributes for course seat products
...
...
@@ -66,7 +69,7 @@ def remove_catalog(apps, schema_editor):
ProductClass
=
apps
.
get_model
(
"catalogue"
,
"ProductClass"
)
Category
.
objects
.
filter
(
slug
=
'seats'
)
.
delete
()
ProductClass
.
objects
.
filter
(
slug
=
'seat'
)
.
delete
()
ProductClass
.
objects
.
filter
(
name
=
SEAT_PRODUCT_CLASS_NAME
)
.
delete
()
class
Migration
(
migrations
.
Migration
):
...
...
ecommerce/extensions/catalogue/migrations/0006_credit_provider_attr.py
View file @
da2db893
...
...
@@ -3,12 +3,14 @@ from __future__ import unicode_literals
from
django.db
import
models
,
migrations
from
ecommerce.core.constants
import
SEAT_PRODUCT_CLASS_NAME
def
create_credit_provider_attribute
(
apps
,
schema_editor
):
# Get seat Object
ProductClass
=
apps
.
get_model
(
'catalogue'
,
'ProductClass'
)
seat
=
ProductClass
.
objects
.
get
(
name
=
'Seat'
)
seat
=
ProductClass
.
objects
.
get
(
name
=
SEAT_PRODUCT_CLASS_NAME
)
# Create our Product Attributes
ProductAttribute
=
apps
.
get_model
(
'catalogue'
,
'ProductAttribute'
)
...
...
@@ -23,11 +25,6 @@ def create_credit_provider_attribute(apps, schema_editor):
def
delete_credit_provider_attribute
(
apps
,
schema_editor
):
"""For backward compatibility"""
# Get seat Object
ProductClass
=
apps
.
get_model
(
'catalogue'
,
'ProductClass'
)
seat
=
ProductClass
.
objects
.
get
(
name
=
'Seat'
)
# Delete our Product Attributes
ProductAttribute
=
apps
.
get_model
(
'catalogue'
,
'ProductAttribute'
)
ProductAttribute
.
objects
.
filter
(
code
=
'credit_provider'
)
.
delete
()
...
...
ecommerce/extensions/catalogue/migrations/0009_credit_hours_attr.py
View file @
da2db893
...
...
@@ -3,12 +3,14 @@ from __future__ import unicode_literals
from
django.db
import
models
,
migrations
from
ecommerce.core.constants
import
SEAT_PRODUCT_CLASS_NAME
def
create_credit_hours_attribute
(
apps
,
schema_editor
):
# Get seat Object
ProductClass
=
apps
.
get_model
(
'catalogue'
,
'ProductClass'
)
seat
=
ProductClass
.
objects
.
get
(
name
=
'Seat'
)
seat
=
ProductClass
.
objects
.
get
(
name
=
SEAT_PRODUCT_CLASS_NAME
)
# Create our Product Attributes
ProductAttribute
=
apps
.
get_model
(
'catalogue'
,
'ProductAttribute'
)
...
...
ecommerce/extensions/catalogue/migrations/0013_coupon_product_class.py
View file @
da2db893
...
...
@@ -3,6 +3,9 @@ from __future__ import unicode_literals
from
django.db
import
migrations
from
oscar.core.loading
import
get_model
from
oscar.core.utils
import
slugify
from
ecommerce.core.constants
import
COUPON_PRODUCT_CLASS_NAME
Category
=
get_model
(
"catalogue"
,
"Category"
)
ProductAttribute
=
get_model
(
"catalogue"
,
"ProductAttribute"
)
...
...
@@ -15,8 +18,8 @@ def create_product_class(apps, schema_editor):
coupon
=
ProductClass
.
objects
.
create
(
track_stock
=
False
,
requires_shipping
=
False
,
name
=
'Coupon'
,
slug
=
'coupon'
,
name
=
COUPON_PRODUCT_CLASS_NAME
,
slug
=
slugify
(
COUPON_PRODUCT_CLASS_NAME
)
,
)
ProductAttribute
.
objects
.
create
(
...
...
@@ -40,7 +43,7 @@ def create_product_class(apps, schema_editor):
def
remove_product_class
(
apps
,
schema_editor
):
""" Reverse function. """
Category
.
objects
.
filter
(
slug
=
'coupon'
)
.
delete
()
ProductClass
.
objects
.
filter
(
slug
=
'coupon'
)
.
delete
()
ProductClass
.
objects
.
filter
(
name
=
COUPON_PRODUCT_CLASS_NAME
)
.
delete
()
def
remove_enrollment_code
(
apps
,
schema_editor
):
...
...
ecommerce/extensions/catalogue/migrations/0016_coupon_note_attribute.py
View file @
da2db893
...
...
@@ -4,13 +4,16 @@ from __future__ import unicode_literals
from
django.db
import
migrations
from
oscar.core.loading
import
get_model
from
ecommerce.core.constants
import
COUPON_PRODUCT_CLASS_NAME
ProductAttribute
=
get_model
(
"catalogue"
,
"ProductAttribute"
)
ProductClass
=
get_model
(
"catalogue"
,
"ProductClass"
)
def
create_note_attribute
(
apps
,
schema_editor
):
"""Create coupon note attribute."""
coupon
=
ProductClass
.
objects
.
get
(
name
=
'Coupon'
)
coupon
=
ProductClass
.
objects
.
get
(
name
=
COUPON_PRODUCT_CLASS_NAME
)
ProductAttribute
.
objects
.
create
(
product_class
=
coupon
,
name
=
'Note'
,
...
...
@@ -22,7 +25,7 @@ def create_note_attribute(apps, schema_editor):
def
remove_note_attribute
(
apps
,
schema_editor
):
"""Remove coupon note attribute."""
coupon
=
ProductClass
.
objects
.
get
(
name
=
'Coupon'
)
coupon
=
ProductClass
.
objects
.
get
(
name
=
COUPON_PRODUCT_CLASS_NAME
)
ProductAttribute
.
objects
.
get
(
product_class
=
coupon
,
name
=
'Note'
)
.
delete
()
...
...
ecommerce/extensions/catalogue/migrations/0017_enrollment_code_product_class.py
View file @
da2db893
...
...
@@ -3,6 +3,7 @@ from __future__ import unicode_literals
from
django.db
import
migrations
from
oscar.core.loading
import
get_model
from
oscar.core.utils
import
slugify
from
ecommerce.core.constants
import
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
...
...
@@ -18,7 +19,7 @@ def create_enrollment_code_product_class(apps, schema_editor):
track_stock
=
False
,
requires_shipping
=
False
,
name
=
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
,
slug
=
'enrollment_code'
,
slug
=
slugify
(
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
)
,
)
ProductAttribute
.
objects
.
create
(
...
...
ecommerce/extensions/catalogue/models.py
View file @
da2db893
...
...
@@ -4,6 +4,10 @@ from django.utils.translation import ugettext_lazy as _
from
oscar.apps.catalogue.abstract_models
import
AbstractProduct
,
AbstractProductAttributeValue
from
simple_history.models
import
HistoricalRecords
from
ecommerce.core.constants
import
(
COUPON_PRODUCT_CLASS_NAME
,
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
,
SEAT_PRODUCT_CLASS_NAME
)
class
Product
(
AbstractProduct
):
course
=
models
.
ForeignKey
(
...
...
@@ -13,6 +17,18 @@ class Product(AbstractProduct):
help_text
=
_
(
'Last date/time on which this product can be purchased.'
))
history
=
HistoricalRecords
()
@property
def
is_seat_product
(
self
):
return
self
.
get_product_class
()
.
name
==
SEAT_PRODUCT_CLASS_NAME
@property
def
is_enrollment_code_product
(
self
):
return
self
.
get_product_class
()
.
name
==
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
@property
def
is_coupon_product
(
self
):
return
self
.
get_product_class
()
.
name
==
COUPON_PRODUCT_CLASS_NAME
class
ProductAttributeValue
(
AbstractProductAttributeValue
):
history
=
HistoricalRecords
()
...
...
@@ -32,4 +48,4 @@ class Catalog(models.Model):
# noinspection PyUnresolvedReferences
from
oscar.apps.catalogue.models
import
*
# noqa pylint: disable=wildcard-import,unused-wildcard-import,wrong-import-position,ungrouped-imports
from
oscar.apps.catalogue.models
import
*
# noqa pylint: disable=wildcard-import,unused-wildcard-import,wrong-import-position,ungrouped-imports
,wrong-import-order
ecommerce/extensions/catalogue/tests/mixins.py
View file @
da2db893
...
...
@@ -3,9 +3,10 @@ from __future__ import unicode_literals
import
logging
from
oscar.core.loading
import
get_model
from
oscar.core.utils
import
slugify
from
oscar.test
import
factories
from
ecommerce.core.constants
import
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
from
ecommerce.core.constants
import
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
,
SEAT_PRODUCT_CLASS_NAME
from
ecommerce.courses.tests.factories
import
CourseFactory
from
ecommerce.tests.factories
import
PartnerFactory
...
...
@@ -94,7 +95,9 @@ class CourseCatalogTestMixin(object):
(
'id_verification_required'
,
'boolean'
),
(
'credit_hours'
,
'integer'
),
)
product_class
=
self
.
_create_product_class
(
'Seat'
,
'seat'
,
attributes
)
product_class
=
self
.
_create_product_class
(
SEAT_PRODUCT_CLASS_NAME
,
slugify
(
SEAT_PRODUCT_CLASS_NAME
),
attributes
)
return
product_class
@property
...
...
@@ -104,5 +107,7 @@ class CourseCatalogTestMixin(object):
(
'course_key'
,
'text'
),
(
'id_verification_required'
,
'boolean'
)
)
product_class
=
self
.
_create_product_class
(
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
,
'enrollment_code'
,
attributes
)
product_class
=
self
.
_create_product_class
(
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
,
slugify
(
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
),
attributes
)
return
product_class
ecommerce/extensions/catalogue/tests/test_coupons.py
View file @
da2db893
from
oscar.core.loading
import
get_model
from
oscar.test.factories
import
ProductFactory
,
VoucherFactory
from
ecommerce.core.constants
import
COUPON_PRODUCT_CLASS_NAME
from
ecommerce.extensions.voucher.models
import
CouponVouchers
from
ecommerce.tests.testcases
import
TestCase
...
...
@@ -13,7 +14,7 @@ class CouponProductTest(TestCase):
def
test_coupon_product
(
self
):
"""Test if a coupon product is properly created."""
coupon_product_class
,
_
=
ProductClass
.
objects
.
get_or_create
(
name
=
'coupon'
)
coupon_product_class
,
_
=
ProductClass
.
objects
.
get_or_create
(
name
=
COUPON_PRODUCT_CLASS_NAME
)
coupon_product
=
ProductFactory
(
product_class
=
coupon_product_class
,
title
=
'Test product'
...
...
ecommerce/extensions/catalogue/utils.py
View file @
da2db893
...
...
@@ -7,7 +7,7 @@ from django.conf import settings
from
django.db.utils
import
IntegrityError
from
oscar.core.loading
import
get_model
from
ecommerce.core.constants
import
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
,
SEAT
_PRODUCT_CLASS_NAME
from
ecommerce.core.constants
import
COUPON
_PRODUCT_CLASS_NAME
from
ecommerce.extensions.voucher.models
import
CouponVouchers
from
ecommerce.extensions.voucher.utils
import
create_vouchers
...
...
@@ -71,7 +71,7 @@ def create_coupon_product(
IntegrityError: An error occured when create_vouchers method returns
an IntegrityError exception
"""
product_class
=
ProductClass
.
objects
.
get
(
slug
=
'coupon'
)
product_class
=
ProductClass
.
objects
.
get
(
name
=
COUPON_PRODUCT_CLASS_NAME
)
coupon_product
=
Product
.
objects
.
create
(
title
=
title
,
product_class
=
product_class
)
ProductCategory
.
objects
.
get_or_create
(
product
=
coupon_product
,
category
=
category
)
...
...
@@ -126,23 +126,21 @@ def generate_sku(product, partner):
Example: 76E4E71
"""
product_class
=
product
.
get_product_class
()
if
not
product_class
:
if
not
product
.
get_product_class
():
raise
AttributeError
(
'Product has no product class'
)
if
product
_class
.
name
==
'Coupon'
:
if
product
.
is_coupon_product
:
_hash
=
' '
.
join
((
unicode
(
product
.
id
),
str
(
partner
.
id
)
))
elif
product
_class
.
name
==
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
:
elif
product
.
is_enrollment_code_product
:
_hash
=
' '
.
join
((
getattr
(
product
.
attr
,
'course_key'
,
''
),
getattr
(
product
.
attr
,
'seat_type'
,
''
),
unicode
(
partner
.
id
)
))
elif
product
_class
.
name
==
SEAT_PRODUCT_CLASS_NAME
:
elif
product
.
is_seat_product
:
_hash
=
' '
.
join
((
getattr
(
product
.
attr
,
'certificate_type'
,
''
),
product
.
attr
.
course_key
,
...
...
ecommerce/extensions/checkout/signals.py
View file @
da2db893
...
...
@@ -71,7 +71,7 @@ def send_course_purchase_email(sender, order=None, **kwargs): # pylint: disable
'Failed to send credit receipt notification. Credit seat product [
%
s] has no provider.'
,
product
.
id
)
return
elif
product
.
get_product_class
()
.
name
==
'Seat'
:
elif
product
.
is_seat_product
:
provider_data
=
get_credit_provider_details
(
access_token
=
order
.
site
.
siteconfiguration
.
access_token
,
credit_provider_id
=
credit_provider_id
,
...
...
ecommerce/extensions/fulfillment/modules.py
View file @
da2db893
...
...
@@ -15,7 +15,7 @@ from rest_framework import status
import
requests
from
requests.exceptions
import
ConnectionError
,
Timeout
from
ecommerce.core.constants
import
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
,
SEAT_PRODUCT_CLASS_NAME
from
ecommerce.core.constants
import
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
from
ecommerce.core.url_utils
import
get_lms_enrollment_api_url
from
ecommerce.courses.models
import
Course
from
ecommerce.courses.utils
import
mode_for_seat
...
...
@@ -126,7 +126,7 @@ class EnrollmentFulfillmentModule(BaseFulfillmentModule):
return
requests
.
post
(
enrollment_api_url
,
data
=
json
.
dumps
(
data
),
headers
=
headers
,
timeout
=
timeout
)
def
supports_line
(
self
,
line
):
return
line
.
product
.
get_product_class
()
.
name
==
SEAT_PRODUCT_CLASS_NAME
return
line
.
product
.
is_seat_product
def
get_supported_lines
(
self
,
lines
):
""" Return a list of lines that can be fulfilled through enrollment.
...
...
@@ -329,7 +329,7 @@ class CouponFulfillmentModule(BaseFulfillmentModule):
True if the line contains product of product class Coupon.
False otherwise.
"""
return
line
.
product
.
get_product_class
()
.
name
==
'Coupon'
return
line
.
product
.
is_coupon_product
def
get_supported_lines
(
self
,
lines
):
""" Return a list of lines containing products with Coupon product class
...
...
@@ -385,7 +385,7 @@ class EnrollmentCodeFulfillmentModule(BaseFulfillmentModule):
True if the line contains an Enrollment code.
False otherwise.
"""
return
line
.
product
.
get_product_class
()
.
name
==
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
return
line
.
product
.
is_enrollment_code_product
def
get_supported_lines
(
self
,
lines
):
""" Return a list of lines containing Enrollment code products that can be fulfilled.
...
...
ecommerce/extensions/fulfillment/tests/test_modules.py
View file @
da2db893
...
...
@@ -12,7 +12,9 @@ from oscar.test.newfactories import UserFactory, BasketFactory
from
requests.exceptions
import
ConnectionError
,
Timeout
from
testfixtures
import
LogCapture
from
ecommerce.core.constants
import
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
,
ENROLLMENT_CODE_SWITCH
from
ecommerce.core.constants
import
(
COUPON_PRODUCT_CLASS_NAME
,
ENROLLMENT_CODE_PRODUCT_CLASS_NAME
,
ENROLLMENT_CODE_SWITCH
,
SEAT_PRODUCT_CLASS_NAME
)
from
ecommerce.core.tests
import
toggle_switch
from
ecommerce.core.url_utils
import
get_lms_enrollment_api_url
from
ecommerce.coupons.tests.mixins
import
CouponMixin
...
...
@@ -152,7 +154,7 @@ class EnrollmentFulfillmentModuleTests(CourseCatalogTestMixin, FulfillmentTestMi
def
test_enrollment_module_fulfill_bad_attributes
(
self
):
"""Test that use of the Fulfillment Module fails when the product does not have attributes."""
ProductAttribute
.
objects
.
get
(
product_class__name
=
'Seat'
,
code
=
'course_key'
)
.
delete
()
ProductAttribute
.
objects
.
get
(
product_class__name
=
SEAT_PRODUCT_CLASS_NAME
,
code
=
'course_key'
)
.
delete
()
EnrollmentFulfillmentModule
()
.
fulfill_product
(
self
.
order
,
list
(
self
.
order
.
lines
.
all
()))
self
.
assertEqual
(
LINE
.
FULFILLMENT_CONFIGURATION_ERROR
,
self
.
order
.
lines
.
all
()[
0
]
.
status
)
...
...
@@ -374,7 +376,7 @@ class EnrollmentFulfillmentModuleTests(CourseCatalogTestMixin, FulfillmentTestMi
"""
catalog
=
Catalog
.
objects
.
create
(
partner
=
self
.
partner
)
coupon_product_class
,
_
=
ProductClass
.
objects
.
get_or_create
(
name
=
'coupon'
)
coupon_product_class
,
_
=
ProductClass
.
objects
.
get_or_create
(
name
=
COUPON_PRODUCT_CLASS_NAME
)
coupon
=
factories
.
create_product
(
product_class
=
coupon_product_class
,
title
=
'Test product'
...
...
ecommerce/extensions/order/models.py
View file @
da2db893
...
...
@@ -18,8 +18,7 @@ class Order(AbstractOrder):
@property
def
contains_coupon
(
self
):
""" Return a boolean if the order contains a Coupon. """
return
any
(
line
.
product
.
get_product_class
()
.
name
==
'Coupon'
for
line
in
self
.
basket
.
all_lines
())
return
any
(
line
.
product
.
is_coupon_product
for
line
in
self
.
basket
.
all_lines
())
class
PaymentEvent
(
AbstractPaymentEvent
):
...
...
ecommerce/extensions/order/tests/test_models.py
View file @
da2db893
import
ddt
from
oscar.test
import
factories
from
ecommerce.core.constants
import
COUPON_PRODUCT_CLASS_NAME
from
ecommerce.extensions.fulfillment.status
import
ORDER
from
ecommerce.tests.testcases
import
TestCase
...
...
@@ -31,8 +32,7 @@ class OrderTests(TestCase):
def
test_contains_coupon
(
self
):
self
.
assertFalse
(
self
.
order
.
contains_coupon
)
product_class
=
u'Coupon'
product
=
factories
.
create_product
(
product_class
=
product_class
)
product
=
factories
.
create_product
(
product_class
=
COUPON_PRODUCT_CLASS_NAME
)
basket
=
factories
.
create_basket
(
empty
=
True
)
factories
.
create_stockrecord
(
product
,
num_in_stock
=
1
)
basket
.
add_product
(
product
)
...
...
ecommerce/extensions/partner/strategy.py
View file @
da2db893
...
...
@@ -3,6 +3,8 @@ from django.utils import timezone
from
oscar.apps.partner
import
availability
,
strategy
from
oscar.core.loading
import
get_model
from
ecommerce.core.constants
import
SEAT_PRODUCT_CLASS_NAME
class
CourseSeatAvailabilityPolicyMixin
(
strategy
.
StockRequired
):
"""
...
...
@@ -15,7 +17,7 @@ class CourseSeatAvailabilityPolicyMixin(strategy.StockRequired):
@property
def
seat_class
(
self
):
ProductClass
=
get_model
(
'catalogue'
,
'ProductClass'
)
return
ProductClass
.
objects
.
get
(
slug
=
'seat'
)
return
ProductClass
.
objects
.
get
(
name
=
SEAT_PRODUCT_CLASS_NAME
)
def
availability_policy
(
self
,
product
,
stockrecord
):
""" A product is unavailable for non-admin users if the current date is
...
...
ecommerce/sailthru/signals.py
View file @
da2db893
...
...
@@ -5,7 +5,6 @@ from django.dispatch import receiver
from
ecommerce_worker.sailthru.v1.tasks
import
update_course_enrollment
from
oscar.core.loading
import
get_class
,
get_model
from
ecommerce.core.constants
import
SEAT_PRODUCT_CLASS_NAME
from
ecommerce.core.url_utils
import
get_lms_url
from
ecommerce.courses.utils
import
mode_for_seat
from
ecommerce.extensions.analytics.utils
import
silence_exceptions
...
...
@@ -58,9 +57,7 @@ def process_checkout_complete(sender, order=None, user=None, request=None, # py
product
=
line
.
product
# ignore everything except course seats. no support for coupons as of yet
product_class_name
=
product
.
get_product_class
()
.
name
if
product_class_name
==
SEAT_PRODUCT_CLASS_NAME
:
if
product
.
is_seat_product
:
price
=
line
.
line_price_excl_tax
course_id
=
product
.
course_id
...
...
@@ -90,11 +87,8 @@ def process_basket_addition(sender, product=None, user=None, request=None, baske
return
# ignore everything except course seats. no support for coupons as of yet
product_class_name
=
product
.
get_product_class
()
.
name
if
product_class_name
==
SEAT_PRODUCT_CLASS_NAME
:
if
product
.
is_seat_product
:
course_id
=
product
.
course_id
stock_record
=
product
.
stockrecords
.
first
()
if
stock_record
:
price
=
stock_record
.
price_excl_tax
...
...
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