import logging from datetime import datetime from django.conf import settings from django.utils import timezone from django.shortcuts import render, reverse, redirect, get_object_or_404 from django.http import HttpResponseRedirect from django.urls import reverse, reverse_lazy from django.views.generic.base import RedirectView, TemplateView from django.views.generic.edit import ( FormView, CreateView, UpdateView, DeleteView, FormMixin ) from django.views.generic.detail import DetailView, SingleObjectMixin from django.views.generic.list import ListView from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin from django.forms import inlineformset_factory 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 ) from django.db.models.functions import Coalesce from accounts.models import User from accounts.utils import get_or_create_customer from accounts.forms import AddressForm from core.models import ( Product, ProductPhoto, Order, OrderLine, ShippingMethod, Transaction, TrackingNumber, Coupon ) from core import ( DiscountValueType, VoucherType, OrderStatus, ShippingMethodType ) from .forms import ( OrderLineFulfillForm, OrderLineFormset, OrderCancelForm, OrderTrackingFormset, CouponForm, ProductPhotoForm ) logger = logging.getLogger(__name__) class DashboardHomeView(LoginRequiredMixin, TemplateView): template_name = 'dashboard/dashboard_detail.html' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) today = timezone.localtime(timezone.now()).date() context['order_count'] = Order.objects.exclude( status=OrderStatus.DRAFT ).filter( created_at__date=today ).count() context['orders_unfulfilled'] = Order.objects.filter( status=OrderStatus.UNFULFILLED ).count() context['todays_sales'] = Order.objects.exclude( status=OrderStatus.DRAFT ).filter( created_at__date=today ).aggregate(total=Sum('total_net_amount'))['total'] return context class DashboardConfigView(TemplateView): template_name = 'dashboard/config.html' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) today = timezone.localtime(timezone.now()).date() context['shipping_method_list'] = ShippingMethod.objects.all() return context class ShippingMethodCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): model = ShippingMethod template_name = 'dashboard/shipmeth_create_form.html' fields = '__all__' success_message = '%(name)s created.' class ShippingMethodDetailView(LoginRequiredMixin, DetailView): model = ShippingMethod template_name = 'dashboard/shipmeth_detail.html' class CouponListView(LoginRequiredMixin, ListView): model = Coupon template_name = 'dashboard/coupon_list.html' class CouponCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): model = Coupon template_name = 'dashboard/coupon_create_form.html' form_class = CouponForm success_message = '%(name)s created.' class CouponDetailView(LoginRequiredMixin, DetailView): model = Coupon template_name = 'dashboard/coupon_detail.html' class CouponUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): model = Coupon template_name = 'dashboard/coupon_form.html' success_message = '%(name)s saved.' form_class = CouponForm class CouponDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView): model = Coupon template_name = 'dashboard/coupon_confirm_delete.html' success_url = reverse_lazy('dashboard:coupon-list') success_message = 'Coupon deleted.' class OrderListView(LoginRequiredMixin, ListView): model = Order template_name = 'dashboard/order_list.html' def get_queryset(self): query = self.request.GET.get('status') if query == 'unfulfilled': object_list = Order.objects.filter( Q(status=OrderStatus.UNFULFILLED) | Q(status=OrderStatus.PARTIALLY_FULFILLED) ).order_by( '-created_at' ).select_related( 'customer' ) else: object_list = Order.objects.order_by( '-created_at' ).select_related( 'customer' ) return object_list class OrderDetailView(LoginRequiredMixin, DetailView): model = Order template_name = 'dashboard/order_detail.html' def get_object(self): queryset = Order.objects.with_fulfillment_and_filter( self.kwargs.get(self.pk_url_kwarg) ).select_related( 'customer', 'billing_address', 'shipping_address', 'shipping_method' ).prefetch_related( 'lines__product__productphoto_set' ) obj = queryset.get() return obj def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) return context class OrderFulfillView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): model = Order template_name = "dashboard/order_fulfill.html" form_class = OrderLineFormset success_message = "Order saved." def form_valid(self, form): form.save() return HttpResponseRedirect(self.get_success_url()) def get_success_url(self): return reverse('dashboard:order-detail', kwargs={'pk': self.object.pk}) class OrderCancelView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): model = Order template_name = "dashboard/order_cancel_form.html" form_class = OrderCancelForm success_message = "Order canceled." initial = { 'status': OrderStatus.CANCELED } def get_success_url(self): return reverse('dashboard:order-detail', kwargs={'pk': self.object.pk}) class OrderTrackingView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): model = Order template_name = "dashboard/order_tracking_form.html" form_class = OrderTrackingFormset success_message = "Order saved." def form_valid(self, form): form.save() return HttpResponseRedirect(self.get_success_url()) def get_success_url(self): return reverse('dashboard:order-detail', kwargs={'pk': self.object.pk}) class ProductListView(LoginRequiredMixin, ListView): model = Product template_name = 'dashboard/product_list.html' ordering = 'sorting' # def get_queryset(self): # object_list = Product.objects.filter( # status=OrderStatus.UNFULFILLED # ).select_related( # 'customer' # ) # return object_list class ProductDetailView(LoginRequiredMixin, DetailView): model = Product template_name = 'dashboard/product_detail.html' class ProductUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): model = Product template_name = 'dashboard/product_update_form.html' fields = '__all__' success_message = '%(name)s saved.' class ProductCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): model = Product template_name = 'dashboard/product_create_form.html' fields = '__all__' success_message = '%(name)s created.' class ProductDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView): model = Product template_name = 'dashboard/product_confirm_delete.html' success_url = reverse_lazy('dashboard:product-list') success_message = 'Product deleted.' class ProductPhotoCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): model = ProductPhoto pk_url_kwarg = 'photo_pk' template_name = 'dashboard/prodphoto_create_form.html' form_class = ProductPhotoForm success_message = 'Photo added.' 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) def get_success_url(self): return reverse('dashboard:product-detail', kwargs={'pk': self.kwargs['pk']}) class ProductPhotoDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView): model = ProductPhoto pk_url_kwarg = 'photo_pk' template_name = 'dashboard/prodphoto_confirm_delete.html' success_message = 'Photo deleted.' def get_success_url(self): return reverse('dashboard:product-detail', kwargs={'pk': self.kwargs['pk']}) class CustomerListView(LoginRequiredMixin, ListView): model = User template_name = 'dashboard/customer_list.html' def get_queryset(self): object_list = User.objects.filter( Exists( Order.objects.filter(customer=OuterRef('pk')) ) | Q(is_staff=False) ).prefetch_related( 'orders' ).annotate( num_orders=Count('orders') ) return object_list class CustomerDetailView(LoginRequiredMixin, DetailView): model = User template_name = 'dashboard/customer_detail.html' context_object_name = 'customer' class CustomerUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): model = User template_name = 'dashboard/customer_form.html' context_object_name = 'customer' success_message = 'Customer saved.' fields = ( 'first_name', 'last_name', 'email', 'is_staff', 'addresses', 'default_shipping_address' ) def get_success_url(self): return reverse('dashboard:customer-detail', kwargs={'pk': self.object.pk})