Commit c9f1d6d0 by Zubair Afzal

Revert "ENT-110: Add Catalog selection option to Coupon create/edit form"

parent 104f6147
...@@ -149,7 +149,7 @@ class CouponMixin(object): ...@@ -149,7 +149,7 @@ class CouponMixin(object):
def create_coupon(self, benefit_type=Benefit.PERCENTAGE, benefit_value=100, catalog=None, def create_coupon(self, benefit_type=Benefit.PERCENTAGE, benefit_value=100, catalog=None,
catalog_query=None, client=None, code='', course_seat_types=None, email_domains=None, catalog_query=None, client=None, code='', course_seat_types=None, email_domains=None,
max_uses=None, note=None, partner=None, price=100, quantity=5, title='Test coupon', max_uses=None, note=None, partner=None, price=100, quantity=5, title='Test coupon',
voucher_type=Voucher.SINGLE_USE, course_catalog=None): voucher_type=Voucher.SINGLE_USE):
"""Helper method for creating a coupon. """Helper method for creating a coupon.
Arguments: Arguments:
...@@ -157,18 +157,12 @@ class CouponMixin(object): ...@@ -157,18 +157,12 @@ class CouponMixin(object):
benefit_value(int): The voucher benefit value benefit_value(int): The voucher benefit value
catalog(Catalog): Catalog of courses for which the coupon applies catalog(Catalog): Catalog of courses for which the coupon applies
catalog_query(str): Course query string catalog_query(str): Course query string
client (BusinessClient): Optional business client object
code(str): Custom coupon code code(str): Custom coupon code
course_catalog (int): Course catalog id from Catalog Service
course_seat_types(str): A string of comma-separated list of seat types course_seat_types(str): A string of comma-separated list of seat types
email_domains(str): A comma seperated list of email domains email_domains(str): A comma seperated list of email domains
max_uses (int): Number of Voucher max uses
note (str): Coupon note.
partner(Partner): Partner used for creating a catalog partner(Partner): Partner used for creating a catalog
price(int): Price of the coupon price(int): Price of the coupon
quantity (int): Number of vouchers to be created and associated with the coupon
title(str): Title of the coupon title(str): Title of the coupon
voucher_type (str): Voucher type
Returns: Returns:
coupon (Coupon) coupon (Coupon)
...@@ -190,7 +184,6 @@ class CouponMixin(object): ...@@ -190,7 +184,6 @@ class CouponMixin(object):
catalog_query=catalog_query, catalog_query=catalog_query,
category=self.category, category=self.category,
code=code, code=code,
course_catalog=course_catalog,
course_seat_types=course_seat_types, course_seat_types=course_seat_types,
email_domains=email_domains, email_domains=email_domains,
end_datetime=datetime.datetime(2020, 1, 1), end_datetime=datetime.datetime(2020, 1, 1),
......
import json
import httpretty
from django.conf import settings
from django.core.cache import cache
class CourseCatalogServiceMockMixin(object):
"""
Mocks for the Open edX service 'Course Catalog Service' responses.
"""
COURSE_DISCOVERY_CATALOGS_URL = '{}catalogs/'.format(
settings.COURSE_CATALOG_API_URL,
)
def setUp(self):
super(CourseCatalogServiceMockMixin, self).setUp()
cache.clear()
def mock_course_discovery_api_for_catalog_by_resource_id(self):
"""
Helper function to register course catalog API endpoint for a
single catalog with its resource id.
"""
catalog_id = 1
course_discovery_api_response = {
'count': 1,
'next': None,
'previous': None,
'results': [
{
'id': catalog_id,
'name': 'Catalog {}'.format(catalog_id),
'query': 'title: *',
'courses_count': 0,
'viewers': []
}
]
}
course_discovery_api_response_json = json.dumps(course_discovery_api_response)
single_catalog_uri = '{}{}/'.format(self.COURSE_DISCOVERY_CATALOGS_URL, catalog_id)
httpretty.register_uri(
method=httpretty.GET,
uri=single_catalog_uri,
body=course_discovery_api_response_json,
content_type='application/json'
)
def mock_course_discovery_api_for_catalogs(self, catalog_name_list):
"""
Helper function to register course catalog API endpoint for a
single catalog or multiple catalogs response.
"""
mocked_results = []
for catalog_index, catalog_name in enumerate(catalog_name_list):
catalog_id = catalog_index + 1
mocked_results.append(
{
'id': catalog_id,
'name': catalog_name,
'query': 'title: *',
'courses_count': 0,
'viewers': []
}
)
course_discovery_api_response = {
'count': len(catalog_name_list),
'next': None,
'previous': None,
'results': mocked_results
}
course_discovery_api_response_json = json.dumps(course_discovery_api_response)
httpretty.register_uri(
method=httpretty.GET,
uri=self.COURSE_DISCOVERY_CATALOGS_URL,
body=course_discovery_api_response_json,
content_type='application/json'
)
def mock_course_discovery_api_for_paginated_catalogs(self, catalog_name_list):
"""
Helper function to register course catalog API endpoint for multiple
catalogs with paginated response.
"""
mocked_api_responses = []
for catalog_index, catalog_name in enumerate(catalog_name_list):
catalog_id = catalog_index + 1
mocked_result = {
'id': catalog_id,
'name': catalog_name,
'query': 'title: *',
'courses_count': 0,
'viewers': []
}
next_page_url = None
if catalog_id < len(catalog_name_list):
# Not a last page so there will be more catalogs for another page
next_page_url = '{}?limit=1&offset={}'.format(
self.COURSE_DISCOVERY_CATALOGS_URL,
catalog_id
)
previous_page_url = None
if catalog_index != 0:
# Not a first page so there will always be catalogs on previous page
previous_page_url = '{}?limit=1&offset={}'.format(
self.COURSE_DISCOVERY_CATALOGS_URL,
catalog_index
)
course_discovery_api_paginated_response = {
'count': len(catalog_name_list),
'next': next_page_url,
'previous': previous_page_url,
'results': [mocked_result]
}
course_discovery_api_paginated_response_json = json.dumps(course_discovery_api_paginated_response)
mocked_api_responses.append(
httpretty.Response(body=course_discovery_api_paginated_response_json, content_type='application/json')
)
httpretty.register_uri(
method=httpretty.GET,
uri=self.COURSE_DISCOVERY_CATALOGS_URL,
responses=mocked_api_responses
)
def mock_course_discovery_api_for_failure(self):
"""
Helper function to register course catalog API endpoint for a
failure.
"""
httpretty.register_uri(
method=httpretty.GET,
uri=self.COURSE_DISCOVERY_CATALOGS_URL,
responses=[
httpretty.Response(body='Clunk', content_type='application/json', status_code=500)
]
)
import hashlib import hashlib
import ddt import ddt
from django.core.cache import cache
import httpretty import httpretty
from django.core.cache import cache
from ecommerce.core.constants import ENROLLMENT_CODE_SWITCH from ecommerce.core.constants import ENROLLMENT_CODE_SWITCH
from ecommerce.core.tests import toggle_switch from ecommerce.core.tests import toggle_switch
from ecommerce.core.tests.decorators import mock_course_catalog_api_client from ecommerce.core.tests.decorators import mock_course_catalog_api_client
from ecommerce.coupons.tests.mixins import CourseCatalogMockMixin from ecommerce.coupons.tests.mixins import CourseCatalogMockMixin
from ecommerce.courses.models import Course from ecommerce.courses.models import Course
from ecommerce.courses.tests.factories import CourseFactory from ecommerce.courses.tests.factories import CourseFactory
from ecommerce.courses.tests.mixins import CourseCatalogServiceMockMixin
from ecommerce.courses.utils import ( from ecommerce.courses.utils import (
get_certificate_type_display_value, get_course_info_from_catalog, mode_for_seat, get_course_catalogs get_certificate_type_display_value, get_course_info_from_catalog, mode_for_seat
) )
from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin
from ecommerce.tests.testcases import TestCase from ecommerce.tests.testcases import TestCase
...@@ -74,110 +74,3 @@ class UtilsTests(CourseCatalogTestMixin, CourseCatalogMockMixin, TestCase): ...@@ -74,110 +74,3 @@ class UtilsTests(CourseCatalogTestMixin, CourseCatalogMockMixin, TestCase):
def test_cert_display_assertion(self): def test_cert_display_assertion(self):
""" Verify assertion for invalid cert type """ """ Verify assertion for invalid cert type """
self.assertRaises(ValueError, lambda: get_certificate_type_display_value('junk')) self.assertRaises(ValueError, lambda: get_certificate_type_display_value('junk'))
@ddt.ddt
@httpretty.activate
class GetCourseCatalogUtilTests(CourseCatalogServiceMockMixin, TestCase):
def tearDown(self):
# Reset HTTPretty state (clean up registered urls and request history)
httpretty.reset()
def _assert_num_requests(self, count):
"""
DRY helper for verifying request counts.
"""
self.assertEqual(len(httpretty.httpretty.latest_requests), count)
def _assert_get_course_catalogs(self, catalog_name_list):
"""
Helper method to validate the response from the method
"get_course_catalogs".
"""
cache_key = '{}.catalog.api.data'.format(self.request.site.domain)
cache_key = hashlib.md5(cache_key).hexdigest()
cached_course_catalogs = cache.get(cache_key)
self.assertIsNone(cached_course_catalogs)
response = get_course_catalogs(self.request.site)
self.assertEqual(len(response), len(catalog_name_list))
for catalog_index, catalog in enumerate(response):
self.assertEqual(catalog['name'], catalog_name_list[catalog_index])
cached_course = cache.get(cache_key)
self.assertEqual(cached_course, response)
@mock_course_catalog_api_client
def test_get_course_catalogs_for_single_catalog_with_id(self):
"""
Verify that method "get_course_catalogs" returns proper response for a
single catalog by its id.
"""
self.mock_course_discovery_api_for_catalog_by_resource_id()
catalog_id = 1
cache_key = '{}.catalog.api.data.{}'.format(self.request.site.domain, catalog_id)
cache_key = hashlib.md5(cache_key).hexdigest()
cached_course_catalog = cache.get(cache_key)
self.assertIsNone(cached_course_catalog)
response = get_course_catalogs(self.request.site, catalog_id)
self.assertEqual(response['count'], 1)
self.assertEqual(response['results'][0]['name'], 'Catalog {}'.format(catalog_id))
cached_course = cache.get(cache_key)
self.assertEqual(cached_course, response)
# Verify the API was actually hit (not the cache)
self._assert_num_requests(1)
@mock_course_catalog_api_client
@ddt.data(
['Catalog 1'],
['Catalog 1', 'Catalog 2'],
)
def test_get_course_catalogs_for_single_page_api_response(self, catalog_name_list):
"""
Verify that method "get_course_catalogs" returns proper response for
single page Course Discovery API response and uses cache to return data
in case of same API request.
"""
self.mock_course_discovery_api_for_catalogs(catalog_name_list)
self._assert_get_course_catalogs(catalog_name_list)
# Verify the API was hit once
self._assert_num_requests(1)
# Now fetch the catalogs again and there should be no more actual call
# to Course Discovery API as the data will be fetched from the cache
get_course_catalogs(self.request.site)
self._assert_num_requests(1)
@mock_course_catalog_api_client
def test_get_course_catalogs_for_paginated_api_response(self):
"""
Verify that method "get_course_catalogs" returns all catalogs for
paginated Course Discovery API response for multiple catalogs.
"""
catalog_name_list = ['Catalog 1', 'Catalog 2', 'Catalog 3']
self.mock_course_discovery_api_for_paginated_catalogs(catalog_name_list)
self._assert_get_course_catalogs(catalog_name_list)
# Verify the API was hit for each catalog page
self._assert_num_requests(len(catalog_name_list))
@mock_course_catalog_api_client
def test_get_course_catalogs_for_failure(self):
"""
Verify that method "get_course_catalogs" raises exception in case
the Course Discovery API fails to return data.
"""
self.mock_course_discovery_api_for_failure()
with self.assertRaises(Exception):
get_course_catalogs(self.request.site)
import hashlib import hashlib
from urlparse import parse_qs, urlparse
from django.conf import settings from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
...@@ -34,67 +33,6 @@ def get_course_info_from_catalog(site, course_key): ...@@ -34,67 +33,6 @@ def get_course_info_from_catalog(site, course_key):
return course_run return course_run
def get_course_catalogs(site, resource_id=None):
"""
Get details related to course catalogs from Catalog Service.
Arguments:
site (Site): Site object containing Site Configuration data
resource_id (int or str): Identifies a specific resource to be retrieved
Returns:
dict: Course catalogs received from Course Catalog API
"""
resource = 'catalogs'
base_cache_key = '{}.catalog.api.data'.format(site.domain)
cache_key = '{}.{}'.format(base_cache_key, resource_id) if resource_id else base_cache_key
cache_key = hashlib.md5(cache_key).hexdigest()
cached = cache.get(cache_key)
if cached:
return cached
api = site.siteconfiguration.course_catalog_api_client
endpoint = getattr(api, resource)
response = endpoint(resource_id).get()
if resource_id:
results = response
else:
results = traverse_pagination(response, endpoint)
cache.set(cache_key, results, settings.COURSES_API_CACHE_TIMEOUT)
return results
def traverse_pagination(response, endpoint):
"""
Traverse a paginated API response.
Extracts and concatenates "results" (list of dict) returned by DRF-powered
APIs.
Arguments:
response (Dict): Current response dict from service API
endpoint (slumber Resource object): slumber Resource object from edx-rest-api-client
Returns:
list of dict.
"""
results = response.get('results', [])
next_page = response.get('next')
while next_page:
querystring = parse_qs(urlparse(next_page).query, keep_blank_values=True)
response = endpoint.get(**querystring)
results += response.get('results', [])
next_page = response.get('next')
return results
def get_certificate_type_display_value(certificate_type): def get_certificate_type_display_value(certificate_type):
display_values = { display_values = {
'audit': _('Audit'), 'audit': _('Audit'),
......
...@@ -531,7 +531,6 @@ class CouponSerializer(ProductPaymentInfoMixin, serializers.ModelSerializer): ...@@ -531,7 +531,6 @@ class CouponSerializer(ProductPaymentInfoMixin, serializers.ModelSerializer):
benefit_type = serializers.SerializerMethodField() benefit_type = serializers.SerializerMethodField()
benefit_value = serializers.SerializerMethodField() benefit_value = serializers.SerializerMethodField()
catalog_query = serializers.SerializerMethodField() catalog_query = serializers.SerializerMethodField()
course_catalog = serializers.SerializerMethodField()
category = serializers.SerializerMethodField() category = serializers.SerializerMethodField()
client = serializers.SerializerMethodField() client = serializers.SerializerMethodField()
code = serializers.SerializerMethodField() code = serializers.SerializerMethodField()
...@@ -559,9 +558,6 @@ class CouponSerializer(ProductPaymentInfoMixin, serializers.ModelSerializer): ...@@ -559,9 +558,6 @@ class CouponSerializer(ProductPaymentInfoMixin, serializers.ModelSerializer):
def get_catalog_query(self, obj): def get_catalog_query(self, obj):
return retrieve_offer(obj).condition.range.catalog_query return retrieve_offer(obj).condition.range.catalog_query
def get_course_catalog(self, obj):
return retrieve_offer(obj).condition.range.course_catalog
def get_category(self, obj): def get_category(self, obj):
category = ProductCategory.objects.filter(product=obj).first().category category = ProductCategory.objects.filter(product=obj).first().category
return CategorySerializer(category).data return CategorySerializer(category).data
...@@ -649,7 +645,7 @@ class CouponSerializer(ProductPaymentInfoMixin, serializers.ModelSerializer): ...@@ -649,7 +645,7 @@ class CouponSerializer(ProductPaymentInfoMixin, serializers.ModelSerializer):
class Meta(object): class Meta(object):
model = Product model = Product
fields = ( fields = (
'benefit_type', 'benefit_value', 'catalog_query', 'course_catalog', 'category', 'benefit_type', 'benefit_value', 'catalog_query', 'category',
'client', 'code', 'code_status', 'coupon_type', 'course_seat_types', 'client', 'code', 'code_status', 'coupon_type', 'course_seat_types',
'email_domains', 'end_date', 'id', 'last_edited', 'max_uses', 'email_domains', 'end_date', 'id', 'last_edited', 'max_uses',
'note', 'num_uses', 'payment_information', 'price', 'quantity', 'note', 'num_uses', 'payment_information', 'price', 'quantity',
......
...@@ -5,28 +5,25 @@ import httpretty ...@@ -5,28 +5,25 @@ import httpretty
from django.conf import settings from django.conf import settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.test import RequestFactory from django.test import RequestFactory
import mock
from oscar.core.loading import get_model from oscar.core.loading import get_model
from requests.exceptions import ConnectionError, Timeout from requests.exceptions import ConnectionError, Timeout
from slumber.exceptions import SlumberBaseException from slumber.exceptions import SlumberBaseException
from ecommerce.core.tests.decorators import mock_course_catalog_api_client from ecommerce.core.tests.decorators import mock_course_catalog_api_client
from ecommerce.coupons.tests.mixins import CourseCatalogMockMixin from ecommerce.coupons.tests.mixins import CourseCatalogMockMixin
from ecommerce.courses.tests.mixins import CourseCatalogServiceMockMixin
from ecommerce.extensions.api.serializers import ProductSerializer from ecommerce.extensions.api.serializers import ProductSerializer
from ecommerce.extensions.api.v2.tests.views.mixins import CatalogMixin from ecommerce.extensions.api.v2.tests.views.mixins import CatalogMixin
from ecommerce.extensions.api.v2.views.catalog import CatalogViewSet from ecommerce.extensions.api.v2.views.catalog import CatalogViewSet
from ecommerce.tests.mixins import ApiMockMixin from ecommerce.tests.mixins import ApiMockMixin
from ecommerce.tests.testcases import TestCase from ecommerce.tests.testcases import TestCase
Catalog = get_model('catalogue', 'Catalog') Catalog = get_model('catalogue', 'Catalog')
StockRecord = get_model('partner', 'StockRecord') StockRecord = get_model('partner', 'StockRecord')
@httpretty.activate @httpretty.activate
@ddt.ddt @ddt.ddt
class CatalogViewSetTest(CatalogMixin, CourseCatalogMockMixin, CourseCatalogServiceMockMixin, ApiMockMixin, TestCase): class CatalogViewSetTest(CatalogMixin, CourseCatalogMockMixin, ApiMockMixin, TestCase):
"""Test the Catalog and related products APIs.""" """Test the Catalog and related products APIs."""
catalog_list_path = reverse('api:v2:catalog-list') catalog_list_path = reverse('api:v2:catalog-list')
...@@ -133,60 +130,6 @@ class CatalogViewSetTest(CatalogMixin, CourseCatalogMockMixin, CourseCatalogServ ...@@ -133,60 +130,6 @@ class CatalogViewSetTest(CatalogMixin, CourseCatalogMockMixin, CourseCatalogServ
response = CatalogViewSet().preview(request) response = CatalogViewSet().preview(request)
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
@ddt.data(
(
'/api/v2/coupons/course_catalogs/',
['Catalog 1'],
['Catalog 1']
),
(
'/api/v2/coupons/course_catalogs/',
['Clean Catalog', 'ABC Catalog', 'New Catalog', 'Edx Catalog'],
['ABC Catalog', 'Clean Catalog', 'Edx Catalog', 'New Catalog']
),
)
@ddt.unpack
@mock_course_catalog_api_client
def test_course_catalogs_for_single_page_api_response(self, url, catalog_name_list, sorted_catalog_name_list):
"""
Test course catalogs list view "course_catalogs" for valid response
with catalogs in alphabetical order.
"""
self.mock_course_discovery_api_for_catalogs(catalog_name_list)
request = self.prepare_request(url)
response = CatalogViewSet().course_catalogs(request)
self.assertEqual(response.status_code, 200)
# Validate that the catalogs are sorted by name in alphabetical order
self._assert_get_course_catalogs_response_with_order(response, sorted_catalog_name_list)
def _assert_get_course_catalogs_response_with_order(self, response, catalog_name_list):
"""
Helper method to validate the response from the method
"course_catalogs".
"""
response_results = response.data.get('results')
self.assertEqual(len(response_results), len(catalog_name_list))
for catalog_index, catalog in enumerate(response_results):
self.assertEqual(catalog['name'], catalog_name_list[catalog_index])
@mock_course_catalog_api_client
@mock.patch('ecommerce.extensions.api.v2.views.catalog.logger.exception')
def test_get_course_catalogs_for_failure(self, mock_exception):
"""
Verify that the course catalogs list view "course_catalogs" returns
empty results list in case the Course Discovery API fails to return
data.
"""
self.mock_course_discovery_api_for_failure()
request = self.prepare_request('/api/v2/coupons/course_catalogs/')
response = CatalogViewSet().course_catalogs(request)
self.assertTrue(mock_exception.called)
self.assertEqual(response.data.get('results'), [])
class PartnerCatalogViewSetTest(CatalogMixin, TestCase): class PartnerCatalogViewSetTest(CatalogMixin, TestCase):
def setUp(self): def setUp(self):
......
...@@ -73,7 +73,6 @@ class CouponViewSetTest(CouponMixin, CourseCatalogTestMixin, TestCase): ...@@ -73,7 +73,6 @@ class CouponViewSetTest(CouponMixin, CourseCatalogTestMixin, TestCase):
'catalog_query': None, 'catalog_query': None,
'course_seat_types': None, 'course_seat_types': None,
'email_domains': None, 'email_domains': None,
'course_catalog': {'id': '', 'name': ''},
} }
def setup_site_configuration(self): def setup_site_configuration(self):
...@@ -239,8 +238,7 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat ...@@ -239,8 +238,7 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat
'start_datetime': str(now() - datetime.timedelta(days=10)), 'start_datetime': str(now() - datetime.timedelta(days=10)),
'stock_record_ids': [1, 2], 'stock_record_ids': [1, 2],
'title': 'Tešt čoupon', 'title': 'Tešt čoupon',
'voucher_type': Voucher.SINGLE_USE, 'voucher_type': Voucher.SINGLE_USE
'course_catalog': {'id': '', 'name': ''},
} }
self.response = self.client.post(COUPONS_LINK, json.dumps(self.data), 'application/json') self.response = self.client.post(COUPONS_LINK, json.dumps(self.data), 'application/json')
self.coupon = Product.objects.get(title=self.data['title']) self.coupon = Product.objects.get(title=self.data['title'])
......
...@@ -13,7 +13,6 @@ from slumber.exceptions import SlumberBaseException ...@@ -13,7 +13,6 @@ from slumber.exceptions import SlumberBaseException
from ecommerce.core.constants import DEFAULT_CATALOG_PAGE_SIZE from ecommerce.core.constants import DEFAULT_CATALOG_PAGE_SIZE
from ecommerce.coupons.utils import get_range_catalog_query_results from ecommerce.coupons.utils import get_range_catalog_query_results
from ecommerce.extensions.api import serializers from ecommerce.extensions.api import serializers
from ecommerce.courses.utils import get_course_catalogs
Catalog = get_model('catalogue', 'Catalog') Catalog = get_model('catalogue', 'Catalog')
...@@ -74,20 +73,3 @@ class CatalogViewSet(NestedViewSetMixin, ReadOnlyModelViewSet): ...@@ -74,20 +73,3 @@ class CatalogViewSet(NestedViewSetMixin, ReadOnlyModelViewSet):
logger.error('Unable to connect to Course Catalog service.') logger.error('Unable to connect to Course Catalog service.')
return Response(status=status.HTTP_400_BAD_REQUEST) return Response(status=status.HTTP_400_BAD_REQUEST)
return Response(status=status.HTTP_400_BAD_REQUEST) return Response(status=status.HTTP_400_BAD_REQUEST)
@action(is_for_list=True, methods=['get'])
def course_catalogs(self, request):
"""
Returns response with all course catalogs in the format:
["results": {"id": 1, "name": "Dummy Catalog"}]
"""
try:
results = get_course_catalogs(site=request.site)
except: # pylint: disable=bare-except
logger.exception('Failed to retrieve course catalogs data from the Course Discovery API.')
results = []
# Create catalogs list with sorting by name
catalogs = [{'id': catalog['id'], 'name': catalog['name']} for catalog in results]
data = {'results': sorted(catalogs, key=lambda catalog: catalog.get('name', '').lower())}
return Response(data=data)
...@@ -122,7 +122,6 @@ class CouponCreationTests(CouponMixin, TestCase): ...@@ -122,7 +122,6 @@ class CouponCreationTests(CouponMixin, TestCase):
catalog_query=None, catalog_query=None,
category=self.category, category=self.category,
code=code, code=code,
course_catalog=None,
course_seat_types=None, course_seat_types=None,
email_domains=None, email_domains=None,
end_datetime='2020-1-1', end_datetime='2020-1-1',
......
...@@ -36,8 +36,7 @@ def create_coupon_product( ...@@ -36,8 +36,7 @@ def create_coupon_product(
quantity, quantity,
start_datetime, start_datetime,
title, title,
voucher_type, voucher_type
course_catalog,
): ):
""" """
Creates a coupon product and a stock record for it. Creates a coupon product and a stock record for it.
...@@ -50,7 +49,6 @@ def create_coupon_product( ...@@ -50,7 +49,6 @@ def create_coupon_product(
category (dict): Contains category ID and name. category (dict): Contains category ID and name.
code (str): Voucher code. code (str): Voucher code.
course_seat_types (str): Comma-separated list of course seat types. course_seat_types (str): Comma-separated list of course seat types.
course_catalog (int): Course catalog id from Catalog Service
email_domains (str): Comma-separated list of email domains. email_domains (str): Comma-separated list of email domains.
end_datetime (Datetime): Voucher end Datetime. end_datetime (Datetime): Voucher end Datetime.
max_uses (int): Number of Voucher max uses. max_uses (int): Number of Voucher max uses.
...@@ -84,7 +82,6 @@ def create_coupon_product( ...@@ -84,7 +82,6 @@ def create_coupon_product(
catalog_query=catalog_query, catalog_query=catalog_query,
code=code or None, code=code or None,
coupon=coupon_product, coupon=coupon_product,
course_catalog=course_catalog,
course_seat_types=course_seat_types, course_seat_types=course_seat_types,
email_domains=email_domains, email_domains=email_domains,
end_datetime=end_datetime, end_datetime=end_datetime,
......
...@@ -2,9 +2,8 @@ from oscar.apps.offer.admin import * # pylint: disable=unused-import,wildcard-i ...@@ -2,9 +2,8 @@ from oscar.apps.offer.admin import * # pylint: disable=unused-import,wildcard-i
class RangeAdminExtended(admin.ModelAdmin): class RangeAdminExtended(admin.ModelAdmin):
list_display = ('name', 'catalog', 'course_catalog',) list_display = ('name', 'catalog',)
raw_id_fields = ('catalog',) raw_id_fields = ('catalog',)
search_fields = ['name', 'course_catalog']
admin.site.unregister(Range) admin.site.unregister(Range)
......
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('offer', '0007_auto_20161026_0856'),
]
operations = [
migrations.AddField(
model_name='range',
name='course_catalog',
field=models.PositiveIntegerField(help_text='Course catalog id from the Catalog Service.', null=True, blank=True),
),
]
...@@ -119,16 +119,10 @@ class Range(AbstractRange): ...@@ -119,16 +119,10 @@ class Range(AbstractRange):
UPDATABLE_RANGE_FIELDS = [ UPDATABLE_RANGE_FIELDS = [
'catalog_query', 'catalog_query',
'course_seat_types', 'course_seat_types',
'course_catalog',
] ]
ALLOWED_SEAT_TYPES = ['credit', 'professional', 'verified'] ALLOWED_SEAT_TYPES = ['credit', 'professional', 'verified']
catalog = models.ForeignKey('catalogue.Catalog', blank=True, null=True, related_name='ranges') catalog = models.ForeignKey('catalogue.Catalog', blank=True, null=True, related_name='ranges')
catalog_query = models.TextField(blank=True, null=True) catalog_query = models.TextField(blank=True, null=True)
course_catalog = models.PositiveIntegerField(
help_text=_('Course catalog id from the Catalog Service.'),
null=True,
blank=True
)
course_seat_types = models.CharField( course_seat_types = models.CharField(
max_length=255, max_length=255,
validators=[validate_credit_seat_type], validators=[validate_credit_seat_type],
......
...@@ -52,13 +52,5 @@ class Voucher(AbstractVoucher): ...@@ -52,13 +52,5 @@ class Voucher(AbstractVoucher):
logger.exception('Failed to create Voucher. Voucher start and end datetime fields must be type datetime.') logger.exception('Failed to create Voucher. Voucher start and end datetime fields must be type datetime.')
raise ValidationError(_('Voucher start and end datetime fields must be type datetime.')) raise ValidationError(_('Voucher start and end datetime fields must be type datetime.'))
@classmethod
def does_exist(cls, code):
try:
Voucher.objects.get(code=code)
return True
except Voucher.DoesNotExist:
return False
from oscar.apps.voucher.models import * # noqa pylint: disable=wildcard-import,unused-wildcard-import,wrong-import-position from oscar.apps.voucher.models import * # noqa pylint: disable=wildcard-import,unused-wildcard-import,wrong-import-position
...@@ -137,13 +137,6 @@ class UtilTests(CouponMixin, CourseCatalogMockMixin, CourseCatalogTestMixin, Lms ...@@ -137,13 +137,6 @@ class UtilTests(CouponMixin, CourseCatalogMockMixin, CourseCatalogTestMixin, Lms
course_seat_types=course_seat_types course_seat_types=course_seat_types
) )
def create_course_catalog_coupon(self, coupon_title, quantity, course_catalog):
return self.create_coupon(
title=coupon_title,
quantity=quantity,
course_catalog=course_catalog,
)
def use_voucher(self, order_num, voucher, user): def use_voucher(self, order_num, voucher, user):
""" """
Mark voucher as used by provided users Mark voucher as used by provided users
...@@ -249,39 +242,6 @@ class UtilTests(CouponMixin, CourseCatalogMockMixin, CourseCatalogTestMixin, Lms ...@@ -249,39 +242,6 @@ class UtilTests(CouponMixin, CourseCatalogMockMixin, CourseCatalogTestMixin, Lms
with self.assertRaises(IntegrityError): with self.assertRaises(IntegrityError):
create_vouchers(**self.data) create_vouchers(**self.data)
def test_create_course_catalog_coupon(self):
"""
Test course catalog coupon voucher creation with specified catalog id.
"""
coupon_title = 'Course catalog coupon'
quantity = 1
course_catalog = 1
course_catalog_coupon = self.create_course_catalog_coupon(
coupon_title=coupon_title,
quantity=quantity,
course_catalog=course_catalog,
)
self.assertEqual(course_catalog_coupon.title, coupon_title)
course_catalog_vouchers = course_catalog_coupon.attr.coupon_vouchers.vouchers.all()
self.assertEqual(course_catalog_vouchers.count(), quantity)
course_catalog_voucher_range = course_catalog_vouchers.first().offers.first().benefit.range
self.assertEqual(course_catalog_voucher_range.course_catalog, course_catalog)
self.data.update({
'name': coupon_title,
'benefit_value': course_catalog_vouchers.first().offers.first().benefit.value,
'code': course_catalog_vouchers.first().code,
'quantity': quantity,
'course_catalog': course_catalog,
'catalog': None,
'course_seat_types': None
})
with self.assertRaises(IntegrityError):
create_vouchers(**self.data)
def assert_report_first_row(self, row, coupon, voucher): def assert_report_first_row(self, row, coupon, voucher):
""" """
Verify that the first row fields contain the right data. Verify that the first row fields contain the right data.
......
...@@ -418,33 +418,26 @@ def create_vouchers( ...@@ -418,33 +418,26 @@ def create_vouchers(
_range=None, _range=None,
catalog_query=None, catalog_query=None,
course_seat_types=None, course_seat_types=None,
email_domains=None, email_domains=None):
course_catalog=None,
):
""" """
Create vouchers. Create vouchers.
Arguments: Args:
benefit_type (str): Type of benefit associated with vouchers. benefit_type (str): Type of benefit associated with vouchers.
benefit_value (Decimal): Value of benefit associated with vouchers. benefit_value (Decimal): Value of benefit associated with vouchers.
catalog (Catalog): Catalog associated with range of products catalog (Catalog): Catalog associated with range of products
to which a voucher can be applied to. to which a voucher can be applied to.
catalog_query (str): ElasticSearch query used by dynamic coupons. Defaults to None. coupon (Coupon): Coupon entity associated with vouchers.
code (str): Code associated with vouchers. Defaults to None. end_datetime (datetime): End date for voucher offer.
coupon (Coupon): Coupon entity associated with vouchers. name (str): Voucher name.
course_catalog (int): Course catalog id from Catalog Service. Defaults to None. quantity (int): Number of vouchers to be created.
course_seat_types (str): Comma-separated list of course seat types. start_datetime (datetime): Start date for voucher offer.
email_domains (str): List of email domains to restrict coupons. Defaults to None. voucher_type (str): Type of voucher.
end_datetime (datetime): End date for voucher offer. code (str): Code associated with vouchers. Defaults to None.
max_uses (int): Number of Voucher max uses. Defaults to None. email_domains (str): List of email domains to restrict coupons. Defaults to None.
name (str): Voucher name.
quantity (int): Number of vouchers to be created.
start_datetime (datetime): Start date for voucher offer.
voucher_type (str): Type of voucher.
_range (Range): Product range. Defaults to None.
Returns: Returns:
List[Voucher] List[Voucher]
""" """
logger.info("Creating [%d] vouchers product [%s]", quantity, coupon.id) logger.info("Creating [%d] vouchers product [%s]", quantity, coupon.id)
vouchers = [] vouchers = []
...@@ -457,13 +450,10 @@ def create_vouchers( ...@@ -457,13 +450,10 @@ def create_vouchers(
else: else:
logger.info("Creating [%d] vouchers for coupon [%s]", quantity, coupon.id) logger.info("Creating [%d] vouchers for coupon [%s]", quantity, coupon.id)
range_name = (_('Range for coupon [{coupon_id}]').format(coupon_id=coupon.id)) range_name = (_('Range for coupon [{coupon_id}]').format(coupon_id=coupon.id))
# make sure course catalog is None if its empty
course_catalog = course_catalog if course_catalog else None
product_range, __ = Range.objects.get_or_create( product_range, __ = Range.objects.get_or_create(
name=range_name, name=range_name,
catalog=catalog, catalog=catalog,
catalog_query=catalog_query, catalog_query=catalog_query,
course_catalog=course_catalog,
course_seat_types=course_seat_types, course_seat_types=course_seat_types,
) )
......
require([ require([
'backbone', 'backbone',
'collections/category_collection', 'collections/category_collection',
'collections/catalog_collection',
'ecommerce', 'ecommerce',
'routers/coupon_router', 'routers/coupon_router',
'utils/navigate', 'utils/navigate',
], ],
function (Backbone, function (Backbone,
CategoryCollection, CategoryCollection,
CatalogCollection,
ecommerce, ecommerce,
CouponRouter, CouponRouter,
navigate) { navigate) {
...@@ -22,10 +20,6 @@ require([ ...@@ -22,10 +20,6 @@ require([
ecommerce.coupons.categories = new CategoryCollection(); ecommerce.coupons.categories = new CategoryCollection();
ecommerce.coupons.categories.url = '/api/v2/coupons/categories/'; ecommerce.coupons.categories.url = '/api/v2/coupons/categories/';
ecommerce.coupons.categories.fetch({ async: false }); ecommerce.coupons.categories.fetch({ async: false });
ecommerce.coupons.catalogs = new CatalogCollection();
ecommerce.coupons.catalogs.fetch({ async: false });
couponApp.start(); couponApp.start();
// Handle navbar clicks. // Handle navbar clicks.
......
define([
'collections/paginated_collection',
'models/catalog_model'
],
function (PaginatedCollection,
Catalog) {
'use strict';
return PaginatedCollection.extend({
model: Catalog,
url: '/api/v2/catalogs/course_catalogs/'
}
);
}
);
define([
'backbone.relational'
],
function () {
'use strict';
return Backbone.RelationalModel.extend({
urlRoot: '/api/v2/catalogs/course_catalogs/'
});
}
);
...@@ -7,9 +7,7 @@ define([ ...@@ -7,9 +7,7 @@ define([
'underscore', 'underscore',
'moment', 'moment',
'collections/category_collection', 'collections/category_collection',
'collections/catalog_collection',
'models/category', 'models/category',
'models/catalog_model',
'utils/validation_patterns' 'utils/validation_patterns'
], ],
function (Backbone, function (Backbone,
...@@ -26,23 +24,15 @@ define([ ...@@ -26,23 +24,15 @@ define([
required: gettext('This field is required.'), required: gettext('This field is required.'),
number: gettext('This value must be a number.'), number: gettext('This value must be a number.'),
date: gettext('This value must be a date.'), date: gettext('This value must be a date.'),
seat_types: gettext('At least one seat type must be selected.') seat_types: gettext('At least one seat type must be selected.'),
}); });
_.extend(Backbone.Model.prototype, Backbone.Validation.mixin); _.extend(Backbone.Model.prototype, Backbone.Validation.mixin);
/* jshint esnext: true */
var CATALOG_TYPES = {
single_course: 'Single course',
multiple_courses: 'Multiple courses',
catalog: 'Catalog'
};
return Backbone.RelationalModel.extend({ return Backbone.RelationalModel.extend({
urlRoot: '/api/v2/coupons/', urlRoot: '/api/v2/coupons/',
defaults: { defaults: {
category: {id: 3, name: 'Affiliate Promotion'}, category: {id: 3, name: 'Affiliate Promotion'},
course_catalog: {id: '', name: ''},
code: '', code: '',
course_seats: [], course_seats: [],
course_seat_types: [], course_seat_types: [],
...@@ -52,11 +42,9 @@ define([ ...@@ -52,11 +42,9 @@ define([
quantity: 1, quantity: 1,
seats: [], seats: [],
stock_record_ids: [], stock_record_ids: [],
total_value: 0 total_value: 0,
}, },
catalogTypes: CATALOG_TYPES,
validation: { validation: {
benefit_value: { benefit_value: {
pattern: 'number', pattern: 'number',
...@@ -66,12 +54,7 @@ define([ ...@@ -66,12 +54,7 @@ define([
}, },
catalog_query: { catalog_query: {
required: function () { required: function () {
return this.get('catalog_type') === CATALOG_TYPES.multiple_courses; return this.get('catalog_type') === 'Multiple courses';
}
},
course_catalog: {
required: function () {
return this.get('catalog_type') === CATALOG_TYPES.catalog;
} }
}, },
category: {required: true}, category: {required: true},
...@@ -86,11 +69,11 @@ define([ ...@@ -86,11 +69,11 @@ define([
pattern: 'courseId', pattern: 'courseId',
msg: gettext('A valid course ID is required'), msg: gettext('A valid course ID is required'),
required: function () { required: function () {
return this.get('catalog_type') === CATALOG_TYPES.single_course; return this.get('catalog_type') === 'Single course';
} }
}, },
course_seat_types: function (val) { course_seat_types: function (val) {
if (this.get('catalog_type') === CATALOG_TYPES.multiple_courses && val.length === 0) { if (this.get('catalog_type') === 'Multiple courses' && val.length === 0) {
return Backbone.Validation.messages.seat_types; return Backbone.Validation.messages.seat_types;
} }
}, },
...@@ -150,7 +133,7 @@ define([ ...@@ -150,7 +133,7 @@ define([
// seat_type is for validation only, stock_record_ids holds the values // seat_type is for validation only, stock_record_ids holds the values
seat_type: { seat_type: {
required: function () { required: function () {
return this.get('catalog_type') === CATALOG_TYPES.single_course; return this.get('catalog_type') === 'Single course';
} }
}, },
start_date: function (val) { start_date: function (val) {
...@@ -168,7 +151,7 @@ define([ ...@@ -168,7 +151,7 @@ define([
return gettext('Must occur before end date'); return gettext('Must occur before end date');
} }
}, },
title: {required: true} title: {required: true},
}, },
initialize: function () { initialize: function () {
...@@ -202,29 +185,11 @@ define([ ...@@ -202,29 +185,11 @@ define([
updateSeatData: function () { updateSeatData: function () {
var seat_data, var seat_data,
seats = this.get('seats'), seats = this.get('seats');
catalogId,
catalogType;
catalogId = ''; this.set('catalog_type', this.has('catalog_query') ? 'Multiple courses': 'Single course');
if (this.has('course_catalog')) {
if (typeof this.get('course_catalog') === 'number') {
catalogId = this.get('course_catalog');
} else if (!$.isEmptyObject(this.get('course_catalog'))) {
catalogId = this.get('course_catalog').id;
}
}
if (this.has('catalog_query') && this.get('catalog_query') !== '') {
catalogType = this.catalogTypes.multiple_courses;
} else if (catalogId !== '') {
catalogType = this.catalogTypes.catalog;
} else {
catalogType = this.catalogTypes.single_course;
}
this.set('catalog_type', catalogType);
if (this.get('catalog_type') === this.catalogTypes.single_course) { if (this.get('catalog_type') === 'Single course') {
if (seats[0]) { if (seats[0]) {
seat_data = seats[0].attribute_values; seat_data = seats[0].attribute_values;
...@@ -232,7 +197,6 @@ define([ ...@@ -232,7 +197,6 @@ define([
this.set('course_id', this.getCourseID(seat_data)); this.set('course_id', this.getCourseID(seat_data));
this.updateTotalValue(this.getSeatPrice()); this.updateTotalValue(this.getSeatPrice());
} }
this.set('course_catalog', this.defaults.course_catalog);
} }
}, },
...@@ -247,7 +211,7 @@ define([ ...@@ -247,7 +211,7 @@ define([
'invoice_number': invoice.number, 'invoice_number': invoice.number,
'invoice_payment_date': invoice.payment_date, 'invoice_payment_date': invoice.payment_date,
'tax_deducted_source': invoice.tax_deducted_source, 'tax_deducted_source': invoice.tax_deducted_source,
'tax_deduction': tax_deducted 'tax_deduction': tax_deducted,
}); });
}, },
......
define([], function(){
'use strict';
var catalogs = [
{
'id': 1,
'name': 'All Courses Catalog'
},
{
'id': 2,
'name': 'No Courses Catalog'
},
{
'id': 3,
'name': 'edX Catalog'
},
{
'id': 4,
'name': 'Test Catalog 1'
},
{
'id': 5,
'name': 'Enterprise Catalog'
},
{
'id': 6,
'name': 'Test 2 Catalog'
},
{
'id': 7,
'name': 'Empty Catalog'
}
];
return catalogs;
});
...@@ -194,7 +194,6 @@ define([], function () { ...@@ -194,7 +194,6 @@ define([], function () {
'id': 4, 'id': 4,
'name': 'TESTCAT' 'name': 'TESTCAT'
}, },
course_catalog: {},
'price': '100.00', 'price': '100.00',
'invoice_type': 'Prepaid', 'invoice_type': 'Prepaid',
'invoice_discount_type': 'Percentage', 'invoice_discount_type': 'Percentage',
......
define([], function(){
'use strict';
var selected_catalog = {
1 : {
'id': 1,
'name': 'Courses Catalog 1'
},
2 : {
'id': 2,
'name': 'Courses Catalog 2'
}
};
return selected_catalog;
});
...@@ -20,8 +20,6 @@ if (!window.gettext) { ...@@ -20,8 +20,6 @@ if (!window.gettext) {
// Establish the global namespace // Establish the global namespace
window.ecommerce = window.ecommerce || {}; window.ecommerce = window.ecommerce || {};
window.ecommerce.coupons = window.ecommerce.coupons || {}; window.ecommerce.coupons = window.ecommerce.coupons || {};
window.ecommerce.catalogs = window.ecommerce.catalogs || {};
window.ecommerce.categories = window.ecommerce.categories || {};
window.ecommerce.credit = window.ecommerce.credit || {}; window.ecommerce.credit = window.ecommerce.credit || {};
// you can automatically get the test files using karma's configs // you can automatically get the test files using karma's configs
......
...@@ -14,13 +14,7 @@ define([ ...@@ -14,13 +14,7 @@ define([
return Backbone.Model.extend({ return Backbone.Model.extend({
defaults: { defaults: {
id: null, id: null,
category: {}, category: {}
course_catalog: {}
},
catalogTypes: {
single_course: 'Single course',
multiple_courses: 'Multiple courses',
catalog: 'Catalog'
}, },
isValid: function () { isValid: function () {
......
define([
'collections/catalog_collection',
'test/mock_data/catalogs'
],
function (CatalogCollection,
Mock_Catalogs) {
'use strict';
var collection,
response = Mock_Catalogs;
beforeEach(function () {
collection = new CatalogCollection();
});
describe('Catalog collection', function () {
describe('parse', function () {
it('should fetch the next page of results', function () {
spyOn(collection, 'fetch').and.returnValue(null);
response.next = '/api/v2/catalogs/course_catalogs/?page=2';
collection.parse(response);
expect(collection.url).toEqual(response.next);
expect(collection.fetch).toHaveBeenCalledWith({remove: false});
});
});
});
}
);
...@@ -81,32 +81,12 @@ define([ ...@@ -81,32 +81,12 @@ define([
model.validate(); model.validate();
expect(model.isValid()).toBeFalsy(); expect(model.isValid()).toBeFalsy();
model.set('catalog_query', '');
model.set('course_seat_types', ['verified']);
model.validate();
expect(model.isValid()).toBe(false);
model.set('catalog_query', '*:*'); model.set('catalog_query', '*:*');
model.set('course_seat_types', ['verified']); model.set('course_seat_types', ['verified']);
model.validate(); model.validate();
expect(model.isValid()).toBeTruthy(); expect(model.isValid()).toBeTruthy();
}); });
it('should validate course catalog for type Catalog', function () {
model.set('catalog_type', 'Catalog');
model.set('course_catalog', '');
model.validate();
expect(model.isValid()).toBe(false);
model.set('course_catalog', '');
model.validate();
expect(model.isValid()).toBe(false);
model.set('course_catalog', '1');
model.validate();
expect(model.isValid()).toBe(true);
});
it('should validate invoice data.', function() { it('should validate invoice data.', function() {
model.set('price', 'text'); model.set('price', 'text');
model.validate(); model.validate();
...@@ -193,36 +173,6 @@ define([ ...@@ -193,36 +173,6 @@ define([
}); });
}); });
describe('Should Update Seat Data Correctly.', function () {
it('should set correct catalog type for each seat.', function () {
// Test single course catalog type when a single course is selected for coupon creation.
var model = new Coupon({});
model.updateSeatData();
expect(model.get('catalog_type')).toEqual(model.catalogTypes.single_course);
// Test course catalog type when an existing course catalog is selected for coupon creation.
model = new Coupon({
course_catalog: {id: 1, name: 'Test Catalog'}
});
model.updateSeatData();
expect(model.get('catalog_type')).toEqual(model.catalogTypes.catalog);
model = new Coupon({
course_catalog: 1
});
model.updateSeatData();
expect(model.get('catalog_type')).toEqual(model.catalogTypes.catalog);
// Test multiple course type when an catalog query is given for coupon creation.
model = new Coupon({
catalog_query: '*:*'
});
model.updateSeatData();
expect(model.get('catalog_type')).toEqual(model.catalogTypes.multiple_courses);
});
});
describe('save', function () { describe('save', function () {
it('should POST enrollment data', function () { it('should POST enrollment data', function () {
var model, args, ajaxData; var model, args, ajaxData;
......
...@@ -4,7 +4,6 @@ define([ ...@@ -4,7 +4,6 @@ define([
'utils/utils', 'utils/utils',
'views/coupon_form_view', 'views/coupon_form_view',
'test/mock_data/categories', 'test/mock_data/categories',
'test/mock_data/catalogs',
'ecommerce' 'ecommerce'
], ],
function (Backbone, function (Backbone,
...@@ -12,7 +11,6 @@ define([ ...@@ -12,7 +11,6 @@ define([
Utils, Utils,
CouponFormView, CouponFormView,
Mock_Categories, Mock_Categories,
Mock_Catalogs,
ecommerce) { ecommerce) {
'use strict'; 'use strict';
...@@ -67,8 +65,7 @@ define([ ...@@ -67,8 +65,7 @@ define([
beforeEach(function () { beforeEach(function () {
ecommerce.coupons = { ecommerce.coupons = {
categories: Mock_Categories, categories: Mock_Categories
catalogs: Mock_Catalogs
}; };
}); });
......
...@@ -4,7 +4,6 @@ define([ ...@@ -4,7 +4,6 @@ define([
'views/alert_view', 'views/alert_view',
'models/coupon_model', 'models/coupon_model',
'test/mock_data/categories', 'test/mock_data/categories',
'test/mock_data/catalogs',
'ecommerce' 'ecommerce'
], ],
function ($, function ($,
...@@ -12,7 +11,6 @@ define([ ...@@ -12,7 +11,6 @@ define([
AlertView, AlertView,
Coupon, Coupon,
Mock_Categories, Mock_Categories,
Mock_Catalogs,
ecommerce) { ecommerce) {
'use strict'; 'use strict';
...@@ -22,8 +20,7 @@ define([ ...@@ -22,8 +20,7 @@ define([
beforeEach(function () { beforeEach(function () {
ecommerce.coupons = { ecommerce.coupons = {
categories: Mock_Categories, categories: Mock_Categories
catalogs: Mock_Catalogs
}; };
model = new Coupon(); model = new Coupon();
view = new CouponCreateEditView({ model: model, editing: false }).render(); view = new CouponCreateEditView({ model: model, editing: false }).render();
......
...@@ -6,8 +6,6 @@ define([ ...@@ -6,8 +6,6 @@ define([
'models/coupon_model', 'models/coupon_model',
'test/mock_data/categories', 'test/mock_data/categories',
'test/mock_data/coupons', 'test/mock_data/coupons',
'test/mock_data/catalogs',
'test/mock_data/selected_catalogs',
'test/spec-utils', 'test/spec-utils',
'ecommerce', 'ecommerce',
'test/custom-matchers' 'test/custom-matchers'
...@@ -19,8 +17,6 @@ define([ ...@@ -19,8 +17,6 @@ define([
Coupon, Coupon,
Mock_Categories, Mock_Categories,
Mock_Coupons, Mock_Coupons,
Mock_Catalogs,
Mock_Selected_Catalogs,
SpecUtils, SpecUtils,
ecommerce) { ecommerce) {
'use strict'; 'use strict';
...@@ -32,10 +28,9 @@ define([ ...@@ -32,10 +28,9 @@ define([
beforeEach(function () { beforeEach(function () {
ecommerce.coupons = { ecommerce.coupons = {
categories: Mock_Categories, categories: Mock_Categories
catalogs: Mock_Catalogs
}; };
model = new Coupon({course_catalog: Mock_Catalogs}); model = new Coupon();
view = new CouponFormView({ editing: false, model: model }).render(); view = new CouponFormView({ editing: false, model: model }).render();
}); });
...@@ -129,50 +124,6 @@ define([ ...@@ -129,50 +124,6 @@ define([
}); });
}); });
describe('course catalogs', function() {
it('course catalog drop down should be hidden when catalog is not selected', function() {
view.$('#single-course').prop('checked', true).trigger('change');
expect(SpecUtils.formGroup(view, '[name=course_catalog]')).not.toBeVisible();
view.$('#multiple-courses').prop('checked', true).trigger('change');
expect(SpecUtils.formGroup(view, '[name=course_catalog]')).not.toBeVisible();
view.$('#catalog').prop('checked', true).trigger('change');
expect(SpecUtils.formGroup(view, '[name=course_catalog]')).toBeVisible();
});
it('course catalog is setting properly', function() {
view.$('#catalog').prop('checked', true).trigger('change');
view.$('[name=course_catalog]').val(1).trigger('change');
expect(view.$('select[name=course_catalog] option:selected').text()).toEqual(Mock_Catalogs[0].name);
expect(view.$('[name=course_catalog]').val()).toEqual('1');
view.$('[name=course_catalog]').val(2).trigger('change');
expect(view.$('select[name=course_catalog] option:selected').text()).toEqual(Mock_Catalogs[1].name);
expect(view.$('[name=course_catalog]').val()).toEqual('2');
view.$('[name=course_catalog]').val(3).trigger('change');
expect(view.$('select[name=course_catalog] option:selected').text()).toEqual(Mock_Catalogs[2].name);
expect(view.$('[name=course_catalog]').val()).toEqual('3');
});
it('returning right course catalog when selected catalog is number', function() {
ecommerce.coupons = {
categories: Mock_Categories,
catalogs: Mock_Selected_Catalogs
};
var coupon_model = new Coupon({course_catalog: 1});
var coupon_form_view = new CouponFormView({ editing: true, model: coupon_model }).render();
expect(coupon_model.get('course_catalog')).toEqual({id: 1,'name': 'Courses Catalog 1'});
coupon_model.set({course_catalog: 2});
coupon_form_view.render();
expect(coupon_model.get('course_catalog')).toEqual({id: 2,'name': 'Courses Catalog 2'});
});
});
describe('discount code', function () { describe('discount code', function () {
var prepaid_invoice_fields = [ var prepaid_invoice_fields = [
'[name=invoice_number]', '[name=invoice_number]',
......
define([ define([
'jquery', 'jquery',
'backbone', 'backbone',
'ecommerce',
'underscore', 'underscore',
'underscore.string', 'underscore.string',
'moment', 'moment',
...@@ -12,7 +11,6 @@ define([ ...@@ -12,7 +11,6 @@ define([
], ],
function ($, function ($,
Backbone, Backbone,
ecommerce,
_, _,
_s, _s,
moment, moment,
...@@ -116,8 +114,6 @@ define([ ...@@ -116,8 +114,6 @@ define([
render: function () { render: function () {
var html, var html,
category = this.model.get('category').name, category = this.model.get('category').name,
courseCatalog = this.model.get('course_catalog'),
courseCatalogName = '',
invoice_data = this.formatInvoiceData(), invoice_data = this.formatInvoiceData(),
emailDomains = this.model.get('email_domains'), emailDomains = this.model.get('email_domains'),
template_data, template_data,
...@@ -127,16 +123,9 @@ define([ ...@@ -127,16 +123,9 @@ define([
price = _s.sprintf('$%s', this.model.get('price')); price = _s.sprintf('$%s', this.model.get('price'));
} }
if (typeof courseCatalog === 'number') {
courseCatalogName = ecommerce.coupons.catalogs.get(courseCatalog).get('name');
} else if (!$.isEmptyObject(courseCatalog)) {
courseCatalogName = courseCatalog.name;
}
template_data = { template_data = {
category: category, category: category,
coupon: this.model.toJSON(), coupon: this.model.toJSON(),
courseCatalogName: courseCatalogName,
courseSeatType: this.formatSeatTypes(), courseSeatType: this.formatSeatTypes(),
discountValue: this.discountValue(), discountValue: this.discountValue(),
endDateTime: this.formatDateTime(this.model.get('end_date')), endDateTime: this.formatDateTime(this.model.get('end_date')),
......
...@@ -168,28 +168,6 @@ define([ ...@@ -168,28 +168,6 @@ define([
'input[name=course_seat_types]': { 'input[name=course_seat_types]': {
observe: 'course_seat_types' observe: 'course_seat_types'
}, },
'select[name=course_catalog]': {
observe: 'course_catalog',
selectOptions: {
collection: function () {
return ecommerce.coupons.catalogs;
},
labelPath: 'name',
valuePath: 'id'
},
setOptions: {
validate: true
},
onGet: function (val) {
return val.id;
},
onSet: function (val) {
return {
id: val,
name: $('select[name=course_catalog] option:selected').text()
};
}
},
'input[name=email_domains]': { 'input[name=email_domains]': {
observe: 'email_domains', observe: 'email_domains',
onSet: function(val) { onSet: function(val) {
...@@ -274,7 +252,6 @@ define([ ...@@ -274,7 +252,6 @@ define([
'category', 'category',
'client', 'client',
'course_seat_types', 'course_seat_types',
'course_catalog',
'end_date', 'end_date',
'invoice_discount_type', 'invoice_discount_type',
'invoice_discount_value', 'invoice_discount_value',
...@@ -440,37 +417,22 @@ define([ ...@@ -440,37 +417,22 @@ define([
}, },
toggleCatalogTypeField: function() { toggleCatalogTypeField: function() {
if (this.model.get('catalog_type') === this.model.catalogTypes.single_course) { if (this.model.get('catalog_type') === 'Single course') {
this.model.unset('course_seat_types'); this.model.unset('course_seat_types');
this.model.unset('catalog_query'); this.model.unset('catalog_query');
this.model.set('course_catalog', this.model.defaults.course_catalog);
this.formGroup('[name=catalog_query]').addClass(this.hiddenClass); this.formGroup('[name=catalog_query]').addClass(this.hiddenClass);
this.formGroup('[name=course_seat_types]').addClass(this.hiddenClass); this.formGroup('[name=course_seat_types]').addClass(this.hiddenClass);
this.formGroup('[name=course_id]').removeClass(this.hiddenClass); this.formGroup('[name=course_id]').removeClass(this.hiddenClass);
this.formGroup('[name=seat_type]').removeClass(this.hiddenClass); this.formGroup('[name=seat_type]').removeClass(this.hiddenClass);
this.formGroup('[name=course_catalog]').addClass(this.hiddenClass);
} else if (this.model.get('catalog_type') === this.model.catalogTypes.catalog) {
this.model.unset('course_id');
this.model.unset('seat_type');
this.model.unset('stock_record_ids');
this.model.unset('catalog_query');
this.model.unset('course_seat_types');
this.formGroup('[name=catalog_query]').addClass(this.hiddenClass);
this.formGroup('[name=course_seat_types]').addClass(this.hiddenClass);
this.formGroup('[name=course_id]').addClass(this.hiddenClass);
this.formGroup('[name=seat_type]').addClass(this.hiddenClass);
this.formGroup('[name=course_catalog]').removeClass(this.hiddenClass);
} else { } else {
this.formGroup('[name=catalog_query]').removeClass(this.hiddenClass); this.formGroup('[name=catalog_query]').removeClass(this.hiddenClass);
this.formGroup('[name=course_seat_types]').removeClass(this.hiddenClass); this.formGroup('[name=course_seat_types]').removeClass(this.hiddenClass);
this.formGroup('[name=course_id]').addClass(this.hiddenClass); this.formGroup('[name=course_id]').addClass(this.hiddenClass);
this.formGroup('[name=seat_type]').addClass(this.hiddenClass); this.formGroup('[name=seat_type]').addClass(this.hiddenClass);
this.formGroup('[name=course_catalog]').addClass(this.hiddenClass);
this.$('[name=seat_type] option').remove(); this.$('[name=seat_type] option').remove();
this.model.unset('course_id'); this.model.unset('course_id');
this.model.unset('seat_type'); this.model.unset('seat_type');
this.model.unset('stock_record_ids'); this.model.unset('stock_record_ids');
this.model.set('course_catalog', this.model.defaults.course_catalog);
if (!this.model.get('course_seat_types')) { if (!this.model.get('course_seat_types')) {
this.model.set('course_seat_types', []); this.model.set('course_seat_types', []);
...@@ -652,8 +614,6 @@ define([ ...@@ -652,8 +614,6 @@ define([
render: function () { render: function () {
// Render the parent form/template // Render the parent form/template
var catalogId = '';
this.$el.html(this.template(this.model.attributes)); this.$el.html(this.template(this.model.attributes));
this.stickit(); this.stickit();
...@@ -670,21 +630,6 @@ define([ ...@@ -670,21 +630,6 @@ define([
this.$('.non-credit-seats').addClass(this.hiddenClass); this.$('.non-credit-seats').addClass(this.hiddenClass);
} }
} }
if (this.model.has('course_catalog')) {
if (typeof this.model.get('course_catalog') === 'number') {
catalogId = this.model.get('course_catalog');
} else if (!$.isEmptyObject(this.model.get('course_catalog'))) {
catalogId = this.model.get('course_catalog').id;
}
}
if (catalogId !== '') {
this.model.set('course_catalog', ecommerce.coupons.catalogs[catalogId]);
} else {
this.model.set('course_catalog', this.model.defaults.course_catalog);
}
this.disableNonEditableFields(); this.disableNonEditableFields();
this.toggleCouponTypeField(); this.toggleCouponTypeField();
this.toggleVoucherTypeField(); this.toggleVoucherTypeField();
...@@ -700,7 +645,7 @@ define([ ...@@ -700,7 +645,7 @@ define([
'coupon_type': this.codeTypes[0].value, 'coupon_type': this.codeTypes[0].value,
'voucher_type': this.voucherTypes[0].value, 'voucher_type': this.voucherTypes[0].value,
'benefit_type': 'Percentage', 'benefit_type': 'Percentage',
'catalog_type': this.model.catalogTypes.single_course, 'catalog_type': 'Single course',
'invoice_discount_type': 'Percentage', 'invoice_discount_type': 'Percentage',
'invoice_type': 'Prepaid', 'invoice_type': 'Prepaid',
'tax_deduction': 'No', 'tax_deduction': 'No',
......
...@@ -50,12 +50,6 @@ ...@@ -50,12 +50,6 @@
<div class="value"><%= coupon.catalog_query %></div> <div class="value"><%= coupon.catalog_query %></div>
</div> </div>
<%}%> <%}%>
<% if(courseCatalogName) {%>
<div class="info-item grid-item catalog-name">
<div class="heading"><%= gettext('Catalog:') %></div>
<div class="value"><%= courseCatalogName %></div>
</div>
<%}%>
<div class="info-item grid-item date-info"> <div class="info-item grid-item date-info">
<div class="start-date-info"> <div class="start-date-info">
<div class="heading"><%= gettext('Valid from:') %></div> <div class="heading"><%= gettext('Valid from:') %></div>
......
...@@ -164,8 +164,6 @@ ...@@ -164,8 +164,6 @@
<label for="single-course"><%= gettext('Single course') %></label> <label for="single-course"><%= gettext('Single course') %></label>
<input id="multiple-courses" type="radio" name="catalog_type" value="Multiple courses"> <input id="multiple-courses" type="radio" name="catalog_type" value="Multiple courses">
<label for="multiple-courses"><%= gettext('Multiple courses') %></label> <label for="multiple-courses"><%= gettext('Multiple courses') %></label>
<input id="catalog" type="radio" name="catalog_type" value="Catalog">
<label for="catalog"><%= gettext('Catalog') %></label>
</div> </div>
<p class="help-block"></p> <p class="help-block"></p>
</div> </div>
...@@ -206,11 +204,6 @@ ...@@ -206,11 +204,6 @@
</div> </div>
<div class="catalog_buttons"></div> <div class="catalog_buttons"></div>
</div> </div>
<div class="form-group course-catalog">
<label for="course-catalog"><%= gettext('Select from course catalogs:') %> *</label>
<select id="course-catalog" class="form-control" name="course_catalog"></select>
<p class="help-block"></p>
</div>
<div class="form-group email-domains"> <div class="form-group email-domains">
<label for="email-domains"><%= gettext('Email domains:') %> </label> <label for="email-domains"><%= gettext('Email domains:') %> </label>
<input id="email-domains" class="form-control" name="email_domains" maxlength="255"> <input id="email-domains" class="form-control" name="email_domains" maxlength="255">
......
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