Commit e01699f3 by Vedran Karacic

Set coupon custom code lower bound limit to 1.

parent 70915da6
...@@ -9,7 +9,6 @@ import ddt ...@@ -9,7 +9,6 @@ import ddt
import httpretty import httpretty
import pytz import pytz
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db.utils import IntegrityError
from django.test import RequestFactory from django.test import RequestFactory
from oscar.apps.catalogue.categories import create_from_breadcrumbs from oscar.apps.catalogue.categories import create_from_breadcrumbs
from oscar.core.loading import get_class, get_model from oscar.core.loading import get_class, get_model
...@@ -189,25 +188,6 @@ class CouponViewSetTest(CouponMixin, CourseCatalogTestMixin, TestCase): ...@@ -189,25 +188,6 @@ class CouponViewSetTest(CouponMixin, CourseCatalogTestMixin, TestCase):
self.assertEqual(custom_coupon.attr.coupon_vouchers.vouchers.count(), 1) self.assertEqual(custom_coupon.attr.coupon_vouchers.vouchers.count(), 1)
self.assertEqual(custom_coupon.attr.coupon_vouchers.vouchers.first().code, 'CUSTOMCODE') self.assertEqual(custom_coupon.attr.coupon_vouchers.vouchers.first().code, 'CUSTOMCODE')
def test_custom_code_integrity_error(self):
"""Test custom coupon code duplication."""
self.coupon_data.update({
'code': 'CUSTOMCODE',
'quantity': 1,
})
CouponViewSet().create_coupon_product(
title='Custom coupon',
price=100,
data=self.coupon_data
)
with self.assertRaises(IntegrityError):
CouponViewSet().create_coupon_product(
title='Coupon with integrity issue',
price=100,
data=self.coupon_data
)
def test_coupon_note(self): def test_coupon_note(self):
"""Test creating a coupon with a note.""" """Test creating a coupon with a note."""
self.coupon_data.update({ self.coupon_data.update({
...@@ -376,6 +356,16 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat ...@@ -376,6 +356,16 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat
coupon_data = json.loads(response.content)['results'][0] coupon_data = json.loads(response.content)['results'][0]
self.assertEqual(coupon_data['title'], 'Test coupon') self.assertEqual(coupon_data['title'], 'Test coupon')
def test_already_existing_code(self):
"""Test custom coupon code duplication."""
self.data.update({
'code': 'CUSTOMCODE',
'quantity': 1,
})
self.client.post(COUPONS_LINK, data=self.data, format='json')
response = self.client.post(COUPONS_LINK, data=self.data, format='json')
self.assertEqual(response.status_code, 400)
def test_update(self): def test_update(self):
"""Test updating a coupon.""" """Test updating a coupon."""
coupon = Product.objects.get(title='Test coupon') coupon = Product.objects.get(title='Test coupon')
......
...@@ -6,7 +6,6 @@ from decimal import Decimal ...@@ -6,7 +6,6 @@ from decimal import Decimal
import dateutil.parser import dateutil.parser
from django.conf import settings from django.conf import settings
from django.db import transaction from django.db import transaction
from django.db.utils import IntegrityError
from django.http import Http404 from django.http import Http404
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from oscar.core.loading import get_model from oscar.core.loading import get_model
...@@ -106,6 +105,8 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet): ...@@ -106,6 +105,8 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet):
Returns: Returns:
200 if the order was created successfully; the basket ID is included in the response 200 if the order was created successfully; the basket ID is included in the response
body along with the order ID and payment information. body along with the order ID and payment information.
400 if a custom code is received that already exists,
if a course mode is selected that is not supported.
401 if an unauthenticated request is denied permission to access the endpoint. 401 if an unauthenticated request is denied permission to access the endpoint.
429 if the client has made requests at a rate exceeding that allowed by the configured rate limit. 429 if the client has made requests at a rate exceeding that allowed by the configured rate limit.
500 if an error occurs when attempting to create a coupon. 500 if an error occurs when attempting to create a coupon.
...@@ -130,6 +131,16 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet): ...@@ -130,6 +131,16 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet):
catalog_query = request.data.get(CATALOG_QUERY) catalog_query = request.data.get(CATALOG_QUERY)
course_seat_types = request.data.get(COURSE_SEAT_TYPES) course_seat_types = request.data.get(COURSE_SEAT_TYPES)
if code:
try:
Voucher.objects.get(code=code)
return Response(
'A coupon with code {code} already exists.'.format(code=code),
status=status.HTTP_400_BAD_REQUEST
)
except Voucher.DoesNotExist:
pass
invoice_data = self.retrieve_invoice_data(request.data) invoice_data = self.retrieve_invoice_data(request.data)
if course_seat_types: if course_seat_types:
...@@ -218,10 +229,6 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet): ...@@ -218,10 +229,6 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet):
Returns: Returns:
A coupon product object. A coupon product object.
Raises:
IntegrityError: An error occured when create_vouchers method returns
an IntegrityError exception
""" """
product_class = ProductClass.objects.get(slug='coupon') product_class = ProductClass.objects.get(slug='coupon')
...@@ -231,25 +238,21 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet): ...@@ -231,25 +238,21 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet):
# Vouchers are created during order and not fulfillment like usual # Vouchers are created during order and not fulfillment like usual
# because we want vouchers to be part of the line in the order. # because we want vouchers to be part of the line in the order.
try: create_vouchers(
create_vouchers( name=title,
name=title, benefit_type=data['benefit_type'],
benefit_type=data['benefit_type'], benefit_value=Decimal(data['benefit_value']),
benefit_value=Decimal(data['benefit_value']), catalog=data['catalog'],
catalog=data['catalog'], coupon=coupon_product,
coupon=coupon_product, end_datetime=data['end_date'],
end_datetime=data['end_date'], code=data['code'] or None,
code=data['code'] or None, quantity=int(data['quantity']),
quantity=int(data['quantity']), start_datetime=data['start_date'],
start_datetime=data['start_date'], voucher_type=data['voucher_type'],
voucher_type=data['voucher_type'], max_uses=data['max_uses'],
max_uses=data['max_uses'], catalog_query=data['catalog_query'],
catalog_query=data['catalog_query'], course_seat_types=data['course_seat_types']
course_seat_types=data['course_seat_types'] )
)
except IntegrityError:
logger.exception('Failed to create vouchers for [%s] coupon.', coupon_product.title)
raise
coupon_vouchers = CouponVouchers.objects.get(coupon=coupon_product) coupon_vouchers = CouponVouchers.objects.get(coupon=coupon_product)
......
...@@ -92,9 +92,10 @@ define([ ...@@ -92,9 +92,10 @@ define([
} }
}, },
code: { code: {
pattern: /^[a-zA-Z0-9]+$/,
required: false, required: false,
rangeLength: [8, 16], rangeLength: [1, 16],
msg: gettext('Code field must be empty or between 8 and 16 characters') msg: gettext('This field must be empty or contain 1-16 alphanumeric characters.')
}, },
catalog_query: { catalog_query: {
required: function () { required: function () {
......
...@@ -100,6 +100,16 @@ define([ ...@@ -100,6 +100,16 @@ define([
model.validate(); model.validate();
expect(model.isValid()).toBeTruthy(); expect(model.isValid()).toBeTruthy();
}); });
it('should validate coupon code.', function() {
model.set('code', '!#$%&/()=');
model.validate();
expect(model.isValid()).toBeFalsy();
model.set('code', 'CODE12345');
model.validate();
expect(model.isValid()).toBeTruthy();
});
}); });
describe('test model methods', function () { describe('test model methods', function () {
......
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