727 lines
22 KiB
Python
727 lines
22 KiB
Python
import logging
|
|
import json
|
|
from datetime import datetime
|
|
from django.conf import settings
|
|
from django.utils import timezone
|
|
from django import forms
|
|
from django.apps import apps
|
|
from django.shortcuts import render, reverse, redirect, get_object_or_404
|
|
from django.http import JsonResponse, 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.views.decorators.http import require_POST
|
|
from django.contrib.auth.mixins import (
|
|
LoginRequiredMixin, PermissionRequiredMixin
|
|
)
|
|
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,
|
|
ExpressionWrapper, IntegerField
|
|
)
|
|
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 (
|
|
ProductCategory,
|
|
Product,
|
|
ProductPhoto,
|
|
ProductVariant,
|
|
ProductOption,
|
|
Order,
|
|
OrderLine,
|
|
ShippingRate,
|
|
Transaction,
|
|
TrackingNumber,
|
|
Coupon,
|
|
SiteSettings
|
|
)
|
|
|
|
from core import (
|
|
DiscountValueType,
|
|
VoucherType,
|
|
OrderStatus
|
|
)
|
|
from .forms import (
|
|
ProductVariantUpdateForm,
|
|
OrderLineFulfillForm,
|
|
OrderLineFormset,
|
|
OrderCancelForm,
|
|
OrderTrackingFormset,
|
|
CouponForm,
|
|
ProductPhotoForm
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class DashboardHomeView(LoginRequiredMixin, TemplateView):
|
|
template_name = 'dashboard/home.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_amount'))['total']
|
|
return context
|
|
|
|
|
|
class DashboardConfigView(LoginRequiredMixin, TemplateView):
|
|
template_name = 'dashboard/config.html'
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['shipping_rate_list'] = ShippingRate.objects.all()
|
|
return context
|
|
|
|
|
|
class SiteSettingsUpdateView(
|
|
LoginRequiredMixin, PermissionRequiredMixin, UpdateView
|
|
):
|
|
permission_required = 'core.change_sitesettings'
|
|
model = SiteSettings
|
|
context_object_name = 'settings'
|
|
template_name = 'dashboard/settings_form.html'
|
|
fields = '__all__'
|
|
success_url = reverse_lazy('dashboard:config')
|
|
success_message = 'Settings saved.'
|
|
|
|
|
|
class CatalogView(LoginRequiredMixin, ListView):
|
|
model = ProductCategory
|
|
context_object_name = 'category_list'
|
|
template_name = 'dashboard/catalog.html'
|
|
|
|
def get_context_data(self, *args, **kwargs):
|
|
context = super().get_context_data(*args, **kwargs)
|
|
context['uncategorized_products'] = Product.objects.filter(
|
|
category=None
|
|
)
|
|
context['option_list'] = ProductOption.objects.all()
|
|
return context
|
|
|
|
|
|
class StockView(LoginRequiredMixin, 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'
|
|
template_name = 'dashboard/rate/detail.html'
|
|
|
|
|
|
class ShippingRateCreateView(
|
|
LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView
|
|
):
|
|
permission_required = 'core.add_shippingrate'
|
|
model = ShippingRate
|
|
context_object_name = 'rate'
|
|
template_name = 'dashboard/rate/create_form.html'
|
|
fields = '__all__'
|
|
success_message = '%(name)s created.'
|
|
|
|
|
|
class ShippingRateUpdateView(
|
|
LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView
|
|
):
|
|
permission_required = 'core.change_shippingrate'
|
|
model = ShippingRate
|
|
context_object_name = 'rate'
|
|
template_name = 'dashboard/rate/form.html'
|
|
success_message = 'ShippingRate saved.'
|
|
fields = '__all__'
|
|
|
|
|
|
class ShippingRateDeleteView(
|
|
LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, DeleteView
|
|
):
|
|
permission_required = 'core.delete_shippingrate'
|
|
model = ShippingRate
|
|
context_object_name = 'rate'
|
|
template_name = 'dashboard/rate/confirm_delete.html'
|
|
success_message = 'ShippingRate deleted.'
|
|
success_url = reverse_lazy('dashboard:config')
|
|
|
|
|
|
class CouponListView(LoginRequiredMixin, ListView):
|
|
model = Coupon
|
|
template_name = 'dashboard/coupon/list.html'
|
|
|
|
|
|
class CouponCreateView(
|
|
LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView
|
|
):
|
|
permission_required = 'core.add_coupon'
|
|
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, PermissionRequiredMixin, SuccessMessageMixin, UpdateView
|
|
):
|
|
permission_required = 'core.change_coupon'
|
|
model = Coupon
|
|
template_name = 'dashboard/coupon/form.html'
|
|
success_message = '%(name)s saved.'
|
|
form_class = CouponForm
|
|
|
|
|
|
class CouponDeleteView(
|
|
LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, DeleteView
|
|
):
|
|
permission_required = 'core.delete_coupon'
|
|
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'
|
|
paginate_by = 50
|
|
|
|
def get_queryset(self):
|
|
status = self.request.GET.get('status')
|
|
query = self.request.GET.get('q')
|
|
|
|
object_list = Order.objects.order_by(
|
|
'-created_at'
|
|
).select_related('customer')
|
|
|
|
if status == 'unfulfilled':
|
|
object_list = object_list.filter(
|
|
Q(status=OrderStatus.UNFULFILLED) |
|
|
Q(status=OrderStatus.PARTIALLY_FULFILLED)
|
|
)
|
|
|
|
if query:
|
|
object_list = find_order_by_no_or_customer(object_list, query)
|
|
|
|
return object_list.order_by('-created_at').select_related('customer')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context["query"] = self.request.GET.get("q")
|
|
return context
|
|
|
|
|
|
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'
|
|
).prefetch_related(
|
|
'lines__variant__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, PermissionRequiredMixin, SuccessMessageMixin, UpdateView
|
|
):
|
|
permission_required = 'core.cancel_order'
|
|
model = Order
|
|
template_name = 'dashboard/order/cancel_form.html'
|
|
form_class = OrderCancelForm
|
|
success_message = 'Order canceled.'
|
|
initial = {
|
|
'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})
|
|
|
|
|
|
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 CategoryListView(LoginRequiredMixin, ListView):
|
|
model = ProductCategory
|
|
context_object_name = 'category_list'
|
|
template_name = 'dashboard/category/list.html'
|
|
|
|
|
|
class CategoryCreateView(
|
|
LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView
|
|
):
|
|
permission_required = 'core.add_productcategory'
|
|
model = ProductCategory
|
|
context_object_name = 'category'
|
|
success_message = 'Category created.'
|
|
template_name = 'dashboard/category/create_form.html'
|
|
fields = '__all__'
|
|
|
|
|
|
class CategoryDetailView(LoginRequiredMixin, DetailView):
|
|
model = ProductCategory
|
|
context_object_name = 'category'
|
|
template_name = 'dashboard/category/detail.html'
|
|
|
|
|
|
class CategoryUpdateView(
|
|
LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView
|
|
):
|
|
permission_required = 'core.change_productcategory'
|
|
model = ProductCategory
|
|
context_object_name = 'category'
|
|
success_message = 'Category saved.'
|
|
template_name = 'dashboard/category/form.html'
|
|
fields = '__all__'
|
|
|
|
|
|
class CategoryDeleteView(
|
|
LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, DeleteView
|
|
):
|
|
permission_required = 'core.delete_productcategory'
|
|
model = ProductCategory
|
|
context_object_name = 'category'
|
|
success_message = 'Category deleted.'
|
|
template_name = 'dashboard/category/confirm_delete.html'
|
|
success_url = reverse_lazy('dashboard:catalog')
|
|
|
|
|
|
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'
|
|
|
|
def get_object(self):
|
|
pk = self.kwargs.get(self.pk_url_kwarg)
|
|
queryset = Product.objects.filter(
|
|
pk=pk
|
|
).select_related(
|
|
'category',
|
|
).prefetch_related(
|
|
'options',
|
|
'productphoto_set',
|
|
Prefetch(
|
|
'variants',
|
|
queryset=ProductVariant.objects.all().order_by('sorting')
|
|
)
|
|
)
|
|
obj = queryset.get()
|
|
return obj
|
|
|
|
|
|
class ProductCreateView(
|
|
LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView
|
|
):
|
|
permission_required = 'core.add_product'
|
|
model = Product
|
|
template_name = 'dashboard/product/create_form.html'
|
|
fields = '__all__'
|
|
success_message = '%(name)s created.'
|
|
|
|
|
|
class ProductUpdateView(
|
|
LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView
|
|
):
|
|
permission_required = 'core.change_product'
|
|
model = Product
|
|
template_name = 'dashboard/product/form.html'
|
|
fields = '__all__'
|
|
success_message = '%(name)s saved.'
|
|
|
|
|
|
class ProductDeleteView(
|
|
LoginRequiredMixin,
|
|
PermissionRequiredMixin,
|
|
SuccessMessageMixin,
|
|
DeleteView
|
|
):
|
|
permission_required = 'core.delete_product'
|
|
model = Product
|
|
template_name = 'dashboard/product/confirm_delete.html'
|
|
success_url = reverse_lazy('dashboard:catalog')
|
|
success_message = 'Product deleted.'
|
|
|
|
|
|
class ProductPhotoCreateView(
|
|
LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView
|
|
):
|
|
permission_required = 'core.add_productphoto'
|
|
model = ProductPhoto
|
|
pk_url_kwarg = 'photo_pk'
|
|
template_name = 'dashboard/product/photo_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, PermissionRequiredMixin, SuccessMessageMixin, DeleteView
|
|
):
|
|
permission_required = 'core.delete_productphoto'
|
|
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 ProductVariantCreateView(
|
|
LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView
|
|
):
|
|
permission_required = 'core.add_productvariant'
|
|
model = ProductVariant
|
|
success_message = 'Variant created.'
|
|
template_name = 'dashboard/variant/create_form.html'
|
|
fields = [
|
|
'name',
|
|
'sku',
|
|
'price',
|
|
'weight',
|
|
'visible_in_listings',
|
|
'track_inventory',
|
|
'stock',
|
|
'order_limit',
|
|
]
|
|
|
|
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 ProductVariantUpdateView(
|
|
LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView
|
|
):
|
|
permission_required = 'core.change_productvariant'
|
|
model = ProductVariant
|
|
pk_url_kwarg = 'variant_pk'
|
|
success_message = 'Variant saved.'
|
|
template_name = 'dashboard/variant/form.html'
|
|
form_class = ProductVariantUpdateForm
|
|
context_object_name = 'variant'
|
|
|
|
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 ProductVariantDeleteView(
|
|
LoginRequiredMixin,
|
|
PermissionRequiredMixin,
|
|
SuccessMessageMixin,
|
|
DeleteView
|
|
):
|
|
permission_required = 'core.delete_productvariant'
|
|
model = ProductVariant
|
|
pk_url_kwarg = 'variant_pk'
|
|
success_message = 'ProductVariant deleted.'
|
|
template_name = 'dashboard/variant/confirm_delete.html'
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['product'] = Product.objects.get(pk=self.kwargs['pk'])
|
|
return context
|
|
|
|
def get_success_url(self):
|
|
return reverse('dashboard:product-detail', kwargs={'pk': self.kwargs['pk']})
|
|
|
|
|
|
class ProductVariantStockUpdateView(
|
|
LoginRequiredMixin, PermissionRequiredMixin, UpdateView
|
|
):
|
|
permission_required = 'core.change_productvariant'
|
|
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'
|
|
context_object_name = 'option'
|
|
|
|
|
|
class ProductOptionCreateView(
|
|
LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView
|
|
):
|
|
permission_required = 'core.add_productoption'
|
|
model = ProductOption
|
|
template_name = 'dashboard/option/create_form.html'
|
|
fields = [
|
|
'name',
|
|
'options',
|
|
'products',
|
|
]
|
|
success_message = '%(name)s created.'
|
|
|
|
|
|
class ProductOptionUpdateView(
|
|
LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView
|
|
):
|
|
permission_required = 'core.change_productoption'
|
|
model = ProductOption
|
|
success_message = 'Option saved.'
|
|
template_name = 'dashboard/option/form.html'
|
|
fields = [
|
|
'name',
|
|
'options',
|
|
'products',
|
|
]
|
|
context_object_name = 'option'
|
|
success_url = reverse_lazy('dashboard:catalog')
|
|
|
|
|
|
class ProductOptionDeleteView(
|
|
LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, DeleteView
|
|
):
|
|
permission_required = 'core.delete_productoption'
|
|
model = ProductOption
|
|
success_message = 'ProductOption deleted.'
|
|
template_name = 'dashboard/option/confirm_delete.html'
|
|
context_object_name = 'option'
|
|
success_url = reverse_lazy('dashboard:catalog')
|
|
|
|
|
|
def sort(objs, order):
|
|
for i, pk in enumerate(order):
|
|
m = objs.get(pk=pk)
|
|
m.sorting = i+1
|
|
yield m
|
|
|
|
|
|
@require_POST
|
|
def update_sorting(request):
|
|
data = json.loads(request.body)
|
|
model = apps.get_model('core', data['model_name'])
|
|
objs = model.objects.filter(
|
|
Q((data['filter'], data['filter_id']))
|
|
).order_by('sorting')
|
|
|
|
updated_objs = sort(objs, data['order'])
|
|
|
|
model.objects.bulk_update(updated_objs, ['sorting'])
|
|
return JsonResponse({'message': 'Sorting updated'})
|
|
|
|
|
|
def find_user_by_name_or_email(qs, query):
|
|
for term in query.split():
|
|
qs = qs.filter(
|
|
Q(first_name__icontains = term) | Q(last_name__icontains = term)
|
|
| Q(email__icontains = term)
|
|
)
|
|
return qs
|
|
|
|
|
|
def find_order_by_no_or_customer(qs, query):
|
|
for term in query.split():
|
|
qs = qs.filter(
|
|
Q(pk__icontains = term)
|
|
| Q(customer__first_name__icontains = term)
|
|
| Q(customer__last_name__icontains = term)
|
|
| Q(customer__email__icontains = term)
|
|
)
|
|
return qs
|
|
|
|
|
|
class CustomerListView(LoginRequiredMixin, ListView):
|
|
model = User
|
|
template_name = 'dashboard/customer/list.html'
|
|
paginate_by = 100
|
|
|
|
def get_queryset(self):
|
|
query = self.request.GET.get('q')
|
|
object_list = User.objects.filter(
|
|
Exists(
|
|
Order.objects.filter(customer=OuterRef('pk'))
|
|
) | Q(is_staff=False)
|
|
).prefetch_related(
|
|
'orders'
|
|
).annotate(
|
|
num_orders=Count('orders')
|
|
).order_by('first_name', 'last_name')
|
|
|
|
if query:
|
|
object_list = find_user_by_name_or_email(object_list, query)
|
|
|
|
return object_list
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context["query"] = self.request.GET.get("q")
|
|
return context
|
|
|
|
|
|
class CustomerDetailView(LoginRequiredMixin, DetailView):
|
|
model = User
|
|
template_name = 'dashboard/customer/detail.html'
|
|
context_object_name = 'customer'
|
|
|
|
|
|
class CustomerUpdateView(
|
|
LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView
|
|
):
|
|
permission_required = 'accounts.change_user'
|
|
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})
|