Add basic shipping checkout defaults

This commit is contained in:
Nathan Chapman 2022-10-29 10:20:52 -06:00
parent 11d3b740aa
commit 157296db2b
11 changed files with 154 additions and 24 deletions

View File

@ -19,10 +19,10 @@ class Address(models.Model):
def __str__(self):
return f"""
{first_name} {last_name}
{street_address_1}
{street_address_2}
{city}, {state}, {postal_code}
{self.first_name} {self.last_name}
{self.street_address_1}
{self.street_address_2}
{self.city}, {self.state}, {self.postal_code}
"""

View File

@ -0,0 +1,24 @@
# Generated by Django 4.0.2 on 2022-10-29 14:44
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('core', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='shippingrate',
name='is_selectable',
field=models.BooleanField(default=True),
),
migrations.AddField(
model_name='sitesettings',
name='default_shipping_rate',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='core.shippingrate'),
),
]

View File

@ -56,17 +56,6 @@ class SingletonBase(models.Model):
abstract = True
class SiteSettings(SingletonBase):
usps_user_id = models.CharField(max_length=255)
def __str__(self):
return 'Site Settings'
class Meta:
verbose_name = 'Site Settings'
verbose_name_plural = 'Site Settings'
class ProductCategory(models.Model):
name = models.CharField(max_length=255)
main_category = models.BooleanField(default=True)
@ -271,6 +260,7 @@ class ShippingRate(models.Model):
blank=True,
null=True
)
is_selectable = models.BooleanField(default=True)
def get_absolute_url(self):
return reverse('dashboard:rate-detail', kwargs={'pk': self.pk})
@ -490,3 +480,21 @@ class Subscription(models.Model):
on_delete=models.SET_NULL,
null=True
)
class SiteSettings(SingletonBase):
usps_user_id = models.CharField(max_length=255)
default_shipping_rate = models.ForeignKey(
ShippingRate,
blank=True,
null=True,
related_name='+',
on_delete=models.SET_NULL
)
def __str__(self):
return 'Site Settings'
class Meta:
verbose_name = 'Site Settings'
verbose_name_plural = 'Site Settings'

View File

@ -22,5 +22,24 @@
<span class="object__item">No customers</span>
{% endfor %}
</section>
<section>
<div class="pagination">
<p class="step-links">
{% if page_obj.has_previous %}
<a href="?page=1">&laquo; first</a>
<a href="?page={{ page_obj.previous_page_number }}">previous</a>
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">next</a>
<a href="?page={{ page_obj.paginator.num_pages }}">last &raquo;</a>
{% endif %}
</p>
</div>
</section>
</article>
{% endblock content %}

View File

@ -0,0 +1,25 @@
{% extends "dashboard.html" %}
{% load static %}
{% block content %}
<article>
<header class="object__header">
<h1><img src="{% static 'images/warehouse.png' %}" alt=""> Stock</h1>
</header>
<section class="object__panel">
<div class="object__item panel__header">
<h4>Products</h4>
</div>
<div class="panel__item">
{% for variant in variant_list %}
<form action="">
<h3>{{ variant }}</h3>
<p>Variant ID: {{ variant.pk }}</p>
<p>Total in warehouse: {{ variant.stock }}</p>
</form>
{% endfor %}
</div>
</section>
</article>
{% endblock %}

View File

@ -17,6 +17,11 @@ urlpatterns = [
views.CatalogView.as_view(),
name='catalog'
),
path(
'stock/',
views.StockView.as_view(),
name='stock'
),
path(
'shipping-rates/new/',

View File

@ -101,6 +101,20 @@ class CatalogView(ListView):
return context
class StockView(ListView):
model = ProductVariant
context_object_name = 'variant_list'
template_name = 'dashboard/stock.html'
def get_queryset(self):
object_list = ProductVariant.objects.filter(
track_inventory=True
).order_by('product')
# quantity
# quantity_fulfilled
return object_list
class ShippingRateDetailView(LoginRequiredMixin, DetailView):
model = ShippingRate
context_object_name = 'rate'
@ -487,6 +501,7 @@ class ProductOptionDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteVie
class CustomerListView(LoginRequiredMixin, ListView):
model = User
template_name = 'dashboard/customer_list.html'
paginate_by = 100
def get_queryset(self):
object_list = User.objects.filter(

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -72,7 +72,7 @@ class Cart:
if update_quantity:
self.cart[item['variant']]['quantity'] = item['quantity']
else:
self.cart.append(item)
self.add_or_update_item(item)
# TODO: abstract this to a function that will check the max amount of item in the cart
if len(self) <= 20:
@ -80,6 +80,17 @@ class Cart:
else:
messages.warning(request, "Cart is full: 20 items or less.")
def add_or_update_item(self, new_item):
new_item_pk = int(new_item['variant'])
for item in self:
if new_item_pk == item['variant'].pk:
if new_item['options'] == item['options']:
item['quantity'] += new_item['quantity']
return
else:
continue
self.cart.append(new_item)
def save(self):
self.session[settings.CART_SESSION_ID] = self.cart
self.session.modified = True
@ -123,6 +134,9 @@ class Cart:
return 0
def get_shipping_container_choices(self):
is_selectable = Q(
is_selectable=True
)
min_weight_matched = Q(
min_order_weight__lte=self.get_total_weight()) | Q(
min_order_weight__isnull=True
@ -132,7 +146,7 @@ class Cart:
max_order_weight__isnull=True
)
containers = ShippingRate.objects.filter(
min_weight_matched & max_weight_matched
is_selectable & min_weight_matched & max_weight_matched
)
return containers

View File

@ -7,6 +7,7 @@ from django.utils import timezone
from django.shortcuts import render, reverse, redirect, get_object_or_404
from django.urls import reverse_lazy
from django.core.mail import EmailMessage
from django.core.cache import cache
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
from django.http import JsonResponse, HttpResponseRedirect
from django.views.generic.base import View, RedirectView, TemplateView
@ -36,7 +37,8 @@ from accounts.forms import (
)
from core.models import (
ProductCategory, Product, ProductVariant, ProductOption,
Order, Transaction, OrderLine, Coupon, ShippingRate
Order, Transaction, OrderLine, Coupon, ShippingRate,
SiteSettings
)
from core.forms import ShippingRateForm
from core import OrderStatus, ShippingContainer
@ -222,7 +224,7 @@ class CheckoutAddressView(FormView):
if user.is_authenticated and user.default_shipping_address:
address = user.default_shipping_address
initial = {
'full_name': address.first_name+' '+address.last_name,
'full_name': address.first_name + ' ' + address.last_name,
'email': user.email,
'street_address_1': address.street_address_1,
'street_address_2': address.street_address_2,
@ -233,7 +235,7 @@ class CheckoutAddressView(FormView):
elif self.request.session.get('shipping_address'):
address = self.request.session.get('shipping_address')
initial = {
'full_name': address['first_name']+' '+address['last_name'],
'full_name': address['first_name'] + ' ' + address['last_name'],
'email': address['email'],
'street_address_1': address['street_address_1'],
'street_address_2': address['street_address_2'],
@ -267,6 +269,13 @@ class CheckoutShippingView(FormView):
template_name = 'storefront/checkout_shipping_form.html'
form_class = CheckoutShippingForm
success_url = reverse_lazy('storefront:order-create')
containers = None
def get_containers(self, request):
if self.containers is None:
cart = Cart(request)
self.containers = cart.get_shipping_container_choices()
return self.containers
def get(self, request, *args, **kwargs):
if not self.request.session.get('shipping_address'):
@ -274,16 +283,22 @@ class CheckoutShippingView(FormView):
return HttpResponseRedirect(
reverse('storefront:checkout-address')
)
site_settings = cache.get('SiteSettings')
cart = Cart(self.request)
if len(self.get_containers(request)) == 0:
self.request.session['shipping_container'] = site_settings.default_shipping_rate
return HttpResponseRedirect(
reverse('storefront:order-create')
)
return super().get(request, *args, **kwargs)
def get_form(self, form_class=None):
cart = Cart(self.request)
containers = cart.get_shipping_container_choices()
for container in containers:
for container in self.get_containers(self.request):
container.s_cost = cart.get_shipping_cost(container.container)
if form_class is None:
form_class = self.get_form_class()
return form_class(containers, **self.get_form_kwargs())
return form_class(self.get_containers(self.request), **self.get_form_kwargs())
def form_valid(self, form):
shipping_container = ShippingRate.objects.get(
@ -400,7 +415,6 @@ def paypal_order_transaction_capture(request, transaction_id):
transaction.save()
cart.clear()
logger.debug(f'\nPayPal Response data: {data}\n')
return JsonResponse(data)
else:
return JsonResponse({'details': 'invalid request'})

View File

@ -33,6 +33,12 @@
<img src="{% static 'images/cubes.png' %}" alt="">
Catalog
</a>
<!--
<a href="{% url 'dashboard:stock' %}">
<img src="{% static 'images/warehouse.png' %}" alt="">
Stock
</a>
-->
<a href="{% url 'dashboard:order-list' %}">
<img src="{% static 'images/box.png' %}" alt="">
Orders