| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- from io import TextIOWrapper
-
- from django.conf import settings
- from django.contrib import messages
- from django.core import exceptions
- from django.db.models import Count
- from django.http import HttpResponseRedirect
- from django.shortcuts import HttpResponse, get_object_or_404
- from django.template.loader import render_to_string
- from django.urls import reverse
- from django.utils.translation import gettext_lazy as _
- from django.utils.translation import ngettext
- from django.views.generic import (
- CreateView, DeleteView, ListView, UpdateView, View)
-
- from oscar.core.loading import get_classes, get_model
- from oscar.views.generic import BulkEditMixin
-
- Range = get_model('offer', 'Range')
- RangeProduct = get_model('offer', 'RangeProduct')
- RangeProductFileUpload = get_model('offer', 'RangeProductFileUpload')
- Product = get_model('catalogue', 'Product')
- RangeForm, RangeProductForm = get_classes('dashboard.ranges.forms',
- ['RangeForm', 'RangeProductForm'])
-
-
- class RangeListView(ListView):
- model = Range
- context_object_name = 'ranges'
- template_name = 'oscar/dashboard/ranges/range_list.html'
- paginate_by = settings.OSCAR_DASHBOARD_ITEMS_PER_PAGE
-
-
- class RangeCreateView(CreateView):
- model = Range
- template_name = 'oscar/dashboard/ranges/range_form.html'
- form_class = RangeForm
-
- def get_success_url(self):
- if 'action' in self.request.POST:
- return reverse('dashboard:range-products',
- kwargs={'pk': self.object.id})
- else:
- msg = render_to_string(
- 'oscar/dashboard/ranges/messages/range_saved.html',
- {'range': self.object})
- messages.success(self.request, msg, extra_tags='safe noicon')
- return reverse('dashboard:range-list')
-
- def get_context_data(self, **kwargs):
- ctx = super().get_context_data(**kwargs)
- ctx['title'] = _("Create range")
- return ctx
-
-
- class RangeUpdateView(UpdateView):
- model = Range
- template_name = 'oscar/dashboard/ranges/range_form.html'
- form_class = RangeForm
-
- def get_object(self):
- obj = super().get_object()
- if not obj.is_editable:
- raise exceptions.PermissionDenied("Not allowed")
- return obj
-
- def get_success_url(self):
- if 'action' in self.request.POST:
- return reverse('dashboard:range-products',
- kwargs={'pk': self.object.id})
- else:
- msg = render_to_string(
- 'oscar/dashboard/ranges/messages/range_saved.html',
- {'range': self.object})
- messages.success(self.request, msg, extra_tags='safe noicon')
- return reverse('dashboard:range-list')
-
- def get_context_data(self, **kwargs):
- ctx = super().get_context_data(**kwargs)
- ctx['range'] = self.object
- ctx['title'] = self.object.name
- return ctx
-
-
- class RangeDeleteView(DeleteView):
- model = Range
- template_name = 'oscar/dashboard/ranges/range_delete.html'
- context_object_name = 'range'
-
- def get_success_url(self):
- messages.warning(self.request, _("Range deleted"))
- return reverse('dashboard:range-list')
-
-
- class RangeProductListView(BulkEditMixin, ListView):
- model = Product
- template_name = 'oscar/dashboard/ranges/range_product_list.html'
- context_object_name = 'products'
- actions = ('remove_selected_products', 'add_products')
- form_class = RangeProductForm
- paginate_by = settings.OSCAR_DASHBOARD_ITEMS_PER_PAGE
-
- def post(self, request, *args, **kwargs):
- self.object_list = self.get_queryset()
- if request.POST.get('action', None) == 'add_products':
- return self.add_products(request)
- return super().post(request, *args, **kwargs)
-
- def get_range(self):
- if not hasattr(self, '_range'):
- self._range = get_object_or_404(Range, id=self.kwargs['pk'])
- return self._range
-
- def get_queryset(self):
- products = self.get_range().all_products()
- return products.order_by('rangeproduct__display_order')
-
- def get_context_data(self, **kwargs):
- ctx = super().get_context_data(**kwargs)
- range = self.get_range()
- ctx['range'] = range
- if 'form' not in ctx:
- ctx['form'] = self.form_class(range)
- return ctx
-
- def remove_selected_products(self, request, products):
- range = self.get_range()
- for product in products:
- range.remove_product(product)
- num_products = len(products)
- messages.success(
- request,
- ngettext("Removed %d product from range", "Removed %d products from range", num_products) % num_products
- )
- return HttpResponseRedirect(self.get_success_url(request))
-
- def add_products(self, request):
- range = self.get_range()
- form = self.form_class(range, request.POST, request.FILES)
- if not form.is_valid():
- ctx = self.get_context_data(form=form,
- object_list=self.object_list)
- return self.render_to_response(ctx)
-
- self.handle_query_products(request, range, form)
- self.handle_file_products(request, range, form)
- return HttpResponseRedirect(self.get_success_url(request))
-
- def handle_query_products(self, request, range, form):
- products = form.get_products()
- if not products:
- return
-
- for product in products:
- range.add_product(product)
-
- num_products = len(products)
- messages.success(
- request,
- ngettext("%d product added to range", "%d products added to range", num_products) % num_products
- )
- dupe_skus = form.get_duplicate_skus()
- if dupe_skus:
- messages.warning(
- request,
- _("The products with SKUs or UPCs matching %s are already "
- "in this range") % ", ".join(dupe_skus))
-
- missing_skus = form.get_missing_skus()
- if missing_skus:
- messages.warning(
- request,
- _("No product(s) were found with SKU or UPC matching %s") %
- ", ".join(missing_skus))
- self.check_imported_products_sku_duplicates(request, products)
-
- def handle_file_products(self, request, range, form):
- if 'file_upload' not in request.FILES:
- return
- f = request.FILES['file_upload']
- upload = self.create_upload_object(request, range, f)
- products = upload.process(TextIOWrapper(f, encoding=request.encoding))
- if not upload.was_processing_successful():
- messages.error(request, upload.error_message)
- else:
- msg = render_to_string(
- 'oscar/dashboard/ranges/messages/range_products_saved.html',
- {'range': range,
- 'upload': upload})
- messages.success(request, msg, extra_tags='safe noicon block')
- self.check_imported_products_sku_duplicates(request, products)
-
- def create_upload_object(self, request, range, f):
- upload = RangeProductFileUpload.objects.create(
- range=range,
- uploaded_by=request.user,
- filepath=f.name,
- size=f.size
- )
- return upload
-
- def check_imported_products_sku_duplicates(self, request, queryset):
- dupe_sku_products = queryset.values('stockrecords__partner_sku')\
- .annotate(total=Count('stockrecords__partner_sku'))\
- .filter(total__gt=1).order_by('stockrecords__partner_sku')
- if dupe_sku_products:
- dupe_skus = [p['stockrecords__partner_sku'] for p in dupe_sku_products]
- messages.warning(
- request,
- _("There are more than one product with SKU %s") %
- ", ".join(dupe_skus)
- )
-
-
- class RangeReorderView(View):
- def post(self, request, pk):
- order = dict(request.POST).get('product')
- self._save_page_order(order)
- return HttpResponse(status=200)
-
- def _save_page_order(self, order):
- """
- Save the order of the products within range.
- """
- range_products = RangeProduct.objects.filter(
- range_id=self.kwargs['pk'], product_id__in=order
- )
- for range_product in range_products:
- range_product.display_order = order.index(
- str(range_product.product_id)
- )
- RangeProduct.objects.bulk_update(range_products, ["display_order"])
|