diff --git a/core/migrations/0002_productvariant_max_order_per_customer.py b/core/migrations/0002_productvariant_max_order_per_customer.py
new file mode 100644
index 0000000..9a2ad6b
--- /dev/null
+++ b/core/migrations/0002_productvariant_max_order_per_customer.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.1.6 on 2023-06-19 23:07
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('core', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='productvariant',
+ name='max_order_per_customer',
+ field=models.PositiveIntegerField(blank=True, null=True),
+ ),
+ ]
diff --git a/core/migrations/0003_rename_max_order_per_customer_productvariant_order_limit.py b/core/migrations/0003_rename_max_order_per_customer_productvariant_order_limit.py
new file mode 100644
index 0000000..9e95f90
--- /dev/null
+++ b/core/migrations/0003_rename_max_order_per_customer_productvariant_order_limit.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.1.6 on 2023-06-20 02:59
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('core', '0002_productvariant_max_order_per_customer'),
+ ]
+
+ operations = [
+ migrations.RenameField(
+ model_name='productvariant',
+ old_name='max_order_per_customer',
+ new_name='order_limit',
+ ),
+ ]
diff --git a/core/models.py b/core/models.py
index 4235d53..7116ea8 100644
--- a/core/models.py
+++ b/core/models.py
@@ -192,11 +192,12 @@ class ProductVariant(models.Model):
validators=[MinValueValidator(0)]
)
sorting = models.PositiveIntegerField(blank=True, null=True)
+ order_limit = models.PositiveIntegerField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
- objects = ProductVariantManager()
+ #objects = ProductVariantManager()
def __str__(self):
return f'{self.product}: {self.name}'
diff --git a/dashboard/forms.py b/dashboard/forms.py
index 5348f35..ae168ab 100644
--- a/dashboard/forms.py
+++ b/dashboard/forms.py
@@ -27,6 +27,7 @@ class ProductVariantUpdateForm(forms.ModelForm):
'track_inventory',
'stock',
'sorting',
+ 'order_limit',
'image'
]
diff --git a/dashboard/templates/dashboard/order/detail.html b/dashboard/templates/dashboard/order/detail.html
index 4ae6747..297b7c3 100644
--- a/dashboard/templates/dashboard/order/detail.html
+++ b/dashboard/templates/dashboard/order/detail.html
@@ -31,9 +31,9 @@
{% if order.subscription %}
Subscription
- {{ order.subscription_description }} View on Stripe ↗
+ {{ order.subscription_description }} View on Stripe ↗
- {% else %}
+ {% elif order.transaction.paypal_id %}
PayPal Transaction
{{order.transaction.get_status_display}} View on PayPal ↗
@@ -113,7 +113,7 @@
-${{ order.coupon_amount }} |
{% endif %}
-
+
| Shipping: |
${{ order.shipping_total }} |
diff --git a/dashboard/views.py b/dashboard/views.py
index 2523604..a529b8e 100644
--- a/dashboard/views.py
+++ b/dashboard/views.py
@@ -477,6 +477,7 @@ class ProductVariantCreateView(
'visible_in_listings',
'track_inventory',
'stock',
+ 'order_limit',
]
def get_context_data(self, **kwargs):
diff --git a/storefront/cart.py b/storefront/cart.py
index 16f96f8..cdf283e 100644
--- a/storefront/cart.py
+++ b/storefront/cart.py
@@ -195,6 +195,9 @@ class Cart:
self.items.append(new_item)
self.save()
+ def get_item_by_pk(self, pk):
+ return next((i, v) for i, v in enumerate(self) if v.variant.pk == pk)
+
def update_item_quantity(self, item_index, quantity):
self.items[item_index].quantity = quantity
self.save()
diff --git a/storefront/templates/storefront/order_form.html b/storefront/templates/storefront/order_form.html
index 2e0534c..765fb85 100644
--- a/storefront/templates/storefront/order_form.html
+++ b/storefront/templates/storefront/order_form.html
@@ -60,7 +60,7 @@
Order summary
-
@@ -86,7 +86,13 @@
+ {% if cart.total_price == 0.00 %}
+
+
+
+ {% else %}
+ {% endif %}
{% endblock %}
diff --git a/storefront/templates/storefront/payment_done.html b/storefront/templates/storefront/payment_done.html
index a87db7d..df047a9 100644
--- a/storefront/templates/storefront/payment_done.html
+++ b/storefront/templates/storefront/payment_done.html
@@ -1,10 +1,10 @@
{% extends "base.html" %}
-{% block head_title %}Payment Success | {% endblock %}
+{% block head_title %}Order Success | {% endblock %}
{% block content %}
-
- Payment was successful
- Thank you for your order!
-
+
+ We've received your order
+ Thank you!
+
{% endblock %}
diff --git a/storefront/urls.py b/storefront/urls.py
index 4608e35..df98118 100644
--- a/storefront/urls.py
+++ b/storefront/urls.py
@@ -57,6 +57,11 @@ urlpatterns = [
views.OrderCreateView.as_view(),
name='order-create'
),
+ path(
+ 'checkout/free/',
+ views.FreeOrderCreateView.as_view(),
+ name='free-order-create'
+ ),
path(
'done/',
views.PaymentDoneView.as_view(),
diff --git a/storefront/views.py b/storefront/views.py
index f8567b9..a868c64 100644
--- a/storefront/views.py
+++ b/storefront/views.py
@@ -46,7 +46,7 @@ from core.models import (
)
from core.forms import ShippingRateForm
from core.shipping import get_shipping_cost
-from core import OrderStatus, ShippingContainer
+from core import OrderStatus, ShippingContainer, TransactionStatus
from .forms import (
AddToCartForm, CartItemUpdateForm, OrderCreateForm,
@@ -355,13 +355,43 @@ class OrderCreateView(CreateView):
cart = Cart(request)
+
+ try:
+ user = User.objects.get(
+ email=request.session.get('shipping_address').get('email')
+ )
+ except User.DoesNotExist:
+ user = None
+
+ if user:
+ variants_ordered = ProductVariant.objects.filter(
+ pk__in=cart.item_variant_pks,
+ order_lines__order__customer=user,
+ order_lines__order__status__in=[
+ OrderStatus.UNFULFILLED,
+ OrderStatus.PARTIALLY_FULFILLED,
+ OrderStatus.FULFILLED
+ ]
+ ).values("id", "order_limit").annotate(
+ num_ordered=Sum("order_lines__quantity")
+ ).order_by()
+
+ for variant in variants_ordered:
+ index, item = cart.get_item_by_pk(variant['id'])
+ available = variant['order_limit'] - variant['num_ordered']
+ new_qty = item.quantity if item.quantity < available else available
+ if new_qty and new_qty <= 0:
+ cart.remove_item(index)
+ else:
+ cart.update_item_quantity(index, new_qty)
+
+ if len(cart) == 0:
+ return HttpResponseRedirect(
+ reverse('storefront:product-list')
+ )
+
if cart.coupon is not None:
- try:
- user = User.objects.get(
- email=request.session.get('shipping_address').get('email')
- )
- except User.DoesNotExist:
- user = None
+
if user in cart.coupon.users.all():
cart.remove_coupon()
messages.warning(request, 'Coupon already used.')
@@ -397,6 +427,47 @@ class OrderCreateView(CreateView):
return JsonResponse(data)
+class FreeOrderCreateView(CreateView):
+ http_method_names = ['post']
+ model = Order
+ form_class = OrderCreateForm
+ success_url = reverse_lazy('storefront:payment-done')
+
+ def form_valid(self, form):
+ cart = Cart(self.request)
+ form.instance.subtotal_amount = cart.subtotal_price
+ form.instance.coupon = cart.coupon
+ form.instance.coupon_amount = cart.discount_amount
+ form.instance.total_amount = cart.total_price
+ form.instance.weight = cart.total_weight
+ shipping_container = cart.get_shipping_container()
+ form.instance.shipping_total = cart.get_shipping_price(shipping_container)
+ shipping_address = self.request.session.get('shipping_address')
+ form.instance.customer, form.instance.shipping_address = get_or_create_customer(self.request, shipping_address)
+ form.instance.status = OrderStatus.UNFULFILLED
+ self.object = form.save()
+ bulk_list = cart.build_bulk_list(self.object)
+ OrderLine.objects.bulk_create(bulk_list)
+ self.object.minus_stock()
+
+ try:
+ coupon = Coupon.objects.get(
+ code=self.request.session.get('coupon_code')
+ )
+ except ObjectDoesNotExist:
+ coupon = None
+
+ if coupon:
+ self.object.coupon = coupon
+ coupon.users.add(self.object.customer)
+
+ transaction = Transaction.objects.get(order=self.object)
+ transaction.status = TransactionStatus.COMPLETED
+ transaction.save()
+ cart.clear()
+ return HttpResponseRedirect(self.get_success_url())
+
+
@csrf_exempt
@require_POST
def paypal_order_transaction_capture(request, transaction_id):