Merge branch 'feature/track-stock-on-checkout' into develop
This commit is contained in:
commit
ec8a518a7f
@ -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}
|
||||
"""
|
||||
|
||||
|
||||
|
||||
@ -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'),
|
||||
),
|
||||
]
|
||||
@ -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})
|
||||
@ -366,6 +356,14 @@ class Order(models.Model):
|
||||
|
||||
objects = OrderManager()
|
||||
|
||||
def minus_stock(self):
|
||||
for line in self.lines.all():
|
||||
line.minus_stock()
|
||||
|
||||
def add_stock(self):
|
||||
for line in self.lines.all():
|
||||
line.add_stock()
|
||||
|
||||
def get_total_quantity(self):
|
||||
return sum([line.quantity for line in self])
|
||||
|
||||
@ -443,6 +441,16 @@ class OrderLine(models.Model):
|
||||
def quantity_unfulfilled(self):
|
||||
return self.quantity - self.quantity_fulfilled
|
||||
|
||||
def minus_stock(self):
|
||||
if self.variant.track_inventory:
|
||||
self.variant.stock -= self.quantity
|
||||
self.variant.save()
|
||||
|
||||
def add_stock(self):
|
||||
if self.variant.track_inventory:
|
||||
self.variant.stock += self.quantity
|
||||
self.variant.save()
|
||||
|
||||
|
||||
class TrackingNumber(models.Model):
|
||||
order = models.ForeignKey(
|
||||
@ -472,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'
|
||||
|
||||
@ -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">« 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 »</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
{% endblock content %}
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
{% extends "dashboard.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article class="product">
|
||||
<header class="object__header">
|
||||
<h1>Update option</h1>
|
||||
</header>
|
||||
<section class="object__panel">
|
||||
<form class="panel__item" method="POST" action="{% url 'dashboard:option-update' option.pk %}">
|
||||
{% csrf_token %}
|
||||
{{form.as_p}}
|
||||
<p class="form__submit">
|
||||
<input class="action-button" type="submit" value="Create option"> or <a href="{% url 'dashboard:option-detail' option.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</section>
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -4,7 +4,10 @@
|
||||
{% block content %}
|
||||
<article>
|
||||
<header class="object__header">
|
||||
<h1><img src="{% static 'images/box.png' %}" alt=""> Order #{{order.pk}}</h1>
|
||||
<div>
|
||||
<h1><img src="{% static 'images/box.png' %}" alt=""> Order #{{order.pk}}</h1>
|
||||
<p>Date: {{ order.created_at }}</p>
|
||||
</div>
|
||||
<div class="object__menu">
|
||||
<a class="action-button action-button--warning" href="{% url 'dashboard:order-cancel' order.pk %}">Cancel order</a>
|
||||
<span class="order__status order__status--{{order.status}}">{{order.get_status_display}} ({{order.total_quantity_fulfilled}} / {{order.total_quantity_ordered}})</span>
|
||||
@ -102,7 +105,7 @@
|
||||
<span>Discount: {{order.coupon.discount_value}} {{order.coupon.get_discount_value_type_display}}</span><br>
|
||||
{% endif %}
|
||||
<span>Shipping: ${{order.shipping_total}}</span><br>
|
||||
<span>Total: ${{order.get_total_price_after_discount}}</span>
|
||||
<span>Total: ${{order.total_amount}}</span>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
{% csrf_token %}
|
||||
{{form.as_p}}
|
||||
<p class="form__submit">
|
||||
<input class="action-button" type="submit" value="Create rate"> or <a href="{% url 'dashboard:rate-detail' rate.pk %}">cancel</a>
|
||||
<input class="action-button" type="submit" value="Save changes"> or <a href="{% url 'dashboard:rate-detail' rate.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
34
src/dashboard/templates/dashboard/stock.html
Normal file
34
src/dashboard/templates/dashboard/stock.html
Normal file
@ -0,0 +1,34 @@
|
||||
{% extends "dashboard.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<article>
|
||||
<header class="object__header">
|
||||
<h1><img src="{% static 'images/warehouse.png' %}" alt=""> Stock</h1>
|
||||
<p><strong>Total in warehouse</strong> = available stock + unfulfilled</p>
|
||||
</header>
|
||||
|
||||
<section class="object__list">
|
||||
<div class="object__item panel__header object__item--col5">
|
||||
<span>Product</span>
|
||||
<span>SKU</span>
|
||||
<span>Available Stock</span>
|
||||
<span>Total in warehouse</span>
|
||||
</div>
|
||||
{% for variant in variant_list %}
|
||||
<div class="object__item object__item--col5">
|
||||
{% with product=variant.product %}
|
||||
<figure class="item__figure">
|
||||
<img class="product__image product__image--small" src="{{product.get_first_img.image.url}}" alt="{{product.get_first_img.image}}">
|
||||
<figcaption><strong>{{variant}}</strong></figcaption>
|
||||
</figure>
|
||||
<span>{{ variant.sku }}</span>
|
||||
<span>{{ variant.stock }}</span>
|
||||
<span>{{ variant.total_in_warehouse }}</span>
|
||||
<a href="{% url 'dashboard:variant-restock' product.pk variant.pk %}" class="action-button">Restock →</a>
|
||||
{% endwith %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</section>
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -11,7 +11,7 @@
|
||||
{% csrf_token %}
|
||||
{{form.as_p}}
|
||||
<p class="form__submit">
|
||||
<input class="action-button" type="submit" value="Create variant"> or <a href="{% url 'dashboard:product-detail' product.pk %}">cancel</a>
|
||||
<input class="action-button" type="submit" value="Save changes"> or <a href="{% url 'dashboard:product-detail' product.pk %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
19
src/dashboard/templates/dashboard/variant_restock.html
Normal file
19
src/dashboard/templates/dashboard/variant_restock.html
Normal file
@ -0,0 +1,19 @@
|
||||
{% extends "dashboard.html" %}
|
||||
|
||||
{% block content %}
|
||||
<article class="product">
|
||||
<header class="object__header">
|
||||
<h1>Restock variant</h1>
|
||||
</header>
|
||||
<section class="object__panel">
|
||||
<form class="panel__item" method="POST" action="{% url 'dashboard:variant-restock' product.pk variant.pk %}">
|
||||
{% csrf_token %}
|
||||
{{form.as_p}}
|
||||
<p>Total in warehouse: {{ variant.total_in_warehouse }}</p>
|
||||
<p class="form__submit">
|
||||
<input class="action-button" type="submit" value="Save changes"> or <a href="{% url 'dashboard:stock' %}">cancel</a>
|
||||
</p>
|
||||
</form>
|
||||
</section>
|
||||
</article>
|
||||
{% endblock %}
|
||||
@ -17,6 +17,11 @@ urlpatterns = [
|
||||
views.CatalogView.as_view(),
|
||||
name='catalog'
|
||||
),
|
||||
path(
|
||||
'stock/',
|
||||
views.StockView.as_view(),
|
||||
name='stock'
|
||||
),
|
||||
|
||||
path(
|
||||
'shipping-rates/new/',
|
||||
@ -186,6 +191,11 @@ urlpatterns = [
|
||||
views.ProductVariantDeleteView.as_view(),
|
||||
name='variant-delete'
|
||||
),
|
||||
path(
|
||||
'restock/',
|
||||
views.ProductVariantStockUpdateView.as_view(),
|
||||
name='variant-restock'
|
||||
),
|
||||
])),
|
||||
])),
|
||||
])),
|
||||
|
||||
@ -18,7 +18,8 @@ from django.contrib import messages
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
|
||||
from django.db.models import (
|
||||
Exists, OuterRef, Prefetch, Subquery, Count, Sum, Avg, F, Q, Value
|
||||
Exists, OuterRef, Prefetch, Subquery, Count, Sum, Avg, F, Q, Value,
|
||||
ExpressionWrapper, IntegerField
|
||||
)
|
||||
from django.db.models.functions import Coalesce
|
||||
|
||||
@ -101,6 +102,25 @@ 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
|
||||
).prefetch_related('order_lines', 'product').annotate(
|
||||
total_in_warehouse=F('stock') + Coalesce(Sum('order_lines__quantity', filter=Q(
|
||||
order_lines__order__status=OrderStatus.UNFULFILLED) | Q(
|
||||
order_lines__order__status=OrderStatus.PARTIALLY_FULFILLED)
|
||||
) - Sum('order_lines__quantity_fulfilled', filter=Q(
|
||||
order_lines__order__status=OrderStatus.UNFULFILLED) | Q(
|
||||
order_lines__order__status=OrderStatus.PARTIALLY_FULFILLED)), 0)
|
||||
).order_by('product')
|
||||
return object_list
|
||||
|
||||
|
||||
class ShippingRateDetailView(LoginRequiredMixin, DetailView):
|
||||
model = ShippingRate
|
||||
context_object_name = 'rate'
|
||||
@ -234,6 +254,11 @@ class OrderCancelView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
|
||||
'status': OrderStatus.CANCELED
|
||||
}
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.add_stock()
|
||||
form.instance.save()
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('dashboard:order-detail', kwargs={'pk': self.object.pk})
|
||||
|
||||
@ -441,6 +466,38 @@ class ProductVariantDeleteView(SuccessMessageMixin, DeleteView):
|
||||
return reverse('dashboard:product-detail', kwargs={'pk': self.kwargs['pk']})
|
||||
|
||||
|
||||
class ProductVariantStockUpdateView(LoginRequiredMixin, UpdateView):
|
||||
model = ProductVariant
|
||||
pk_url_kwarg = 'variant_pk'
|
||||
success_message = 'ProductVariant saved.'
|
||||
success_url = reverse_lazy('dashboard:stock')
|
||||
template_name = 'dashboard/variant_restock.html'
|
||||
fields = [
|
||||
'stock',
|
||||
]
|
||||
context_object_name = 'variant'
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = ProductVariant.objects.annotate(
|
||||
total_in_warehouse=F('stock') + Coalesce(Sum('order_lines__quantity', filter=Q(
|
||||
order_lines__order__status=OrderStatus.UNFULFILLED) | Q(
|
||||
order_lines__order__status=OrderStatus.PARTIALLY_FULFILLED)
|
||||
) - Sum('order_lines__quantity_fulfilled', filter=Q(
|
||||
order_lines__order__status=OrderStatus.UNFULFILLED) | Q(
|
||||
order_lines__order__status=OrderStatus.PARTIALLY_FULFILLED)), 0)
|
||||
).prefetch_related('order_lines', 'product')
|
||||
return queryset
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['product'] = Product.objects.get(pk=self.kwargs['pk'])
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.product = Product.objects.get(pk=self.kwargs['pk'])
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class ProductOptionDetailView(LoginRequiredMixin, DetailView):
|
||||
model = ProductOption
|
||||
template_name = 'dashboard/option_detail.html'
|
||||
@ -482,6 +539,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(
|
||||
@ -492,7 +550,7 @@ class CustomerListView(LoginRequiredMixin, ListView):
|
||||
'orders'
|
||||
).annotate(
|
||||
num_orders=Count('orders')
|
||||
)
|
||||
).order_by('first_name', 'last_name')
|
||||
|
||||
return object_list
|
||||
|
||||
|
||||
BIN
src/static/images/warehouse.png
Normal file
BIN
src/static/images/warehouse.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.0 KiB |
@ -72,19 +72,39 @@ 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:
|
||||
self.check_item_stock_quantities(request)
|
||||
self.save()
|
||||
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
|
||||
logger.info(f'\nCart:\n{self.cart}\n')
|
||||
|
||||
def check_item_stock_quantities(self, request):
|
||||
for item in self:
|
||||
if item['variant'].track_inventory:
|
||||
if item['quantity'] > item['variant'].stock:
|
||||
messages.warning(request, 'Quantity added exceeds available stock.')
|
||||
item['quantity'] = item['variant'].stock
|
||||
self.save()
|
||||
|
||||
def remove(self, pk):
|
||||
self.cart.pop(pk)
|
||||
self.save()
|
||||
@ -123,6 +143,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 +155,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
|
||||
|
||||
@ -146,10 +169,7 @@ class Cart:
|
||||
container,
|
||||
str(self.session.get('shipping_address')['postal_code'])
|
||||
)
|
||||
try:
|
||||
logger.info('wafd')
|
||||
except TypeError as e:
|
||||
return Decimal('0.00')
|
||||
|
||||
usps = USPSApi(settings.USPS_USER_ID, test=True)
|
||||
|
||||
try:
|
||||
@ -159,12 +179,12 @@ class Cart:
|
||||
'Could not connect to USPS, try again.'
|
||||
)
|
||||
|
||||
logger.error(validation.result)
|
||||
logger.info(validation.result)
|
||||
package = dict(validation.result['RateV4Response']['Package'])
|
||||
if 'Error' not in package:
|
||||
rate = package['Postage']['CommercialRate']
|
||||
else:
|
||||
logger.error("USPS Rate error")
|
||||
logger.error('USPS Rate error')
|
||||
rate = '0.00'
|
||||
return Decimal(rate)
|
||||
else:
|
||||
|
||||
@ -11,7 +11,7 @@ from localflavor.us.us_states import USPS_CHOICES
|
||||
from usps import USPSApi, Address
|
||||
from captcha.fields import CaptchaField
|
||||
|
||||
from core.models import Order
|
||||
from core.models import Order, ProductVariant
|
||||
from core import CoffeeGrind, ShippingContainer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -10,7 +10,6 @@
|
||||
<h1>Welcome to our new website!</h1>
|
||||
<h4>NEW COOL LOOK, SAME GREAT COFFEE</h4>
|
||||
</div>
|
||||
{# Home > Category > "Coffee/Merchandise" #}
|
||||
<article>
|
||||
<div class="breadcrumbs">
|
||||
<menu>
|
||||
|
||||
@ -62,7 +62,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Total</th>
|
||||
<td><strong>${{order.get_total_price_after_discount}}</strong></td>
|
||||
<td><strong>${{order.total_amount}}</strong></td>
|
||||
</tr>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
@ -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
|
||||
@ -35,8 +36,9 @@ from accounts.forms import (
|
||||
AddressForm as AccountAddressForm, CustomerUpdateForm
|
||||
)
|
||||
from core.models import (
|
||||
ProductCategory, Product, ProductOption,
|
||||
Order, Transaction, OrderLine, Coupon, ShippingRate
|
||||
ProductCategory, Product, ProductVariant, ProductOption,
|
||||
Order, Transaction, OrderLine, Coupon, ShippingRate,
|
||||
SiteSettings
|
||||
)
|
||||
from core.forms import ShippingRateForm
|
||||
from core import OrderStatus, ShippingContainer
|
||||
@ -79,7 +81,12 @@ class CartAddProductView(SingleObjectMixin, FormView):
|
||||
return reverse('storefront:cart-detail')
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
variants = self.get_object().variants.all()
|
||||
variants = self.get_object().variants.filter(
|
||||
Q(track_inventory=False) | Q(
|
||||
track_inventory=True,
|
||||
stock__gt=0
|
||||
)
|
||||
)
|
||||
options = ProductOption.objects.filter(products__pk=self.get_object().pk)
|
||||
if form_class is None:
|
||||
form_class = self.get_form_class()
|
||||
@ -168,8 +175,10 @@ class ProductCategoryDetailView(DetailView):
|
||||
Prefetch(
|
||||
'product_set',
|
||||
queryset=Product.objects.filter(
|
||||
visible_in_listings=True
|
||||
)
|
||||
Q(visible_in_listings=True),
|
||||
Q(variants__track_inventory=False) |
|
||||
Q(variants__track_inventory=True) & Q(variants__stock__gt=0)
|
||||
).distinct()
|
||||
)
|
||||
)
|
||||
return object_list
|
||||
@ -192,7 +201,12 @@ class ProductDetailView(FormMixin, DetailView):
|
||||
form_class = AddToCartForm
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
variants = self.object.variants.all()
|
||||
variants = self.object.variants.filter(
|
||||
Q(track_inventory=False) | Q(
|
||||
track_inventory=True,
|
||||
stock__gt=0
|
||||
)
|
||||
)
|
||||
options = ProductOption.objects.filter(products__pk=self.object.pk)
|
||||
if form_class is None:
|
||||
form_class = self.get_form_class()
|
||||
@ -210,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,
|
||||
@ -221,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'],
|
||||
@ -255,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'):
|
||||
@ -262,16 +283,27 @@ 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')
|
||||
)
|
||||
elif len(self.get_containers(request)) == 1:
|
||||
self.request.session['shipping_container'] = self.get_containers(request)[0]
|
||||
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(
|
||||
@ -370,6 +402,7 @@ def paypal_order_transaction_capture(request, transaction_id):
|
||||
cart = Cart(request)
|
||||
order = Order.objects.get(pk=request.session.get('order_id'))
|
||||
order.status = OrderStatus.UNFULFILLED
|
||||
order.minus_stock()
|
||||
try:
|
||||
coupon = Coupon.objects.get(
|
||||
code=request.session.get('coupon_code')
|
||||
@ -387,7 +420,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'})
|
||||
|
||||
@ -33,6 +33,10 @@
|
||||
<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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user