Commit bfd104ac by Marko Jevtic

Remove API constants

parent b98bfc7f
...@@ -18,7 +18,6 @@ from oscar.core.loading import get_class, get_model ...@@ -18,7 +18,6 @@ from oscar.core.loading import get_class, get_model
from ecommerce.core.url_utils import get_ecommerce_url from ecommerce.core.url_utils import get_ecommerce_url
from ecommerce.core.views import StaffOnlyMixin from ecommerce.core.views import StaffOnlyMixin
from ecommerce.extensions.api import exceptions from ecommerce.extensions.api import exceptions
from ecommerce.extensions.api.constants import APIConstants as AC
from ecommerce.extensions.basket.utils import prepare_basket from ecommerce.extensions.basket.utils import prepare_basket
from ecommerce.extensions.checkout.mixins import EdxOrderPlacementMixin from ecommerce.extensions.checkout.mixins import EdxOrderPlacementMixin
...@@ -184,7 +183,7 @@ class CouponRedeemView(EdxOrderPlacementMixin, View): ...@@ -184,7 +183,7 @@ class CouponRedeemView(EdxOrderPlacementMixin, View):
return render(request, template_name, {'error': _('You are already enrolled in the course.')}) return render(request, template_name, {'error': _('You are already enrolled in the course.')})
basket = prepare_basket(request, product, voucher) basket = prepare_basket(request, product, voucher)
if basket.total_excl_tax == AC.FREE: if basket.total_excl_tax == 0:
self.place_free_order(basket) self.place_free_order(basket)
else: else:
return HttpResponseRedirect(reverse('basket:summary')) return HttpResponseRedirect(reverse('basket:summary'))
......
"""Ecommerce API constants."""
class APIDictionaryKeys(object):
"""Dictionary keys used repeatedly in the ecommerce API."""
BASKET_ID = u'id'
BENEFIT_TYPE = u'benefit_type'
BENEFIT_VALUE = u'benefit_value'
CATEGORY_IDS = u'category_ids'
CHECKOUT = u'checkout'
CLIENT = u'client'
CODE = u'code'
COUPON_ID = u'coupon_id'
END_DATE = u'end_date'
NOTE = u'note'
ORDER = u'order'
ORDER_NUMBER = u'number'
ORDER_TOTAL = u'total'
PAYMENT_DATA = u'payment_data'
PAYMENT_FORM_DATA = u'payment_form_data'
PAYMENT_PAGE_URL = u'payment_page_url'
PAYMENT_PROCESSOR_NAME = u'payment_processor_name'
PRICE = u'price'
PRODUCTS = u'products'
QUANTITY = u'quantity'
SHIPPING_CHARGE = u'shipping_charge'
SHIPPING_METHOD = u'shipping_method'
START_DATE = u'start_date'
STOCK_RECORD_IDS = u'stock_record_ids'
SKU = u'sku'
TITLE = u'title'
VOUCHER_TYPE = u'voucher_type'
class APIConstants(object):
"""Constants used throughout the ecommerce API."""
FREE = 0
KEYS = APIDictionaryKeys()
UPDATEABLE_VOUCHER_FIELDS = [
{
'request_data_key': KEYS.END_DATE,
'attribute': 'end_datetime'
},
{
'request_data_key': KEYS.START_DATE,
'attribute': 'start_datetime'
},
{
'request_data_key': KEYS.TITLE,
'attribute': 'name'
}
]
...@@ -4,7 +4,6 @@ import logging ...@@ -4,7 +4,6 @@ import logging
from oscar.core.loading import get_model, get_class from oscar.core.loading import get_model, get_class
from ecommerce.extensions.api import exceptions from ecommerce.extensions.api import exceptions
from ecommerce.extensions.api.constants import APIConstants as AC
NoShippingRequired = get_class('shipping.methods', 'NoShippingRequired') NoShippingRequired = get_class('shipping.methods', 'NoShippingRequired')
OrderTotalCalculator = get_class('checkout.calculators', 'OrderTotalCalculator') OrderTotalCalculator = get_class('checkout.calculators', 'OrderTotalCalculator')
...@@ -39,10 +38,10 @@ def get_order_metadata(basket): ...@@ -39,10 +38,10 @@ def get_order_metadata(basket):
total = OrderTotalCalculator().calculate(basket, shipping_charge) total = OrderTotalCalculator().calculate(basket, shipping_charge)
metadata = { metadata = {
AC.KEYS.ORDER_NUMBER: basket.order_number, 'number': basket.order_number,
AC.KEYS.SHIPPING_METHOD: shipping_method, 'shipping_method': shipping_method,
AC.KEYS.SHIPPING_CHARGE: shipping_charge, 'shipping_charge': shipping_charge,
AC.KEYS.ORDER_TOTAL: total, 'total': total,
} }
return metadata return metadata
...@@ -14,7 +14,6 @@ from oscar.test.factories import BasketFactory ...@@ -14,7 +14,6 @@ from oscar.test.factories import BasketFactory
from rest_framework.throttling import UserRateThrottle from rest_framework.throttling import UserRateThrottle
from ecommerce.extensions.api import exceptions as api_exceptions from ecommerce.extensions.api import exceptions as api_exceptions
from ecommerce.extensions.api.constants import APIConstants as AC
from ecommerce.extensions.api.v2.tests.views import OrderDetailViewTestMixin, JSON_CONTENT_TYPE from ecommerce.extensions.api.v2.tests.views import OrderDetailViewTestMixin, JSON_CONTENT_TYPE
from ecommerce.extensions.api.v2.views.baskets import BasketCreateView from ecommerce.extensions.api.v2.views.baskets import BasketCreateView
from ecommerce.extensions.payment import exceptions as payment_exceptions from ecommerce.extensions.payment import exceptions as payment_exceptions
...@@ -139,7 +138,7 @@ class BasketCreateViewTests(BasketCreationMixin, ThrottlingMixin, TransactionTes ...@@ -139,7 +138,7 @@ class BasketCreateViewTests(BasketCreationMixin, ThrottlingMixin, TransactionTes
def test_sku_missing(self): def test_sku_missing(self):
"""Test that requests without a SKU fail with appropriate messaging.""" """Test that requests without a SKU fail with appropriate messaging."""
request_data = {AC.KEYS.PRODUCTS: [{'not-sku': 'foo'}]} request_data = {'products': [{'not-sku': 'foo'}]}
response = self.client.post( response = self.client.post(
self.PATH, self.PATH,
data=json.dumps(request_data), data=json.dumps(request_data),
......
...@@ -18,7 +18,6 @@ from rest_framework import status ...@@ -18,7 +18,6 @@ from rest_framework import status
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, CouponMixin from ecommerce.coupons.tests.mixins import CourseCatalogMockMixin, CouponMixin
from ecommerce.courses.tests.factories import CourseFactory from ecommerce.courses.tests.factories import CourseFactory
from ecommerce.extensions.api.constants import APIConstants as AC
from ecommerce.extensions.api.v2.views.coupons import CouponViewSet from ecommerce.extensions.api.v2.views.coupons import CouponViewSet
from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin
from ecommerce.extensions.voucher.models import CouponVouchers from ecommerce.extensions.voucher.models import CouponVouchers
...@@ -230,9 +229,10 @@ class CouponViewSetTest(CouponMixin, CourseCatalogTestMixin, TestCase): ...@@ -230,9 +229,10 @@ class CouponViewSetTest(CouponMixin, CourseCatalogTestMixin, TestCase):
"""Test the order creation.""" """Test the order creation."""
self.create_coupon(partner=self.partner) self.create_coupon(partner=self.partner)
self.assertEqual(self.response_data[AC.KEYS.BASKET_ID], 1) self.assertDictEqual(
self.assertEqual(self.response_data[AC.KEYS.ORDER], 1) self.response_data,
self.assertEqual(self.response_data[AC.KEYS.PAYMENT_DATA][AC.KEYS.PAYMENT_PROCESSOR_NAME], 'Invoice') {'payment_data': {'payment_processor_name': 'Invoice'}, 'id': 1, 'order': 1, 'coupon_id': 3}
)
self.assertEqual(Order.objects.count(), 1) self.assertEqual(Order.objects.count(), 1)
self.assertEqual(Order.objects.first().status, 'Complete') self.assertEqual(Order.objects.first().status, 'Complete')
...@@ -243,7 +243,7 @@ class CouponViewSetTest(CouponMixin, CourseCatalogTestMixin, TestCase): ...@@ -243,7 +243,7 @@ class CouponViewSetTest(CouponMixin, CourseCatalogTestMixin, TestCase):
"""Test the update data dictionary""" """Test the update data dictionary"""
data = {} data = {}
for field in AC.UPDATEABLE_VOUCHER_FIELDS: for field in CouponVouchers.UPDATEABLE_VOUCHER_FIELDS:
CouponViewSet().create_update_data_dict( CouponViewSet().create_update_data_dict(
request_data=self.coupon_data, request_data=self.coupon_data,
request_data_key=field['request_data_key'], request_data_key=field['request_data_key'],
...@@ -333,10 +333,10 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat ...@@ -333,10 +333,10 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat
"""Test the response data given after the order was created.""" """Test the response data given after the order was created."""
self.assertEqual(self.response.status_code, 200) self.assertEqual(self.response.status_code, 200)
response_data = json.loads(self.response.content) response_data = json.loads(self.response.content)
self.assertEqual(response_data[AC.KEYS.COUPON_ID], 5) self.assertDictEqual(
self.assertEqual(response_data[AC.KEYS.BASKET_ID], 1) response_data,
self.assertEqual(response_data[AC.KEYS.ORDER], 1) {'payment_data': {'payment_processor_name': 'Invoice'}, 'id': 1, 'order': 1, 'coupon_id': 5}
self.assertEqual(response_data[AC.KEYS.PAYMENT_DATA][AC.KEYS.PAYMENT_PROCESSOR_NAME], 'Invoice') )
def test_order(self): def test_order(self):
"""Test the order data after order creation.""" """Test the order data after order creation."""
...@@ -394,7 +394,7 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat ...@@ -394,7 +394,7 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat
"""Test updating a coupon's title.""" """Test updating a coupon's title."""
data = { data = {
'id': self.coupon.id, 'id': self.coupon.id,
AC.KEYS.TITLE: 'New title' 'title': 'New title'
} }
response_data = self.get_response_json( response_data = self.get_response_json(
'PUT', 'PUT',
...@@ -412,8 +412,8 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat ...@@ -412,8 +412,8 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat
"""Test that updating a coupons date updates all of it's voucher dates.""" """Test that updating a coupons date updates all of it's voucher dates."""
data = { data = {
'id': self.coupon.id, 'id': self.coupon.id,
AC.KEYS.START_DATE: '2030-01-01', 'start_date': '2030-01-01',
AC.KEYS.END_DATE: '2035-01-01' 'end_date': '2035-01-01'
} }
response_data = self.get_response_json( response_data = self.get_response_json(
'PUT', 'PUT',
...@@ -433,7 +433,7 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat ...@@ -433,7 +433,7 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat
path = reverse('api:v2:coupons-detail', kwargs={'pk': self.coupon.id}) path = reverse('api:v2:coupons-detail', kwargs={'pk': self.coupon.id})
data = { data = {
'id': self.coupon.id, 'id': self.coupon.id,
AC.KEYS.BENEFIT_VALUE: 50 'benefit_value': 50
} }
self.client.put(path, json.dumps(data), 'application/json') self.client.put(path, json.dumps(data), 'application/json')
...@@ -447,7 +447,7 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat ...@@ -447,7 +447,7 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat
path = reverse('api:v2:coupons-detail', kwargs={'pk': self.coupon.id}) path = reverse('api:v2:coupons-detail', kwargs={'pk': self.coupon.id})
data = { data = {
'id': self.coupon.id, 'id': self.coupon.id,
AC.KEYS.CATEGORY_IDS: [category.id] 'category_ids': [category.id]
} }
self.client.put(path, json.dumps(data), 'application/json') self.client.put(path, json.dumps(data), 'application/json')
...@@ -461,7 +461,7 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat ...@@ -461,7 +461,7 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat
client = 'Člient 123' client = 'Člient 123'
data = { data = {
'id': self.coupon.id, 'id': self.coupon.id,
AC.KEYS.CLIENT: client 'client': client
} }
self.client.put(path, json.dumps(data), 'application/json') self.client.put(path, json.dumps(data), 'application/json')
...@@ -475,7 +475,7 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat ...@@ -475,7 +475,7 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat
path = reverse('api:v2:coupons-detail', kwargs={'pk': self.coupon.id}) path = reverse('api:v2:coupons-detail', kwargs={'pk': self.coupon.id})
data = { data = {
'id': self.coupon.id, 'id': self.coupon.id,
AC.KEYS.PRICE: 77 'price': 77
} }
self.client.put(path, json.dumps(data), 'application/json') self.client.put(path, json.dumps(data), 'application/json')
...@@ -489,7 +489,7 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat ...@@ -489,7 +489,7 @@ class CouponViewSetFunctionalTest(CouponMixin, CourseCatalogTestMixin, CourseCat
note = 'Thiš iš the tešt note.' note = 'Thiš iš the tešt note.'
data = { data = {
'id': self.coupon.id, 'id': self.coupon.id,
AC.KEYS.NOTE: note 'note': note
} }
self.client.put(path, json.dumps(data), 'application/json') self.client.put(path, json.dumps(data), 'application/json')
......
...@@ -14,7 +14,6 @@ from rest_framework.response import Response ...@@ -14,7 +14,6 @@ from rest_framework.response import Response
from ecommerce.extensions.analytics.utils import audit_log from ecommerce.extensions.analytics.utils import audit_log
from ecommerce.extensions.api import data as data_api, exceptions as api_exceptions from ecommerce.extensions.api import data as data_api, exceptions as api_exceptions
from ecommerce.extensions.api.constants import APIConstants as AC
from ecommerce.extensions.api.serializers import OrderSerializer from ecommerce.extensions.api.serializers import OrderSerializer
from ecommerce.extensions.checkout.mixins import EdxOrderPlacementMixin from ecommerce.extensions.checkout.mixins import EdxOrderPlacementMixin
from ecommerce.extensions.payment import exceptions as payment_exceptions from ecommerce.extensions.payment import exceptions as payment_exceptions
...@@ -134,11 +133,11 @@ class BasketCreateView(EdxOrderPlacementMixin, generics.CreateAPIView): ...@@ -134,11 +133,11 @@ class BasketCreateView(EdxOrderPlacementMixin, generics.CreateAPIView):
basket = Basket.create_basket(request.site, request.user) basket = Basket.create_basket(request.site, request.user)
basket_id = basket.id basket_id = basket.id
requested_products = request.data.get(AC.KEYS.PRODUCTS) requested_products = request.data.get('products')
if requested_products: if requested_products:
for requested_product in requested_products: for requested_product in requested_products:
# Ensure the requested products exist # Ensure the requested products exist
sku = requested_product.get(AC.KEYS.SKU) sku = requested_product.get('sku')
if sku: if sku:
try: try:
product = data_api.get_product(sku) product = data_api.get_product(sku)
...@@ -173,9 +172,9 @@ class BasketCreateView(EdxOrderPlacementMixin, generics.CreateAPIView): ...@@ -173,9 +172,9 @@ class BasketCreateView(EdxOrderPlacementMixin, generics.CreateAPIView):
api_exceptions.PRODUCT_OBJECTS_MISSING_USER_MESSAGE api_exceptions.PRODUCT_OBJECTS_MISSING_USER_MESSAGE
) )
if request.data.get(AC.KEYS.CHECKOUT) is True: if request.data.get('checkout') is True:
# Begin the checkout process, if requested, with the requested payment processor. # Begin the checkout process, if requested, with the requested payment processor.
payment_processor_name = request.data.get(AC.KEYS.PAYMENT_PROCESSOR_NAME) payment_processor_name = request.data.get('payment_processor_name')
if payment_processor_name: if payment_processor_name:
try: try:
payment_processor = get_processor_class_by_name(payment_processor_name) payment_processor = get_processor_class_by_name(payment_processor_name)
...@@ -229,20 +228,20 @@ class BasketCreateView(EdxOrderPlacementMixin, generics.CreateAPIView): ...@@ -229,20 +228,20 @@ class BasketCreateView(EdxOrderPlacementMixin, generics.CreateAPIView):
response_data = self._generate_basic_response(basket) response_data = self._generate_basic_response(basket)
if basket.total_excl_tax == AC.FREE: if basket.total_excl_tax == 0:
order = self.place_free_order(basket) order = self.place_free_order(basket)
# Note: Our order serializer could be used here, but in an effort to pare down the information # Note: Our order serializer could be used here, but in an effort to pare down the information
# returned by this endpoint, simply returning the order number will suffice for now. # returned by this endpoint, simply returning the order number will suffice for now.
response_data[AC.KEYS.ORDER] = {AC.KEYS.ORDER_NUMBER: order.number} response_data['order'] = {'number': order.number}
else: else:
parameters = payment_processor.get_transaction_parameters(basket, request=self.request) parameters = payment_processor.get_transaction_parameters(basket, request=self.request)
payment_page_url = parameters.pop('payment_page_url') payment_page_url = parameters.pop('payment_page_url')
response_data[AC.KEYS.PAYMENT_DATA] = { response_data['payment_data'] = {
AC.KEYS.PAYMENT_PROCESSOR_NAME: payment_processor.NAME, 'payment_processor_name': payment_processor.NAME,
AC.KEYS.PAYMENT_FORM_DATA: parameters, 'payment_form_data': parameters,
AC.KEYS.PAYMENT_PAGE_URL: payment_page_url, 'payment_page_url': payment_page_url,
} }
return response_data return response_data
...@@ -261,9 +260,9 @@ class BasketCreateView(EdxOrderPlacementMixin, generics.CreateAPIView): ...@@ -261,9 +260,9 @@ class BasketCreateView(EdxOrderPlacementMixin, generics.CreateAPIView):
# Note: A basket serializer could be used here, but in an effort to pare down the information # Note: A basket serializer could be used here, but in an effort to pare down the information
# returned by this endpoint, simply returning the basket ID will suffice for now. # returned by this endpoint, simply returning the basket ID will suffice for now.
response_data = { response_data = {
AC.KEYS.BASKET_ID: basket.id, 'id': basket.id,
AC.KEYS.ORDER: None, 'order': None,
AC.KEYS.PAYMENT_DATA: None, 'payment_data': None,
} }
return response_data return response_data
...@@ -284,7 +283,7 @@ class OrderByBasketRetrieveView(generics.RetrieveAPIView): ...@@ -284,7 +283,7 @@ class OrderByBasketRetrieveView(generics.RetrieveAPIView):
"""Allow the viewing of Orders by Basket. """ """Allow the viewing of Orders by Basket. """
permission_classes = (IsAuthenticated,) permission_classes = (IsAuthenticated,)
serializer_class = OrderSerializer serializer_class = OrderSerializer
lookup_field = AC.KEYS.ORDER_NUMBER lookup_field = 'number'
queryset = Order.objects.all() queryset = Order.objects.all()
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
......
...@@ -16,7 +16,6 @@ from rest_framework.response import Response ...@@ -16,7 +16,6 @@ from rest_framework.response import Response
from ecommerce.core.models import BusinessClient from ecommerce.core.models import BusinessClient
from ecommerce.coupons.utils import prepare_course_seat_types from ecommerce.coupons.utils import prepare_course_seat_types
from ecommerce.extensions.api import data as data_api from ecommerce.extensions.api import data as data_api
from ecommerce.extensions.api.constants import APIConstants as AC
from ecommerce.extensions.api.filters import ProductFilter from ecommerce.extensions.api.filters import ProductFilter
from ecommerce.extensions.api.serializers import CategorySerializer, CouponSerializer, CouponListSerializer from ecommerce.extensions.api.serializers import CategorySerializer, CouponSerializer, CouponListSerializer
from ecommerce.extensions.basket.utils import prepare_basket from ecommerce.extensions.basket.utils import prepare_basket
...@@ -39,31 +38,6 @@ Range = Range = get_model('offer', 'Range') ...@@ -39,31 +38,6 @@ Range = Range = get_model('offer', 'Range')
StockRecord = get_model('partner', 'StockRecord') StockRecord = get_model('partner', 'StockRecord')
Voucher = get_model('voucher', 'Voucher') Voucher = get_model('voucher', 'Voucher')
CATALOG_QUERY = 'catalog_query'
CLIENT = 'client'
COURSE_SEAT_TYPES = 'course_seat_types'
EMAIL_DOMAINS = 'email_domains'
INVOICE_DISCOUNT_TYPE = 'invoice_discount_type'
INVOICE_DISCOUNT_VALUE = 'invoice_discount_value'
INVOICE_NUMBER = 'invoice_number'
INVOICE_PAYMENT_DATE = 'invoice_payment_date'
INVOICE_TYPE = 'invoice_type'
TAX_DEDUCTED_SOURCE = 'tax_deducted_source'
UPDATEABLE_INVOICE_FIELDS = [
INVOICE_DISCOUNT_TYPE,
INVOICE_DISCOUNT_VALUE,
INVOICE_NUMBER,
INVOICE_PAYMENT_DATE,
INVOICE_TYPE,
TAX_DEDUCTED_SOURCE,
]
UPDATABLE_RANGE_FIELDS = [
CATALOG_QUERY,
COURSE_SEAT_TYPES,
]
class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet): class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet):
""" Coupon resource. """ """ Coupon resource. """
...@@ -81,7 +55,7 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet): ...@@ -81,7 +55,7 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet):
""" Retrieve the invoice information from the request data. """ """ Retrieve the invoice information from the request data. """
invoice_data = {} invoice_data = {}
for field in UPDATEABLE_INVOICE_FIELDS: for field in Invoice.UPDATEABLE_INVOICE_FIELDS:
self.create_update_data_dict( self.create_update_data_dict(
request_data=request_data, request_data=request_data,
request_data_key=field, request_data_key=field,
...@@ -113,25 +87,25 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet): ...@@ -113,25 +87,25 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet):
500 if an error occurs when attempting to create a coupon. 500 if an error occurs when attempting to create a coupon.
""" """
with transaction.atomic(): with transaction.atomic():
title = request.data[AC.KEYS.TITLE] title = request.data.get('title')
client_username = request.data[CLIENT] client_username = request.data.get('client')
stock_record_ids = request.data.get(AC.KEYS.STOCK_RECORD_IDS) stock_record_ids = request.data.get('stock_record_ids')
start_date = dateutil.parser.parse(request.data[AC.KEYS.START_DATE]) start_date = dateutil.parser.parse(request.data.get('start_date'))
end_date = dateutil.parser.parse(request.data[AC.KEYS.END_DATE]) end_date = dateutil.parser.parse(request.data.get('end_date'))
code = request.data[AC.KEYS.CODE] code = request.data.get('code')
benefit_type = request.data[AC.KEYS.BENEFIT_TYPE] benefit_type = request.data.get('benefit_type')
benefit_value = request.data[AC.KEYS.BENEFIT_VALUE] benefit_value = request.data.get('benefit_value')
voucher_type = request.data[AC.KEYS.VOUCHER_TYPE] voucher_type = request.data.get('voucher_type')
quantity = request.data[AC.KEYS.QUANTITY] quantity = request.data.get('quantity')
price = request.data[AC.KEYS.PRICE] price = request.data.get('price')
partner = request.site.siteconfiguration.partner partner = request.site.siteconfiguration.partner
categories = Category.objects.filter(id__in=request.data[AC.KEYS.CATEGORY_IDS]) categories = Category.objects.filter(id__in=request.data.get('category_ids'))
client, __ = BusinessClient.objects.get_or_create(name=client_username) client, __ = BusinessClient.objects.get_or_create(name=client_username)
note = request.data.get('note') note = request.data.get('note')
max_uses = request.data.get('max_uses') max_uses = request.data.get('max_uses')
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')
email_domains = request.data.get(EMAIL_DOMAINS) email_domains = request.data.get('email_domains')
if code: if code:
try: try:
...@@ -294,23 +268,23 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet): ...@@ -294,23 +268,23 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet):
order_metadata = data_api.get_order_metadata(basket) order_metadata = data_api.get_order_metadata(basket)
response_data = { response_data = {
AC.KEYS.COUPON_ID: coupon_id, 'coupon_id': coupon_id,
AC.KEYS.BASKET_ID: basket.id, 'id': basket.id,
AC.KEYS.ORDER: None, 'order': None,
AC.KEYS.PAYMENT_DATA: None, 'payment_data': None,
} }
basket.freeze() basket.freeze()
order = self.handle_order_placement( order = self.handle_order_placement(
order_number=order_metadata[AC.KEYS.ORDER_NUMBER],
user=basket.owner,
basket=basket, basket=basket,
shipping_address=None,
shipping_method=order_metadata[AC.KEYS.SHIPPING_METHOD],
shipping_charge=order_metadata[AC.KEYS.SHIPPING_CHARGE],
billing_address=None, billing_address=None,
order_total=order_metadata[AC.KEYS.ORDER_TOTAL], order_number=order_metadata['number'],
request=request order_total=order_metadata['total'],
request=request,
shipping_address=None,
shipping_charge=order_metadata['shipping_charge'],
shipping_method=order_metadata['shipping_method'],
user=basket.owner
) )
# Invoice payment processor invocation. # Invoice payment processor invocation.
...@@ -318,14 +292,14 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet): ...@@ -318,14 +292,14 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet):
payment_processor().handle_processor_response( payment_processor().handle_processor_response(
response={}, order=order, business_client=client, invoice_data=invoice_data response={}, order=order, business_client=client, invoice_data=invoice_data
) )
response_data[AC.KEYS.PAYMENT_DATA] = { response_data['payment_data'] = {
AC.KEYS.PAYMENT_PROCESSOR_NAME: 'Invoice' 'payment_processor_name': 'Invoice'
} }
response_data[AC.KEYS.ORDER] = order.id response_data['order'] = order.id
logger.info( logger.info(
'Created new order number [%s] from basket [%d]', 'Created new order number [%s] from basket [%d]',
order_metadata[AC.KEYS.ORDER_NUMBER], order_metadata['number'],
basket.id basket.id
) )
...@@ -340,7 +314,7 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet): ...@@ -340,7 +314,7 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet):
baskets = Basket.objects.filter(lines__product_id=coupon.id, status=Basket.SUBMITTED) baskets = Basket.objects.filter(lines__product_id=coupon.id, status=Basket.SUBMITTED)
data = {} data = {}
for field in AC.UPDATEABLE_VOUCHER_FIELDS: for field in CouponVouchers.UPDATEABLE_VOUCHER_FIELDS:
self.create_update_data_dict( self.create_update_data_dict(
request_data=request.data, request_data=request.data,
request_data_key=field['request_data_key'], request_data_key=field['request_data_key'],
...@@ -353,7 +327,7 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet): ...@@ -353,7 +327,7 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet):
range_data = {} range_data = {}
for field in UPDATABLE_RANGE_FIELDS: for field in Range.UPDATABLE_RANGE_FIELDS:
self.create_update_data_dict( self.create_update_data_dict(
request_data=request.data, request_data=request.data,
request_data_key=field, request_data_key=field,
...@@ -365,28 +339,28 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet): ...@@ -365,28 +339,28 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet):
voucher_range = vouchers.first().offers.first().benefit.range voucher_range = vouchers.first().offers.first().benefit.range
Range.objects.filter(id=voucher_range.id).update(**range_data) Range.objects.filter(id=voucher_range.id).update(**range_data)
benefit_value = request.data.get(AC.KEYS.BENEFIT_VALUE, '') benefit_value = request.data.get('benefit_value')
if benefit_value: if benefit_value:
self.update_coupon_benefit_value(benefit_value=benefit_value, vouchers=vouchers, coupon=coupon) self.update_coupon_benefit_value(benefit_value=benefit_value, vouchers=vouchers, coupon=coupon)
category_ids = request.data.get(AC.KEYS.CATEGORY_IDS, '') category_ids = request.data.get('category_ids')
if category_ids: if category_ids:
self.update_coupon_category(category_ids=category_ids, coupon=coupon) self.update_coupon_category(category_ids=category_ids, coupon=coupon)
client_username = request.data.get(AC.KEYS.CLIENT, '') client_username = request.data.get('client')
if client_username: if client_username:
self.update_coupon_client(baskets=baskets, client_username=client_username) self.update_coupon_client(baskets=baskets, client_username=client_username)
coupon_price = request.data.get(AC.KEYS.PRICE, '') coupon_price = request.data.get('price')
if coupon_price: if coupon_price:
StockRecord.objects.filter(product=coupon).update(price_excl_tax=coupon_price) StockRecord.objects.filter(product=coupon).update(price_excl_tax=coupon_price)
note = request.data.get(AC.KEYS.NOTE, None) note = request.data.get('note')
if note is not None: if note is not None:
coupon.attr.note = note coupon.attr.note = note
coupon.save() coupon.save()
email_domains = request.data.get(EMAIL_DOMAINS) email_domains = request.data.get('email_domains')
# Need to update for individual vouchers because in case of multiple # Need to update for individual vouchers because in case of multiple
# multi-use voucher each voucher will have individual offer. # multi-use voucher each voucher will have individual offer.
for voucher in vouchers.all(): for voucher in vouchers.all():
...@@ -409,7 +383,7 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet): ...@@ -409,7 +383,7 @@ class CouponViewSet(EdxOrderPlacementMixin, viewsets.ModelViewSet):
if request_data_key in request_data: if request_data_key in request_data:
value = request_data.get(request_data_key) value = request_data.get(request_data_key)
update_dict[update_dict_key] = prepare_course_seat_types(value) \ update_dict[update_dict_key] = prepare_course_seat_types(value) \
if update_dict_key == COURSE_SEAT_TYPES else value if update_dict_key == 'course_seat_types' else value
def update_coupon_benefit_value(self, benefit_value, coupon, vouchers): def update_coupon_benefit_value(self, benefit_value, coupon, vouchers):
""" """
......
...@@ -9,7 +9,6 @@ from rest_framework.permissions import IsAuthenticated, DjangoModelPermissions ...@@ -9,7 +9,6 @@ from rest_framework.permissions import IsAuthenticated, DjangoModelPermissions
from rest_framework.response import Response from rest_framework.response import Response
from ecommerce.extensions.api import serializers from ecommerce.extensions.api import serializers
from ecommerce.extensions.api.constants import APIConstants as AC
from ecommerce.extensions.api.filters import OrderFilter from ecommerce.extensions.api.filters import OrderFilter
from ecommerce.extensions.api.permissions import IsStaffOrOwner from ecommerce.extensions.api.permissions import IsStaffOrOwner
from ecommerce.extensions.api.throttles import ServiceUserThrottle from ecommerce.extensions.api.throttles import ServiceUserThrottle
...@@ -21,7 +20,7 @@ Order = get_model('order', 'Order') ...@@ -21,7 +20,7 @@ Order = get_model('order', 'Order')
class OrderViewSet(viewsets.ReadOnlyModelViewSet): class OrderViewSet(viewsets.ReadOnlyModelViewSet):
lookup_field = AC.KEYS.ORDER_NUMBER lookup_field = 'number'
permission_classes = (IsAuthenticated, IsStaffOrOwner, DjangoModelPermissions,) permission_classes = (IsAuthenticated, IsStaffOrOwner, DjangoModelPermissions,)
queryset = Order.objects.all() queryset = Order.objects.all()
serializer_class = serializers.OrderSerializer serializer_class = serializers.OrderSerializer
......
...@@ -12,7 +12,6 @@ import waffle ...@@ -12,7 +12,6 @@ import waffle
from ecommerce.extensions.analytics.utils import audit_log from ecommerce.extensions.analytics.utils import audit_log
from ecommerce.extensions.api import data as data_api from ecommerce.extensions.api import data as data_api
from ecommerce.extensions.api.constants import APIConstants as AC
from ecommerce.extensions.checkout.exceptions import BasketNotFreeError from ecommerce.extensions.checkout.exceptions import BasketNotFreeError
from ecommerce.extensions.customer.utils import Dispatcher from ecommerce.extensions.customer.utils import Dispatcher
...@@ -132,7 +131,7 @@ class EdxOrderPlacementMixin(OrderPlacementMixin): ...@@ -132,7 +131,7 @@ class EdxOrderPlacementMixin(OrderPlacementMixin):
BasketNotFreeError: if the basket is not free. BasketNotFreeError: if the basket is not free.
""" """
if basket.total_incl_tax != AC.FREE: if basket.total_incl_tax != 0:
raise BasketNotFreeError raise BasketNotFreeError
basket.freeze() basket.freeze()
...@@ -141,22 +140,22 @@ class EdxOrderPlacementMixin(OrderPlacementMixin): ...@@ -141,22 +140,22 @@ class EdxOrderPlacementMixin(OrderPlacementMixin):
logger.info( logger.info(
'Preparing to place order [%s] for the contents of basket [%d]', 'Preparing to place order [%s] for the contents of basket [%d]',
order_metadata[AC.KEYS.ORDER_NUMBER], order_metadata['number'],
basket.id, basket.id,
) )
# Place an order. If order placement succeeds, the order is committed # Place an order. If order placement succeeds, the order is committed
# to the database so that it can be fulfilled asynchronously. # to the database so that it can be fulfilled asynchronously.
order = self.handle_order_placement( order = self.handle_order_placement(
order_number=order_metadata[AC.KEYS.ORDER_NUMBER],
user=basket.owner,
basket=basket, basket=basket,
shipping_address=None,
shipping_method=order_metadata[AC.KEYS.SHIPPING_METHOD],
shipping_charge=order_metadata[AC.KEYS.SHIPPING_CHARGE],
billing_address=None, billing_address=None,
order_total=order_metadata[AC.KEYS.ORDER_TOTAL], order_number=order_metadata['number'],
request=request order_total=order_metadata['total'],
request=request,
shipping_address=None,
shipping_charge=order_metadata['shipping_charge'],
shipping_method=order_metadata['shipping_method'],
user=basket.owner
) )
return order return order
......
...@@ -32,6 +32,10 @@ class ConditionalOffer(AbstractConditionalOffer): ...@@ -32,6 +32,10 @@ class ConditionalOffer(AbstractConditionalOffer):
class Range(AbstractRange): class Range(AbstractRange):
UPDATABLE_RANGE_FIELDS = [
'catalog_query',
'course_seat_types',
]
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.CharField(max_length=255, blank=True, null=True) catalog_query = models.CharField(max_length=255, blank=True, null=True)
course_seat_types = models.CharField(max_length=255, blank=True, null=True) course_seat_types = models.CharField(max_length=255, blank=True, null=True)
......
...@@ -3,6 +3,20 @@ from django.db import models ...@@ -3,6 +3,20 @@ from django.db import models
class CouponVouchers(models.Model): class CouponVouchers(models.Model):
UPDATEABLE_VOUCHER_FIELDS = [
{
'request_data_key': 'end_date',
'attribute': 'end_datetime'
},
{
'request_data_key': 'start_date',
'attribute': 'start_datetime'
},
{
'request_data_key': 'title',
'attribute': 'name'
}
]
coupon = models.ForeignKey('catalogue.Product', related_name='coupon_vouchers') coupon = models.ForeignKey('catalogue.Product', related_name='coupon_vouchers')
vouchers = models.ManyToManyField('voucher.Voucher', blank=True, related_name='coupon_vouchers') vouchers = models.ManyToManyField('voucher.Voucher', blank=True, related_name='coupon_vouchers')
......
...@@ -22,6 +22,14 @@ class Invoice(TimeStampedModel): ...@@ -22,6 +22,14 @@ class Invoice(TimeStampedModel):
(PERCENTAGE, _('Percentage')), (PERCENTAGE, _('Percentage')),
(FIXED, _('Fixed')) (FIXED, _('Fixed'))
) )
UPDATEABLE_INVOICE_FIELDS = [
'invoice_discount_type',
'invoice_discount_value',
'invoice_number',
'invoice_payment_date',
'invoice_type',
'tax_deducted_source',
]
basket = models.ForeignKey('basket.Basket', null=True, blank=True) basket = models.ForeignKey('basket.Basket', null=True, blank=True)
order = models.ForeignKey('order.Order', null=True, blank=False) order = models.ForeignKey('order.Order', null=True, blank=False)
business_client = models.ForeignKey('core.BusinessClient', null=True, blank=False) business_client = models.ForeignKey('core.BusinessClient', null=True, blank=False)
......
...@@ -19,7 +19,6 @@ from threadlocals.threadlocals import set_thread_variable ...@@ -19,7 +19,6 @@ from threadlocals.threadlocals import set_thread_variable
from ecommerce.core.url_utils import get_lms_url from ecommerce.core.url_utils import get_lms_url
from ecommerce.courses.utils import mode_for_seat from ecommerce.courses.utils import mode_for_seat
from ecommerce.extensions.api.constants import APIConstants as AC
from ecommerce.extensions.fulfillment.signals import SHIPPING_EVENT_NAME from ecommerce.extensions.fulfillment.signals import SHIPPING_EVENT_NAME
from ecommerce.tests.factories import SiteConfigurationFactory from ecommerce.tests.factories import SiteConfigurationFactory
...@@ -121,15 +120,15 @@ class BasketCreationMixin(UserMixin, JwtMixin): ...@@ -121,15 +120,15 @@ class BasketCreationMixin(UserMixin, JwtMixin):
"""Issue a POST request to the basket creation endpoint.""" """Issue a POST request to the basket creation endpoint."""
request_data = {} request_data = {}
if skus: if skus:
request_data[AC.KEYS.PRODUCTS] = [] request_data['products'] = []
for sku in skus: for sku in skus:
request_data[AC.KEYS.PRODUCTS].append({AC.KEYS.SKU: sku}) request_data['products'].append({'sku': sku})
if checkout: if checkout:
request_data[AC.KEYS.CHECKOUT] = checkout request_data['checkout'] = checkout
if payment_processor_name: if payment_processor_name:
request_data[AC.KEYS.PAYMENT_PROCESSOR_NAME] = payment_processor_name request_data['payment_processor_name'] = payment_processor_name
if auth: if auth:
response = self.client.post( response = self.client.post(
...@@ -173,16 +172,16 @@ class BasketCreationMixin(UserMixin, JwtMixin): ...@@ -173,16 +172,16 @@ class BasketCreationMixin(UserMixin, JwtMixin):
)) ))
if requires_payment: if requires_payment:
self.assertIsNone(response.data[AC.KEYS.ORDER]) self.assertIsNone(response.data['order'])
self.assertIsNotNone(response.data[AC.KEYS.PAYMENT_DATA][AC.KEYS.PAYMENT_PROCESSOR_NAME]) self.assertIsNotNone(response.data['payment_data']['payment_processor_name'])
self.assertIsNotNone(response.data[AC.KEYS.PAYMENT_DATA][AC.KEYS.PAYMENT_FORM_DATA]) self.assertIsNotNone(response.data['payment_data']['payment_form_data'])
self.assertIsNotNone(response.data[AC.KEYS.PAYMENT_DATA][AC.KEYS.PAYMENT_PAGE_URL]) self.assertIsNotNone(response.data['payment_data']['payment_page_url'])
else: else:
self.assertEqual(response.data[AC.KEYS.ORDER][AC.KEYS.ORDER_NUMBER], Order.objects.get().number) self.assertEqual(response.data['order']['number'], Order.objects.get().number)
self.assertIsNone(response.data[AC.KEYS.PAYMENT_DATA]) self.assertIsNone(response.data['payment_data'])
else: else:
self.assertIsNone(response.data[AC.KEYS.ORDER]) self.assertIsNone(response.data['order'])
self.assertIsNone(response.data[AC.KEYS.PAYMENT_DATA]) self.assertIsNone(response.data['payment_data'])
class BusinessIntelligenceMixin(object): class BusinessIntelligenceMixin(object):
......
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