""" CRUD представления для комплектов товаров (ProductKit). """ from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin from django.views.generic import ListView, CreateView, DetailView, UpdateView, DeleteView from django.urls import reverse_lazy from django.shortcuts import redirect from django.db import transaction from ..models import ProductKit, ProductCategory, ProductTag, ProductKitPhoto from ..forms import ProductKitForm, KitItemFormSetCreate, KitItemFormSetUpdate from .utils import handle_photos class ProductKitListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): model = ProductKit template_name = 'products/productkit_list.html' context_object_name = 'kits' permission_required = 'products.view_productkit' paginate_by = 10 def get_queryset(self): queryset = super().get_queryset() queryset = queryset.prefetch_related('categories', 'photos', 'kit_items', 'tags') # Поиск по названию search_query = self.request.GET.get('search') if search_query: queryset = queryset.filter(name__icontains=search_query) # Фильтр по категории category_id = self.request.GET.get('category') if category_id: queryset = queryset.filter(categories__id=category_id) # Фильтр по статусу is_active = self.request.GET.get('is_active') if is_active == '1': queryset = queryset.filter(is_active=True) elif is_active == '0': queryset = queryset.filter(is_active=False) return queryset.order_by('-created_at') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # Данные для фильтров context['filters'] = { 'categories': ProductCategory.objects.filter(is_active=True), 'tags': ProductTag.objects.all(), 'current': { 'search': self.request.GET.get('search', ''), 'category': self.request.GET.get('category', ''), 'is_active': self.request.GET.get('is_active', ''), 'tags': self.request.GET.getlist('tags'), } } # Кнопки действий action_buttons = [] if self.request.user.has_perm('products.add_productkit'): action_buttons.append({ 'url': reverse_lazy('products:productkit-create'), 'text': 'Создать комплект', 'class': 'btn-primary', 'icon': 'plus-circle' }) action_buttons.append({ 'url': reverse_lazy('products:product-list'), 'text': 'К товарам', 'class': 'btn-outline-primary', 'icon': 'box' }) context['action_buttons'] = action_buttons return context class ProductKitCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): """ View для создания нового комплекта с компонентами. """ model = ProductKit form_class = ProductKitForm template_name = 'products/productkit_form.html' permission_required = 'products.add_productkit' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) if self.request.POST: context['kititem_formset'] = KitItemFormSetCreate(self.request.POST, instance=self.object) else: context['kititem_formset'] = KitItemFormSetCreate(instance=self.object) return context def form_valid(self, form): # Получаем формсет из POST kititem_formset = KitItemFormSetCreate(self.request.POST, instance=self.object) # Проверяем валидность формсета if kititem_formset.is_valid(): try: with transaction.atomic(): # Сохраняем основную форму self.object = form.save() # Сохраняем компоненты kititem_formset.instance = self.object kititem_formset.save() # Обработка фотографий handle_photos(self.request, self.object, ProductKitPhoto, 'kit') messages.success(self.request, f'Комплект "{self.object.name}" успешно создан!') # Проверяем, какую кнопку нажали if self.request.POST.get('action') == 'continue': return redirect('products:productkit-update', pk=self.object.pk) else: return redirect('products:productkit-list') except Exception as e: messages.error(self.request, f'Ошибка при сохранении: {str(e)}') return self.form_invalid(form) else: # Если формсет невалиден, показываем форму с ошибками messages.error(self.request, 'Пожалуйста, исправьте ошибки в компонентах комплекта.') return self.form_invalid(form) def form_invalid(self, form): # Получаем формсет для отображения ошибок context = self.get_context_data(form=form) return self.render_to_response(context) def get_success_url(self): return reverse_lazy('products:productkit-list') class ProductKitUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): """ View для редактирования существующего комплекта. """ model = ProductKit form_class = ProductKitForm template_name = 'products/productkit_form.html' permission_required = 'products.change_productkit' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) if self.request.POST: context['kititem_formset'] = KitItemFormSetUpdate(self.request.POST, instance=self.object) else: context['kititem_formset'] = KitItemFormSetUpdate(instance=self.object) context['productkit_photos'] = self.object.photos.all().order_by('order', 'created_at') context['photos_count'] = self.object.photos.count() return context def form_valid(self, form): # Получаем формсет из POST kititem_formset = KitItemFormSetUpdate(self.request.POST, instance=self.object) # Проверяем валидность формсета if kititem_formset.is_valid(): try: with transaction.atomic(): # Сохраняем основную форму self.object = form.save() # Сохраняем компоненты kititem_formset.instance = self.object kititem_formset.save() # Обработка фотографий handle_photos(self.request, self.object, ProductKitPhoto, 'kit') messages.success(self.request, f'Комплект "{self.object.name}" успешно обновлен!') # Проверяем, какую кнопку нажали if self.request.POST.get('action') == 'continue': return redirect('products:productkit-update', pk=self.object.pk) else: return redirect('products:productkit-list') except Exception as e: messages.error(self.request, f'Ошибка при сохранении: {str(e)}') return self.form_invalid(form) else: # Если формсет невалиден, показываем форму с ошибками messages.error(self.request, 'Пожалуйста, исправьте ошибки в компонентах комплекта.') return self.form_invalid(form) def form_invalid(self, form): # Получаем формсет для отображения ошибок context = self.get_context_data(form=form) return self.render_to_response(context) def get_success_url(self): return reverse_lazy('products:productkit-list') class ProductKitDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): """ View для просмотра деталей комплекта. Показывает все компоненты, цены, фотографии. """ model = ProductKit template_name = 'products/productkit_detail.html' context_object_name = 'kit' permission_required = 'products.view_productkit' def get_queryset(self): # Prefetch для оптимизации запросов return super().get_queryset().prefetch_related( 'photos', 'kit_items__product', 'kit_items__variant_group', 'tags' ) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # Добавляем фотографии комплекта в контекст context['productkit_photos'] = self.object.photos.all().order_by('order', 'created_at') context['photos_count'] = self.object.photos.count() # Добавляем компоненты context['kit_items'] = self.object.kit_items.all().select_related('product', 'variant_group') return context class ProductKitDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): """ View для удаления комплекта. """ model = ProductKit template_name = 'products/productkit_confirm_delete.html' context_object_name = 'kit' permission_required = 'products.delete_productkit' def get_success_url(self): messages.success(self.request, f'Комплект "{self.object.name}" успешно удален!') return reverse_lazy('products:productkit-list')