Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. import os
  2. from django.conf import settings
  3. from django.contrib import messages
  4. from django.core import exceptions
  5. from django.core.urlresolvers import reverse
  6. from django.http import HttpResponseRedirect
  7. from django.shortcuts import get_object_or_404, HttpResponse
  8. from django.template.loader import render_to_string
  9. from django.utils.translation import ungettext, ugettext_lazy as _
  10. from django.views.generic import (
  11. ListView, DeleteView, CreateView, UpdateView, View)
  12. from oscar.views.generic import BulkEditMixin
  13. from oscar.core.loading import get_classes, get_model
  14. Range = get_model('offer', 'Range')
  15. RangeProduct = get_model('offer', 'RangeProduct')
  16. RangeProductFileUpload = get_model('offer', 'RangeProductFileUpload')
  17. Product = get_model('catalogue', 'Product')
  18. RangeForm, RangeProductForm = get_classes('dashboard.ranges.forms',
  19. ['RangeForm', 'RangeProductForm'])
  20. class RangeListView(ListView):
  21. model = Range
  22. context_object_name = 'ranges'
  23. template_name = 'dashboard/ranges/range_list.html'
  24. class RangeCreateView(CreateView):
  25. model = Range
  26. template_name = 'dashboard/ranges/range_form.html'
  27. form_class = RangeForm
  28. def get_success_url(self):
  29. if 'action' in self.request.POST:
  30. return reverse('dashboard:range-products',
  31. kwargs={'pk': self.object.id})
  32. else:
  33. msg = render_to_string(
  34. 'dashboard/ranges/messages/range_saved.html',
  35. {'range': self.object})
  36. messages.success(self.request, msg, extra_tags='safe noicon')
  37. return reverse('dashboard:range-list')
  38. def get_context_data(self, **kwargs):
  39. ctx = super(RangeCreateView, self).get_context_data(**kwargs)
  40. ctx['title'] = _("Create range")
  41. return ctx
  42. class RangeUpdateView(UpdateView):
  43. model = Range
  44. template_name = 'dashboard/ranges/range_form.html'
  45. form_class = RangeForm
  46. def get_object(self):
  47. obj = super(RangeUpdateView, self).get_object()
  48. if not obj.is_editable:
  49. raise exceptions.PermissionDenied("Not allowed")
  50. return obj
  51. def get_success_url(self):
  52. if 'action' in self.request.POST:
  53. return reverse('dashboard:range-products',
  54. kwargs={'pk': self.object.id})
  55. else:
  56. msg = render_to_string(
  57. 'dashboard/ranges/messages/range_saved.html',
  58. {'range': self.object})
  59. messages.success(self.request, msg, extra_tags='safe noicon')
  60. return reverse('dashboard:range-list')
  61. def get_context_data(self, **kwargs):
  62. ctx = super(RangeUpdateView, self).get_context_data(**kwargs)
  63. ctx['range'] = self.object
  64. ctx['title'] = self.object.name
  65. return ctx
  66. class RangeDeleteView(DeleteView):
  67. model = Range
  68. template_name = 'dashboard/ranges/range_delete.html'
  69. context_object_name = 'range'
  70. def get_success_url(self):
  71. messages.warning(self.request, _("Range deleted"))
  72. return reverse('dashboard:range-list')
  73. class RangeProductListView(BulkEditMixin, ListView):
  74. model = Product
  75. template_name = 'dashboard/ranges/range_product_list.html'
  76. context_object_name = 'products'
  77. actions = ('remove_selected_products', 'add_products')
  78. form_class = RangeProductForm
  79. def post(self, request, *args, **kwargs):
  80. self.object_list = self.get_queryset()
  81. if request.POST.get('action', None) == 'add_products':
  82. return self.add_products(request)
  83. return super(RangeProductListView, self).post(request, *args, **kwargs)
  84. def get_range(self):
  85. if not hasattr(self, '_range'):
  86. self._range = get_object_or_404(Range, id=self.kwargs['pk'])
  87. return self._range
  88. def get_queryset(self):
  89. products = self.get_range().included_products.all()
  90. return products.order_by('rangeproduct__display_order')
  91. def get_context_data(self, **kwargs):
  92. ctx = super(RangeProductListView, self).get_context_data(**kwargs)
  93. range = self.get_range()
  94. ctx['range'] = range
  95. if 'form' not in ctx:
  96. ctx['form'] = self.form_class(range)
  97. return ctx
  98. def remove_selected_products(self, request, products):
  99. range = self.get_range()
  100. for product in products:
  101. range.remove_product(product)
  102. num_products = len(products)
  103. messages.success(request, ungettext("Removed %d product from range",
  104. "Removed %d products from range",
  105. num_products) % num_products)
  106. return HttpResponseRedirect(self.get_success_url(request))
  107. def add_products(self, request):
  108. range = self.get_range()
  109. form = self.form_class(range, request.POST, request.FILES)
  110. if not form.is_valid():
  111. ctx = self.get_context_data(form=form,
  112. object_list=self.object_list)
  113. return self.render_to_response(ctx)
  114. self.handle_query_products(request, range, form)
  115. self.handle_file_products(request, range, form)
  116. return HttpResponseRedirect(self.get_success_url(request))
  117. def handle_query_products(self, request, range, form):
  118. products = form.get_products()
  119. if not products:
  120. return
  121. for product in products:
  122. range.add_product(product)
  123. num_products = len(products)
  124. messages.success(request, ungettext("%d product added to range",
  125. "%d products added to range",
  126. num_products) % num_products)
  127. dupe_skus = form.get_duplicate_skus()
  128. if dupe_skus:
  129. messages.warning(
  130. request,
  131. _("The products with SKUs or UPCs matching %s are already "
  132. "in this range") % ", ".join(dupe_skus))
  133. missing_skus = form.get_missing_skus()
  134. if missing_skus:
  135. messages.warning(
  136. request,
  137. _("No product(s) were found with SKU or UPC matching %s") %
  138. ", ".join(missing_skus))
  139. def handle_file_products(self, request, range, form):
  140. if 'file_upload' not in request.FILES:
  141. return
  142. upload = self.create_upload_object(request, range)
  143. upload.process()
  144. if not upload.was_processing_successful():
  145. messages.error(request, upload.error_message)
  146. else:
  147. msg = render_to_string(
  148. 'dashboard/ranges/messages/range_products_saved.html',
  149. {'range': range,
  150. 'upload': upload})
  151. messages.success(request, msg, extra_tags='safe noicon block')
  152. upload.delete_file()
  153. def create_upload_object(self, request, range):
  154. f = request.FILES['file_upload']
  155. destination_path = os.path.join(settings.OSCAR_UPLOAD_ROOT, f.name)
  156. with open(destination_path, 'wb+') as dest:
  157. for chunk in f.chunks():
  158. dest.write(chunk)
  159. upload = RangeProductFileUpload.objects.create(
  160. range=range,
  161. uploaded_by=request.user,
  162. filepath=destination_path,
  163. size=f.size
  164. )
  165. return upload
  166. class RangeReorderView(View):
  167. def post(self, request, pk):
  168. order = dict(request.POST).get('product[]')
  169. self._save_page_order(order)
  170. return HttpResponse(status=200)
  171. def _save_page_order(self, order):
  172. """
  173. Save the order of the products within range.
  174. """
  175. range = get_object_or_404(Range, pk=self.kwargs['pk'])
  176. for index, item in enumerate(order):
  177. entry = RangeProduct.objects.get(range=range, product__pk=item)
  178. if entry.display_order != index:
  179. entry.display_order = index
  180. entry.save()