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

-
+ {% csrf_token %} {{form.as_p}}
@@ -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):