Add coupon check
This commit is contained in:
parent
058d682ddb
commit
4f43cc6a80
29
src/core/fixtures/coupons.json
Normal file
29
src/core/fixtures/coupons.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
[{
|
||||||
|
"model": "core.coupon",
|
||||||
|
"pk": 1,
|
||||||
|
"fields": {
|
||||||
|
"type": "entire_order",
|
||||||
|
"name": "Save 10%: Valid",
|
||||||
|
"code": "MAY2022",
|
||||||
|
"valid_from": "2022-05-01T06:00:00Z",
|
||||||
|
"valid_to": "2022-05-31T06:00:00Z",
|
||||||
|
"discount_value_type": "percentage",
|
||||||
|
"discount_value": "10.00",
|
||||||
|
"products": [],
|
||||||
|
"users": [1]
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"model": "core.coupon",
|
||||||
|
"pk": 2,
|
||||||
|
"fields": {
|
||||||
|
"type": "entire_order",
|
||||||
|
"name": "Save 10%: Invalid",
|
||||||
|
"code": "APR2022",
|
||||||
|
"valid_from": "2022-04-01T06:00:00Z",
|
||||||
|
"valid_to": "2022-04-30T06:00:00Z",
|
||||||
|
"discount_value_type": "percentage",
|
||||||
|
"discount_value": "10.00",
|
||||||
|
"products": [],
|
||||||
|
"users": []
|
||||||
|
}
|
||||||
|
}]
|
||||||
20
src/core/migrations/0009_coupon_users.py
Normal file
20
src/core/migrations/0009_coupon_users.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 4.0.2 on 2022-05-11 00:55
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('core', '0008_alter_order_coupon_alter_order_weight'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='coupon',
|
||||||
|
name='users',
|
||||||
|
field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -54,7 +54,3 @@ class AddressTests(StaticLiveServerTestCase):
|
|||||||
).text,
|
).text,
|
||||||
'USPS: Address Not Found.'
|
'USPS: Address Not Found.'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
100
src/functional_tests/test_coupon.py
Normal file
100
src/functional_tests/test_coupon.py
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
import os
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from django.test import TestCase, Client
|
||||||
|
from django.conf import settings
|
||||||
|
from selenium.webdriver.firefox.webdriver import WebDriver
|
||||||
|
from selenium.webdriver.common.keys import Keys
|
||||||
|
from selenium.webdriver.support.ui import Select
|
||||||
|
from selenium.common.exceptions import WebDriverException
|
||||||
|
from selenium.webdriver.common.by import By
|
||||||
|
from selenium.webdriver.support.ui import WebDriverWait
|
||||||
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
|
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class CouponTests(StaticLiveServerTestCase):
|
||||||
|
fixtures = ['products.json', 'accounts.json', 'coupons.json']
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super().setUpClass()
|
||||||
|
cls.browser = WebDriver()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
cls.browser.quit()
|
||||||
|
super().tearDownClass()
|
||||||
|
|
||||||
|
def login(self):
|
||||||
|
self.browser.get('%s%s' % (self.live_server_url, '/accounts/login/'))
|
||||||
|
username_input = self.browser.find_element_by_name("login")
|
||||||
|
username_input.send_keys('john@example.com')
|
||||||
|
password_input = self.browser.find_element_by_name("password")
|
||||||
|
password_input.send_keys('Bf25XBdP4vdt2X9L')
|
||||||
|
self.browser.find_element_by_xpath('//input[@value="Login"]').click()
|
||||||
|
|
||||||
|
def test_driver_has_session(self):
|
||||||
|
self.browser.get(self.live_server_url)
|
||||||
|
session_id = self.browser.get_cookie('sessionid')
|
||||||
|
self.assertTrue(session_id)
|
||||||
|
|
||||||
|
def test_apply_coupon_to_order(self):
|
||||||
|
# Add item to cart
|
||||||
|
self.browser.get(self.live_server_url + '/products/1/')
|
||||||
|
self.browser.find_element_by_xpath(
|
||||||
|
'//input[@value="Add to cart"]'
|
||||||
|
).click()
|
||||||
|
self.assertEqual(
|
||||||
|
self.browser.find_element_by_class_name('cart__count').text,
|
||||||
|
'1'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add coupon code
|
||||||
|
coupon_input = self.browser.find_element_by_id('id_code')
|
||||||
|
coupon_input.send_keys('MAY2022')
|
||||||
|
self.browser.find_element_by_xpath('//input[@value="Apply"]').click()
|
||||||
|
self.browser.find_element_by_xpath(
|
||||||
|
'//a[contains(text(), "Proceed to Checkout")]'
|
||||||
|
).click()
|
||||||
|
|
||||||
|
# Add address
|
||||||
|
self.assertEqual(
|
||||||
|
self.browser.title,
|
||||||
|
'Checkout | Port Townsend Roasting Co.'
|
||||||
|
)
|
||||||
|
full_name_input = self.browser.find_element_by_name("full_name")
|
||||||
|
full_name_input.send_keys('John Doe')
|
||||||
|
email_input = self.browser.find_element_by_id('id_email')
|
||||||
|
email_input.send_keys('contact@nathanjchapman.com')
|
||||||
|
street_address_1_input = self.browser.find_element_by_name(
|
||||||
|
'street_address_1'
|
||||||
|
)
|
||||||
|
street_address_1_input.send_keys('1579 Talon Dr')
|
||||||
|
city_input = self.browser.find_element_by_name('city')
|
||||||
|
city_input.send_keys('Logan')
|
||||||
|
state_select = select = Select(
|
||||||
|
self.browser.find_element_by_name('state')
|
||||||
|
)
|
||||||
|
state_select.select_by_value('UT')
|
||||||
|
postal_code_input = self.browser.find_element_by_name('postal_code')
|
||||||
|
postal_code_input.send_keys('84321')
|
||||||
|
self.browser.find_element_by_xpath(
|
||||||
|
'//input[@value="Continue to Payment"]'
|
||||||
|
).click()
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
self.browser.title,
|
||||||
|
'Checkout | Port Townsend Roasting Co.'
|
||||||
|
)
|
||||||
|
|
||||||
|
message_text = self.browser.find_element_by_css_selector(
|
||||||
|
'.messages p'
|
||||||
|
).text
|
||||||
|
self.assertEqual(
|
||||||
|
'Coupon already used.',
|
||||||
|
message_text
|
||||||
|
)
|
||||||
@ -24,6 +24,7 @@ from .payments import CreateOrder
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Cart:
|
class Cart:
|
||||||
def __init__(self, request):
|
def __init__(self, request):
|
||||||
self.request = request
|
self.request = request
|
||||||
@ -34,7 +35,9 @@ class Cart:
|
|||||||
cart = self.session[settings.CART_SESSION_ID] = {}
|
cart = self.session[settings.CART_SESSION_ID] = {}
|
||||||
self.cart = cart
|
self.cart = cart
|
||||||
|
|
||||||
def add(self, request, product, quantity=1, grind='', update_quantity=False):
|
def add(
|
||||||
|
self, request, product, quantity=1, grind='', update_quantity=False
|
||||||
|
):
|
||||||
product_id = str(product.id)
|
product_id = str(product.id)
|
||||||
if product_id not in self.cart:
|
if product_id not in self.cart:
|
||||||
self.cart[product_id] = {
|
self.cart[product_id] = {
|
||||||
|
|||||||
@ -61,7 +61,6 @@ class CartTest(TestCase):
|
|||||||
request = response.wsgi_request
|
request = response.wsgi_request
|
||||||
cart = Cart(request)
|
cart = Cart(request)
|
||||||
|
|
||||||
cart = Cart(request)
|
|
||||||
cart.add(
|
cart.add(
|
||||||
request,
|
request,
|
||||||
product=self.product,
|
product=self.product,
|
||||||
|
|||||||
18
src/storefront/tests/test_models.py
Normal file
18
src/storefront/tests/test_models.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import logging
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
from django.test import TestCase, Client, RequestFactory
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.conf import settings
|
||||||
|
from measurement.measures import Weight
|
||||||
|
from paypalcheckoutsdk.orders import OrdersCreateRequest, OrdersCaptureRequest
|
||||||
|
from paypalcheckoutsdk.core import PayPalHttpClient, SandboxEnvironment
|
||||||
|
|
||||||
|
from accounts.models import User, Address
|
||||||
|
from core.models import Product, Order
|
||||||
|
from core import CoffeeGrind
|
||||||
|
from storefront.forms import AddressForm, OrderCreateForm
|
||||||
|
from storefront.views import OrderCreateView, CheckoutAddressView
|
||||||
|
from storefront.cart import Cart
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
@ -9,7 +9,7 @@ from paypalcheckoutsdk.orders import OrdersCreateRequest, OrdersCaptureRequest
|
|||||||
from paypalcheckoutsdk.core import PayPalHttpClient, SandboxEnvironment
|
from paypalcheckoutsdk.core import PayPalHttpClient, SandboxEnvironment
|
||||||
|
|
||||||
from accounts.models import User, Address
|
from accounts.models import User, Address
|
||||||
from core.models import Product, Order
|
from core.models import Product, Order, Coupon
|
||||||
from core import CoffeeGrind
|
from core import CoffeeGrind
|
||||||
from storefront.forms import AddressForm, OrderCreateForm
|
from storefront.forms import AddressForm, OrderCreateForm
|
||||||
from storefront.views import OrderCreateView, CheckoutAddressView
|
from storefront.views import OrderCreateView, CheckoutAddressView
|
||||||
@ -17,7 +17,8 @@ from storefront.cart import Cart
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class CheckoutAddressViewTest(TestCase):
|
|
||||||
|
class CheckoutAddressViewTests(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.client = Client()
|
self.client = Client()
|
||||||
|
|
||||||
@ -30,3 +31,48 @@ class CheckoutAddressViewTest(TestCase):
|
|||||||
response = self.client.get(reverse('storefront:checkout-address'))
|
response = self.client.get(reverse('storefront:checkout-address'))
|
||||||
self.assertTrue(response.context['form'])
|
self.assertTrue(response.context['form'])
|
||||||
self.assertTrue(isinstance(response.context['form'], AddressForm))
|
self.assertTrue(isinstance(response.context['form'], AddressForm))
|
||||||
|
|
||||||
|
|
||||||
|
class OrderCreateViewTests(TestCase):
|
||||||
|
fixtures = ['accounts.json', 'coupons.json']
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
cls.customer = User.objects.get(pk=1)
|
||||||
|
cls.product = Product.objects.create(
|
||||||
|
name="Dante's Tornado",
|
||||||
|
description='Coffee',
|
||||||
|
sku='23987',
|
||||||
|
price=13.4,
|
||||||
|
weight=Weight(oz=16),
|
||||||
|
visible_in_listings=True
|
||||||
|
)
|
||||||
|
cls.order = Order.objects.create(
|
||||||
|
customer=cls.customer,
|
||||||
|
total_net_amount=13.4
|
||||||
|
)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
def test_used_coupon_creates_error_on_checkout(self):
|
||||||
|
session = self.client.session
|
||||||
|
session['shipping_address'] = {
|
||||||
|
'first_name': 'Nathan',
|
||||||
|
'last_name': 'Chapman',
|
||||||
|
'email': 'contact@nathanjchapman.com',
|
||||||
|
'street_address_1': '1504 N 230 E',
|
||||||
|
'street_address_2': '',
|
||||||
|
'city': 'North Logan',
|
||||||
|
'state': 'UT',
|
||||||
|
'postal_code': '84341'
|
||||||
|
}
|
||||||
|
session['coupon_code'] = 'MAY2022'
|
||||||
|
session.save()
|
||||||
|
|
||||||
|
response = self.client.get(
|
||||||
|
reverse('storefront:order-create'), follow=True
|
||||||
|
)
|
||||||
|
self.assertTrue(self.client.session.get('shipping_address'))
|
||||||
|
self.assertTemplateUsed(response, 'storefront/order_form.html')
|
||||||
|
self.assertContains(response, 'Coupon already used', status_code=200)
|
||||||
|
|||||||
@ -222,7 +222,16 @@ class OrderCreateView(CreateView):
|
|||||||
return HttpResponseRedirect(
|
return HttpResponseRedirect(
|
||||||
reverse('storefront:checkout-address')
|
reverse('storefront:checkout-address')
|
||||||
)
|
)
|
||||||
else:
|
elif self.request.session.get('coupon_code'):
|
||||||
|
address = self.request.session.get("shipping_address")
|
||||||
|
coupon = Coupon.objects.get(
|
||||||
|
code=self.request.session.get('coupon_code')
|
||||||
|
)
|
||||||
|
user = get_object_or_404(User, email=address['email'])
|
||||||
|
if user in coupon.users.all():
|
||||||
|
del self.request.session['coupon_code']
|
||||||
|
messages.warning(request, 'Coupon already used.')
|
||||||
|
|
||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
@ -259,6 +268,13 @@ class OrderCreateView(CreateView):
|
|||||||
shipping_address = self.request.session.get('shipping_address')
|
shipping_address = self.request.session.get('shipping_address')
|
||||||
form.instance.customer, form.instance.shipping_address = get_or_create_customer(self.request, form, shipping_address)
|
form.instance.customer, form.instance.shipping_address = get_or_create_customer(self.request, form, shipping_address)
|
||||||
form.instance.status = OrderStatus.DRAFT
|
form.instance.status = OrderStatus.DRAFT
|
||||||
|
coupon = get_object_or_404(
|
||||||
|
Coupon,
|
||||||
|
code=self.request.session.get('coupon_code')
|
||||||
|
)
|
||||||
|
if coupon:
|
||||||
|
form.instance.coupon = coupon
|
||||||
|
coupon.users.add(form.instance.customer)
|
||||||
self.object = form.save()
|
self.object = form.save()
|
||||||
bulk_list = cart.build_bulk_list(self.object)
|
bulk_list = cart.build_bulk_list(self.object)
|
||||||
objs = OrderLine.objects.bulk_create(bulk_list)
|
objs = OrderLine.objects.bulk_create(bulk_list)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user