Add basic product alternatives
This commit is contained in:
parent
a758af854a
commit
7f4ac4ba6a
1
Pipfile
1
Pipfile
@ -25,6 +25,7 @@ usps-api = "*"
|
|||||||
[dev-packages]
|
[dev-packages]
|
||||||
django-debug-toolbar = "*"
|
django-debug-toolbar = "*"
|
||||||
selenium = "*"
|
selenium = "*"
|
||||||
|
pycodestyle = "*"
|
||||||
|
|
||||||
[requires]
|
[requires]
|
||||||
python_version = "3.10"
|
python_version = "3.10"
|
||||||
|
|||||||
14
Pipfile.lock
generated
14
Pipfile.lock
generated
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "0087f9e4fd44233bc6329f7a844a96ae4001ee9d173323e7a715c7295844163c"
|
"sha256": "0847a2b4a481c279572830e8044273e65178f30b74b868549ae41e3aa35b2d03"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
@ -981,6 +981,14 @@
|
|||||||
"markers": "python_version >= '3.6'",
|
"markers": "python_version >= '3.6'",
|
||||||
"version": "==1.1.0"
|
"version": "==1.1.0"
|
||||||
},
|
},
|
||||||
|
"pycodestyle": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20",
|
||||||
|
"sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==2.8.0"
|
||||||
|
},
|
||||||
"pycparser": {
|
"pycparser": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9",
|
"sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9",
|
||||||
@ -1039,7 +1047,7 @@
|
|||||||
"sha256:670a52d3115d0e879e1ac838a4eb999af32f858163e3a704fe4839de2a676070",
|
"sha256:670a52d3115d0e879e1ac838a4eb999af32f858163e3a704fe4839de2a676070",
|
||||||
"sha256:fb2d48e4eab0dfb786a472cd514aaadc71e3445b203bc300bad93daa75d77c1a"
|
"sha256:fb2d48e4eab0dfb786a472cd514aaadc71e3445b203bc300bad93daa75d77c1a"
|
||||||
],
|
],
|
||||||
"markers": "python_full_version >= '3.7.0'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==0.20.0"
|
"version": "==0.20.0"
|
||||||
},
|
},
|
||||||
"trio-websocket": {
|
"trio-websocket": {
|
||||||
@ -1063,7 +1071,7 @@
|
|||||||
"sha256:2218cb57952d90b9fca325c0dcfb08c3bda93e8fd8070b0a17f048e2e47a521b",
|
"sha256:2218cb57952d90b9fca325c0dcfb08c3bda93e8fd8070b0a17f048e2e47a521b",
|
||||||
"sha256:a2e56bfd5c7cd83c1369d83b5feccd6d37798b74872866e62616e0ecf111bda8"
|
"sha256:a2e56bfd5c7cd83c1369d83b5feccd6d37798b74872866e62616e0ecf111bda8"
|
||||||
],
|
],
|
||||||
"markers": "python_full_version >= '3.7.0'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==1.1.0"
|
"version": "==1.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -95,3 +95,26 @@ class ShippingContainer:
|
|||||||
(REGIONAL_RATE_BOX_B, "Regional Rate Box B"),
|
(REGIONAL_RATE_BOX_B, "Regional Rate Box B"),
|
||||||
(VARIABLE, "Variable")
|
(VARIABLE, "Variable")
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class CoffeeGrind:
|
||||||
|
WHOLE = 'whole-beans'
|
||||||
|
ESPRESSO = 'espresso'
|
||||||
|
CONE_DRIP = 'cone-drip'
|
||||||
|
BASKET_DRIP = 'basket-drip'
|
||||||
|
FRENCH_PRESS = 'french-press'
|
||||||
|
STOVETOP_ESPRESSO = 'stovetop-espresso'
|
||||||
|
AEROPRESS = 'aeropress'
|
||||||
|
PERCOLATOR = 'percolator'
|
||||||
|
CAFE_STYLE = 'cafe-style'
|
||||||
|
GRIND_CHOICES = [
|
||||||
|
(WHOLE, 'Whole Beans'),
|
||||||
|
(ESPRESSO, 'Espresso'),
|
||||||
|
(CONE_DRIP, 'Cone Drip'),
|
||||||
|
(BASKET_DRIP, 'Basket Drip'),
|
||||||
|
(FRENCH_PRESS, 'French Press'),
|
||||||
|
(STOVETOP_ESPRESSO, 'Stovetop Espresso (Moka Pot)'),
|
||||||
|
(AEROPRESS, 'AeroPress'),
|
||||||
|
(PERCOLATOR, 'Percolator'),
|
||||||
|
(CAFE_STYLE, 'BLTC cafe pour over')
|
||||||
|
]
|
||||||
|
|||||||
@ -734,7 +734,7 @@ article + article {
|
|||||||
justify-self: end;
|
justify-self: end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item__form p,
|
.item__form,
|
||||||
.coupon__form p {
|
.coupon__form p {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
|
from measurement.measures import Weight
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.shortcuts import redirect, reverse
|
from django.shortcuts import redirect, reverse
|
||||||
@ -36,15 +37,20 @@ class Cart:
|
|||||||
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] = {
|
||||||
'quantity': 0,
|
'variations': {},
|
||||||
'grind': grind,
|
|
||||||
'price': str(product.price)
|
'price': str(product.price)
|
||||||
}
|
}
|
||||||
|
self.cart[product_id]['variations'][grind] = {'quantity': 0}
|
||||||
|
|
||||||
if update_quantity:
|
if update_quantity:
|
||||||
self.cart[product_id]['quantity'] = quantity
|
self.cart[product_id]['variations'][grind]['quantity'] = quantity
|
||||||
else:
|
else:
|
||||||
self.cart[product_id]['quantity'] += quantity
|
if not grind in self.cart[product_id]['variations']:
|
||||||
|
# create it
|
||||||
|
self.cart[product_id]['variations'][grind] = {'quantity': quantity}
|
||||||
|
else:
|
||||||
|
# add to it
|
||||||
|
self.cart[product_id]['variations'][grind]['quantity'] += quantity
|
||||||
if len(self) <= 20:
|
if len(self) <= 20:
|
||||||
self.save()
|
self.save()
|
||||||
else:
|
else:
|
||||||
@ -69,15 +75,31 @@ class Cart:
|
|||||||
|
|
||||||
for item in self.cart.values():
|
for item in self.cart.values():
|
||||||
item['price'] = Decimal(item['price'])
|
item['price'] = Decimal(item['price'])
|
||||||
item['total_price'] = item['price'] * item['quantity']
|
item['total_price'] = Decimal(sum(self.get_item_prices()))
|
||||||
|
item['quantity'] = self.get_single_item_total_quantity(item)
|
||||||
yield item
|
yield item
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return sum(item['quantity'] for item in self.cart.values())
|
return sum(self.get_all_item_quantities())
|
||||||
|
|
||||||
|
def get_all_item_quantities(self):
|
||||||
|
for item in self.cart.values():
|
||||||
|
yield sum([value['quantity'] for value in item['variations'].values()])
|
||||||
|
|
||||||
|
def get_single_item_total_quantity(self, item):
|
||||||
|
return sum([value['quantity'] for value in item['variations'].values()])
|
||||||
|
|
||||||
|
def get_item_prices(self):
|
||||||
|
for item in self.cart.values():
|
||||||
|
yield Decimal(item['price']) * sum([value['quantity'] for value in item['variations'].values()])
|
||||||
|
|
||||||
|
def get_total_price(self):
|
||||||
|
return sum(self.get_item_prices())
|
||||||
|
|
||||||
def get_total_weight(self):
|
def get_total_weight(self):
|
||||||
if len(self) > 0:
|
if len(self) > 0:
|
||||||
return sum([item['product'].weight.value * item['quantity'] for item in self])
|
for item in self:
|
||||||
|
return item['product'].weight.value * sum(self.get_all_item_quantities())
|
||||||
else:
|
else:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@ -106,9 +128,6 @@ class Cart:
|
|||||||
else:
|
else:
|
||||||
return Decimal('0.00')
|
return Decimal('0.00')
|
||||||
|
|
||||||
def get_total_price(self):
|
|
||||||
return sum(Decimal(item['price']) * item['quantity'] for item in self.cart.values())
|
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
del self.session[settings.CART_SESSION_ID]
|
del self.session[settings.CART_SESSION_ID]
|
||||||
try:
|
try:
|
||||||
@ -156,7 +175,7 @@ class Cart:
|
|||||||
bulk_list = [OrderLine(
|
bulk_list = [OrderLine(
|
||||||
order=order,
|
order=order,
|
||||||
product=item['product'],
|
product=item['product'],
|
||||||
customer_note=item['grind'],
|
customer_note=f"{item['variations']}",
|
||||||
unit_price=item['price'],
|
unit_price=item['price'],
|
||||||
quantity=item['quantity'],
|
quantity=item['quantity'],
|
||||||
tax_rate=2,
|
tax_rate=2,
|
||||||
|
|||||||
@ -3,6 +3,7 @@ from django import forms
|
|||||||
from django.core.mail import EmailMessage
|
from django.core.mail import EmailMessage
|
||||||
|
|
||||||
from core.models import Order
|
from core.models import Order
|
||||||
|
from core import CoffeeGrind
|
||||||
from accounts import STATE_CHOICES
|
from accounts import STATE_CHOICES
|
||||||
|
|
||||||
from .tasks import contact_form_email
|
from .tasks import contact_form_email
|
||||||
@ -10,28 +11,7 @@ from .tasks import contact_form_email
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class AddToCartForm(forms.Form):
|
class AddToCartForm(forms.Form):
|
||||||
WHOLE = 'Whole Beans'
|
grind = forms.ChoiceField(choices=CoffeeGrind.GRIND_CHOICES)
|
||||||
ESPRESSO = 'Espresso'
|
|
||||||
CONE_DRIP = 'Cone Drip'
|
|
||||||
BASKET_DRIP = 'Basket Drip'
|
|
||||||
FRENCH_PRESS = 'French Press'
|
|
||||||
STOVETOP_ESPRESSO = 'Stovetop Espresso (Moka Pot)'
|
|
||||||
AEROPRESS = 'AeroPress'
|
|
||||||
PERCOLATOR = 'Percolator'
|
|
||||||
CAFE_STYLE = 'BLTC cafe pour over'
|
|
||||||
GRIND_CHOICES = [
|
|
||||||
(WHOLE, 'Whole Beans'),
|
|
||||||
(ESPRESSO, 'Espresso'),
|
|
||||||
(CONE_DRIP, 'Cone Drip'),
|
|
||||||
(BASKET_DRIP, 'Basket Drip'),
|
|
||||||
(FRENCH_PRESS, 'French Press'),
|
|
||||||
(STOVETOP_ESPRESSO, 'Stovetop Espresso (Moka Pot)'),
|
|
||||||
(AEROPRESS, 'AeroPress'),
|
|
||||||
(PERCOLATOR, 'Percolator'),
|
|
||||||
(CAFE_STYLE, 'BLTC cafe pour over')
|
|
||||||
]
|
|
||||||
|
|
||||||
grind = forms.ChoiceField(choices=GRIND_CHOICES)
|
|
||||||
quantity = forms.IntegerField(min_value=1, max_value=20, initial=1)
|
quantity = forms.IntegerField(min_value=1, max_value=20, initial=1)
|
||||||
|
|
||||||
class UpdateCartItemForm(forms.Form):
|
class UpdateCartItemForm(forms.Form):
|
||||||
@ -40,27 +20,6 @@ class UpdateCartItemForm(forms.Form):
|
|||||||
|
|
||||||
|
|
||||||
class AddToSubscriptionForm(forms.Form):
|
class AddToSubscriptionForm(forms.Form):
|
||||||
WHOLE = 'Whole Beans'
|
|
||||||
ESPRESSO = 'Espresso'
|
|
||||||
CONE_DRIP = 'Cone Drip'
|
|
||||||
BASKET_DRIP = 'Basket Drip'
|
|
||||||
FRENCH_PRESS = 'French Press'
|
|
||||||
STOVETOP_ESPRESSO = 'Stovetop Espresso (Moka Pot)'
|
|
||||||
AEROPRESS = 'AeroPress'
|
|
||||||
PERCOLATOR = 'Percolator'
|
|
||||||
CAFE_STYLE = 'BLTC cafe pour over'
|
|
||||||
GRIND_CHOICES = [
|
|
||||||
(WHOLE, 'Whole Beans'),
|
|
||||||
(ESPRESSO, 'Espresso'),
|
|
||||||
(CONE_DRIP, 'Cone Drip'),
|
|
||||||
(BASKET_DRIP, 'Basket Drip'),
|
|
||||||
(FRENCH_PRESS, 'French Press'),
|
|
||||||
(STOVETOP_ESPRESSO, 'Stovetop Espresso (Moka Pot)'),
|
|
||||||
(AEROPRESS, 'AeroPress'),
|
|
||||||
(PERCOLATOR, 'Percolator'),
|
|
||||||
(CAFE_STYLE, 'BLTC cafe pour over')
|
|
||||||
]
|
|
||||||
|
|
||||||
SEVEN_DAYS = 7
|
SEVEN_DAYS = 7
|
||||||
FOURTEEN_DAYS = 14
|
FOURTEEN_DAYS = 14
|
||||||
THIRTY_DAYS = 30
|
THIRTY_DAYS = 30
|
||||||
@ -71,7 +30,7 @@ class AddToSubscriptionForm(forms.Form):
|
|||||||
]
|
]
|
||||||
|
|
||||||
quantity = forms.IntegerField(min_value=1, initial=1)
|
quantity = forms.IntegerField(min_value=1, initial=1)
|
||||||
grind = forms.ChoiceField(choices=GRIND_CHOICES)
|
grind = forms.ChoiceField(choices=CoffeeGrind.GRIND_CHOICES)
|
||||||
schedule = forms.ChoiceField(choices=SCHEDULE_CHOICES)
|
schedule = forms.ChoiceField(choices=SCHEDULE_CHOICES)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -15,13 +15,15 @@
|
|||||||
<div class="item__info">
|
<div class="item__info">
|
||||||
<h4>{{product.name}}</h4>
|
<h4>{{product.name}}</h4>
|
||||||
<p><strong>Grind</strong>: {{item.grind}}</p>
|
<p><strong>Grind</strong>: {{item.grind}}</p>
|
||||||
<form class="item__form" action="{% url 'storefront:cart-update' product.pk %}" method="POST">
|
{% for key, value in item.variations.items %}
|
||||||
{% csrf_token %}
|
<p><strong>{{key}}</strong><br>
|
||||||
<p>
|
<form class="item__form" action="{% url 'storefront:cart-update' product.pk key %}" method="POST">
|
||||||
{{ item.update_quantity_form }}
|
{% csrf_token %}
|
||||||
<input type="submit" value="Update">
|
{{ value.update_quantity_form }}
|
||||||
|
<input type="submit" value="Update">
|
||||||
|
</form>
|
||||||
</p>
|
</p>
|
||||||
</form>
|
{% endfor %}
|
||||||
<p>
|
<p>
|
||||||
<a href="{% url 'storefront:cart-remove' product.pk %}">Remove from cart</a>
|
<a href="{% url 'storefront:cart-remove' product.pk %}">Remove from cart</a>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@ -34,8 +34,9 @@
|
|||||||
</figure>
|
</figure>
|
||||||
<div>
|
<div>
|
||||||
<h4>{{product.name}}</h4>
|
<h4>{{product.name}}</h4>
|
||||||
<p><strong>Grind options</strong>: {{item.grind}}</p>
|
{% for key, value in item.variations.items %}
|
||||||
<p><strong>Quantity</strong>: {{item.quantity}}</p>
|
<p>Grind: <strong>{{key}}</strong>, Qty: <strong>{{value.quantity}}</strong></p>
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<div class="item__price">
|
<div class="item__price">
|
||||||
<p><strong>${{item.price}}</strong></p>
|
<p><strong>${{item.price}}</strong></p>
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
import logging
|
import logging
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
from django.test import TestCase, Client, RequestFactory
|
from django.test import TestCase, Client, RequestFactory
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from measurement.measures import Weight
|
||||||
from paypalcheckoutsdk.orders import OrdersCreateRequest, OrdersCaptureRequest
|
from paypalcheckoutsdk.orders import OrdersCreateRequest, OrdersCaptureRequest
|
||||||
from paypalcheckoutsdk.core import PayPalHttpClient, SandboxEnvironment
|
from paypalcheckoutsdk.core import PayPalHttpClient, SandboxEnvironment
|
||||||
|
|
||||||
@ -14,19 +16,30 @@ from .cart import Cart
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
WHOLE = 'whole-beans'
|
||||||
|
ESPRESSO = 'espresso'
|
||||||
|
CONE_DRIP = 'cone-drip'
|
||||||
|
BASKET_DRIP = 'basket-drip'
|
||||||
|
FRENCH_PRESS = 'french-press'
|
||||||
|
STOVETOP_ESPRESSO = 'stovetop-espresso'
|
||||||
|
AEROPRESS = 'aeropress'
|
||||||
|
PERCOLATOR = 'percolator'
|
||||||
|
CAFE_STYLE = 'cafe-style'
|
||||||
|
|
||||||
class CartTest(TestCase):
|
class CartTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.client = Client()
|
self.client = Client()
|
||||||
|
self.factory = RequestFactory()
|
||||||
|
|
||||||
self.customer = User.objects.create_user(
|
self.customer = User.objects.create_user(
|
||||||
username='Peter Templer', email='peter@testing.com', password='peterspassword321'
|
username='petertempler', email='peter@testing.com', password='peterspassword321'
|
||||||
)
|
)
|
||||||
self.product = Product.objects.create(
|
self.product = Product.objects.create(
|
||||||
name='Dante\'s Tornado',
|
name='Dante\'s Tornado',
|
||||||
description='Coffee',
|
description='Coffee',
|
||||||
sku='23987',
|
sku='23987',
|
||||||
price=13.4,
|
price=13.4,
|
||||||
weight=10,
|
weight=Weight(oz=16),
|
||||||
visible_in_listings=True
|
visible_in_listings=True
|
||||||
)
|
)
|
||||||
self.order = Order.objects.create(
|
self.order = Order.objects.create(
|
||||||
@ -44,13 +57,12 @@ class CartTest(TestCase):
|
|||||||
request = response.wsgi_request
|
request = response.wsgi_request
|
||||||
cart = Cart(request)
|
cart = Cart(request)
|
||||||
cart.add(
|
cart.add(
|
||||||
request=request,
|
request,
|
||||||
product=self.product,
|
product=self.product,
|
||||||
quantity=1,
|
quantity=1,
|
||||||
|
grind=WHOLE,
|
||||||
update_quantity=False
|
update_quantity=False
|
||||||
)
|
)
|
||||||
logger.info(f'Body data:\n{body_data}\n')
|
|
||||||
|
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
'email': 'nathanchapman@hey.com',
|
'email': 'nathanchapman@hey.com',
|
||||||
@ -60,3 +72,129 @@ class CartTest(TestCase):
|
|||||||
}
|
}
|
||||||
|
|
||||||
response = self.client.post(url, params)
|
response = self.client.post(url, params)
|
||||||
|
self.assertContains(response, 'Checkout', status_code=200)
|
||||||
|
|
||||||
|
def test_cart_item_variations(self):
|
||||||
|
cart_detail_url = reverse('storefront:cart-detail')
|
||||||
|
response = self.client.get(cart_detail_url)
|
||||||
|
request = response.wsgi_request
|
||||||
|
cart = Cart(request)
|
||||||
|
|
||||||
|
cart = Cart(request)
|
||||||
|
cart.add(
|
||||||
|
request,
|
||||||
|
product=self.product,
|
||||||
|
quantity=1,
|
||||||
|
grind=WHOLE,
|
||||||
|
update_quantity=False
|
||||||
|
)
|
||||||
|
cart.add(
|
||||||
|
request,
|
||||||
|
product=self.product,
|
||||||
|
quantity=1,
|
||||||
|
grind=ESPRESSO,
|
||||||
|
update_quantity=False
|
||||||
|
)
|
||||||
|
for item in cart.cart.values():
|
||||||
|
self.assertTrue('variations' in item, item)
|
||||||
|
|
||||||
|
def test_add_item_to_cart(self):
|
||||||
|
cart_detail_url = reverse('storefront:cart-detail')
|
||||||
|
response = self.client.get(cart_detail_url)
|
||||||
|
request = response.wsgi_request
|
||||||
|
cart = Cart(request)
|
||||||
|
|
||||||
|
cart = Cart(request)
|
||||||
|
cart.add(
|
||||||
|
request,
|
||||||
|
product=self.product,
|
||||||
|
quantity=1,
|
||||||
|
grind=WHOLE,
|
||||||
|
update_quantity=False
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(cart.cart[f'{self.product.id}']['variations'][WHOLE]['quantity'], 1)
|
||||||
|
self.assertEqual(len(cart), 1)
|
||||||
|
self.assertEqual(sum(cart.get_item_prices()), Decimal('13.4'))
|
||||||
|
self.assertEqual(cart.get_total_price(), Decimal('13.4'))
|
||||||
|
cart.add(
|
||||||
|
request,
|
||||||
|
product=self.product,
|
||||||
|
quantity=1,
|
||||||
|
grind=WHOLE,
|
||||||
|
update_quantity=False
|
||||||
|
)
|
||||||
|
self.assertEqual(cart.cart[f'{self.product.id}']['variations'][WHOLE]['quantity'], 2)
|
||||||
|
self.assertEqual(len(cart), 2)
|
||||||
|
|
||||||
|
cart.add(
|
||||||
|
request,
|
||||||
|
product=self.product,
|
||||||
|
quantity=3,
|
||||||
|
grind=ESPRESSO,
|
||||||
|
update_quantity=False
|
||||||
|
)
|
||||||
|
self.assertEqual(cart.cart[f'{self.product.id}']['variations'][ESPRESSO]['quantity'], 3)
|
||||||
|
self.assertEqual(len(cart), 5)
|
||||||
|
self.assertEqual(cart.get_total_price(), Decimal('67'))
|
||||||
|
|
||||||
|
def test_update_cart_item_quantity(self):
|
||||||
|
cart_detail_url = reverse('storefront:cart-detail')
|
||||||
|
response = self.client.get(cart_detail_url)
|
||||||
|
request = response.wsgi_request
|
||||||
|
cart = Cart(request)
|
||||||
|
|
||||||
|
cart = Cart(request)
|
||||||
|
cart.add(
|
||||||
|
request,
|
||||||
|
product=self.product,
|
||||||
|
quantity=3,
|
||||||
|
grind=WHOLE,
|
||||||
|
update_quantity=False
|
||||||
|
)
|
||||||
|
self.assertEqual(cart.cart[f'{self.product.id}']['variations'][WHOLE]['quantity'], 3)
|
||||||
|
|
||||||
|
cart.add(
|
||||||
|
request,
|
||||||
|
product=self.product,
|
||||||
|
quantity=1,
|
||||||
|
grind=WHOLE,
|
||||||
|
update_quantity=True
|
||||||
|
)
|
||||||
|
self.assertEqual(cart.cart[f'{self.product.id}']['variations'][WHOLE]['quantity'], 1)
|
||||||
|
|
||||||
|
def test_cart_remove_item(self):
|
||||||
|
cart_detail_url = reverse('storefront:cart-detail')
|
||||||
|
response = self.client.get(cart_detail_url)
|
||||||
|
request = response.wsgi_request
|
||||||
|
cart = Cart(request)
|
||||||
|
|
||||||
|
cart = Cart(request)
|
||||||
|
cart.add(
|
||||||
|
request,
|
||||||
|
product=self.product,
|
||||||
|
quantity=3,
|
||||||
|
grind=WHOLE,
|
||||||
|
update_quantity=False
|
||||||
|
)
|
||||||
|
self.assertEqual(len(cart), 3)
|
||||||
|
cart.remove(self.product)
|
||||||
|
self.assertEqual(len(cart), 0)
|
||||||
|
|
||||||
|
def test_cart_get_total_weight(self):
|
||||||
|
cart_detail_url = reverse('storefront:cart-detail')
|
||||||
|
response = self.client.get(cart_detail_url)
|
||||||
|
request = response.wsgi_request
|
||||||
|
cart = Cart(request)
|
||||||
|
|
||||||
|
cart = Cart(request)
|
||||||
|
cart.add(
|
||||||
|
request,
|
||||||
|
product=self.product,
|
||||||
|
quantity=3,
|
||||||
|
grind=WHOLE,
|
||||||
|
update_quantity=False
|
||||||
|
)
|
||||||
|
self.assertEqual(cart.get_total_weight(), Decimal(48))
|
||||||
|
|
||||||
|
# 96oz
|
||||||
|
|||||||
@ -14,7 +14,7 @@ urlpatterns = [
|
|||||||
|
|
||||||
path('cart/', views.CartView.as_view(), name='cart-detail'),
|
path('cart/', views.CartView.as_view(), name='cart-detail'),
|
||||||
path('cart/<int:pk>/add/', views.CartAddProductView.as_view(), name='cart-add'),
|
path('cart/<int:pk>/add/', views.CartAddProductView.as_view(), name='cart-add'),
|
||||||
path('cart/<int:pk>/update/', views.CartUpdateProductView.as_view(), name='cart-update'),
|
path('cart/<int:pk>/update/<slug:grind>/', views.CartUpdateProductView.as_view(), name='cart-update'),
|
||||||
path('cart/<int:pk>/remove/', views.cart_remove_product_view, name='cart-remove'),
|
path('cart/<int:pk>/remove/', views.cart_remove_product_view, name='cart-remove'),
|
||||||
|
|
||||||
path('coupon/apply/', views.CouponApplyView.as_view(), name='coupon-apply'),
|
path('coupon/apply/', views.CouponApplyView.as_view(), name='coupon-apply'),
|
||||||
|
|||||||
@ -42,11 +42,12 @@ class CartView(TemplateView):
|
|||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
cart = Cart(self.request)
|
cart = Cart(self.request)
|
||||||
for item in cart:
|
for item in cart:
|
||||||
item['update_quantity_form'] = UpdateCartItemForm(
|
for variation in item['variations'].values():
|
||||||
initial={
|
variation['update_quantity_form'] = UpdateCartItemForm(
|
||||||
'quantity': item['quantity'],
|
initial={
|
||||||
}
|
'quantity': variation['quantity']
|
||||||
)
|
}
|
||||||
|
)
|
||||||
context['cart'] = cart
|
context['cart'] = cart
|
||||||
context['coupon_apply_form'] = CouponApplyForm()
|
context['coupon_apply_form'] = CouponApplyForm()
|
||||||
return context
|
return context
|
||||||
@ -90,6 +91,7 @@ class CartUpdateProductView(SingleObjectMixin, FormView):
|
|||||||
cart.add(
|
cart.add(
|
||||||
request=request,
|
request=request,
|
||||||
product=self.get_object(),
|
product=self.get_object(),
|
||||||
|
grind=kwargs['grind'],
|
||||||
quantity=form.cleaned_data['quantity'],
|
quantity=form.cleaned_data['quantity'],
|
||||||
update_quantity=form.cleaned_data['update']
|
update_quantity=form.cleaned_data['update']
|
||||||
)
|
)
|
||||||
@ -198,14 +200,7 @@ class OrderCreateView(CreateView):
|
|||||||
'shipping_total': cart.get_shipping_cost()
|
'shipping_total': cart.get_shipping_cost()
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.request.user.is_authenticated:
|
if self.request.session.get('shipping_address'):
|
||||||
user_info = {
|
|
||||||
'email': self.request.user.email,
|
|
||||||
'first_name': self.request.user.first_name,
|
|
||||||
'last_name': self.request.user.last_name,
|
|
||||||
}
|
|
||||||
initial |= user_info
|
|
||||||
elif self.request.session.get('shipping_address'):
|
|
||||||
a = self.request.session.get('shipping_address')
|
a = self.request.session.get('shipping_address')
|
||||||
user_info = {
|
user_info = {
|
||||||
'email': a['email'],
|
'email': a['email'],
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user