Add coupon check

This commit is contained in:
Nathan Chapman 2022-05-11 20:09:45 -06:00
parent 058d682ddb
commit 4f43cc6a80
9 changed files with 237 additions and 10 deletions

View 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": []
}
}]

View 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),
),
]

View File

@ -54,7 +54,3 @@ class AddressTests(StaticLiveServerTestCase):
).text, ).text,
'USPS: Address Not Found.' 'USPS: Address Not Found.'
) )

View 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
)

View File

@ -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] = {

View File

@ -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,

View 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__)

View File

@ -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)

View File

@ -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)