""" Views для работы с трансформациями товаров (Transformation). """ from django.views.generic import ListView, CreateView, DetailView, View from django.contrib.auth.mixins import LoginRequiredMixin from django.urls import reverse from django.shortcuts import get_object_or_404, redirect from django.contrib import messages from django.http import JsonResponse from django.db import transaction from django.core.exceptions import ValidationError from inventory.models import Transformation, TransformationInput, TransformationOutput from inventory.forms import TransformationForm, TransformationInputForm, TransformationOutputForm from inventory.services.transformation_service import TransformationService class TransformationListView(LoginRequiredMixin, ListView): """Список трансформаций товаров""" model = Transformation template_name = 'inventory/transformation/list.html' context_object_name = 'transformations' paginate_by = 20 def get_queryset(self): return Transformation.objects.select_related( 'warehouse', 'employee' ).prefetch_related('inputs__product', 'outputs__product').order_by('-date') class TransformationCreateView(LoginRequiredMixin, CreateView): """Создание трансформации""" model = Transformation form_class = TransformationForm template_name = 'inventory/transformation/form.html' def form_valid(self, form): transformation = TransformationService.create_transformation( warehouse=form.cleaned_data['warehouse'], comment=form.cleaned_data.get('comment'), employee=self.request.user ) messages.success(self.request, f'Трансформация {transformation.document_number} создана') return redirect('inventory:transformation-detail', pk=transformation.pk) class TransformationDetailView(LoginRequiredMixin, DetailView): """Детальный просмотр трансформации""" model = Transformation template_name = 'inventory/transformation/detail.html' context_object_name = 'transformation' def get_queryset(self): return Transformation.objects.select_related( 'warehouse', 'employee' ).prefetch_related('inputs__product', 'outputs__product') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['input_form'] = TransformationInputForm(transformation=self.object) context['output_form'] = TransformationOutputForm(transformation=self.object) # Добавляем категории и теги для компонента поиска товаров from products.models import ProductCategory, ProductTag context['categories'] = ProductCategory.objects.filter(is_active=True).order_by('name') context['tags'] = ProductTag.objects.filter(is_active=True).order_by('name') return context class TransformationAddInputView(LoginRequiredMixin, View): """Добавление входного товара в трансформацию""" @transaction.atomic def post(self, request, pk): transformation = get_object_or_404(Transformation, pk=pk) form = TransformationInputForm(request.POST, transformation=transformation) if form.is_valid(): try: trans_input = TransformationService.add_input( transformation=transformation, product=form.cleaned_data['product'], quantity=form.cleaned_data['quantity'] ) if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return JsonResponse({ 'success': True, 'item_id': trans_input.id, 'message': f'Добавлено: {trans_input.product.name}' }) messages.success(request, f'Добавлено: {trans_input.product.name}') except ValidationError as e: if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return JsonResponse({'success': False, 'error': str(e)}, status=400) messages.error(request, str(e)) else: if request.headers.get('X-Requested-With') == 'XMLHttpRequest': errors = '; '.join([f"{k}: {v[0]}" for k, v in form.errors.items()]) return JsonResponse({'success': False, 'error': errors}, status=400) for field, errors in form.errors.items(): for error in errors: messages.error(request, f'{field}: {error}') return redirect('inventory:transformation-detail', pk=pk) class TransformationAddOutputView(LoginRequiredMixin, View): """Добавление выходного товара в трансформацию""" @transaction.atomic def post(self, request, pk): transformation = get_object_or_404(Transformation, pk=pk) form = TransformationOutputForm(request.POST, transformation=transformation) if form.is_valid(): try: trans_output = TransformationService.add_output( transformation=transformation, product=form.cleaned_data['product'], quantity=form.cleaned_data['quantity'] ) if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return JsonResponse({ 'success': True, 'item_id': trans_output.id, 'message': f'Добавлено: {trans_output.product.name}' }) messages.success(request, f'Добавлено: {trans_output.product.name}') except ValidationError as e: if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return JsonResponse({'success': False, 'error': str(e)}, status=400) messages.error(request, str(e)) else: if request.headers.get('X-Requested-With') == 'XMLHttpRequest': errors = '; '.join([f"{k}: {v[0]}" for k, v in form.errors.items()]) return JsonResponse({'success': False, 'error': errors}, status=400) for field, errors in form.errors.items(): for error in errors: messages.error(request, f'{field}: {error}') return redirect('inventory:transformation-detail', pk=pk) class TransformationRemoveInputView(LoginRequiredMixin, View): """Удаление входного товара из трансформации""" @transaction.atomic def post(self, request, pk, item_pk): transformation = get_object_or_404(Transformation, pk=pk) trans_input = get_object_or_404(TransformationInput, pk=item_pk, transformation=transformation) try: product_name = trans_input.product.name TransformationService.remove_input(trans_input) if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return JsonResponse({ 'success': True, 'message': f'Удалено: {product_name}' }) messages.success(request, f'Удалено: {product_name}') except ValidationError as e: if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return JsonResponse({'success': False, 'error': str(e)}, status=400) messages.error(request, str(e)) return redirect('inventory:transformation-detail', pk=pk) class TransformationRemoveOutputView(LoginRequiredMixin, View): """Удаление выходного товара из трансформации""" @transaction.atomic def post(self, request, pk, item_pk): transformation = get_object_or_404(Transformation, pk=pk) trans_output = get_object_or_404(TransformationOutput, pk=item_pk, transformation=transformation) try: product_name = trans_output.product.name TransformationService.remove_output(trans_output) if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return JsonResponse({ 'success': True, 'message': f'Удалено: {product_name}' }) messages.success(request, f'Удалено: {product_name}') except ValidationError as e: if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return JsonResponse({'success': False, 'error': str(e)}, status=400) messages.error(request, str(e)) return redirect('inventory:transformation-detail', pk=pk) class TransformationConfirmView(LoginRequiredMixin, View): """Проведение трансформации""" @transaction.atomic def post(self, request, pk): transformation = get_object_or_404(Transformation, pk=pk) try: TransformationService.confirm(transformation) messages.success(request, f'Трансформация {transformation.document_number} проведена') except ValidationError as e: messages.error(request, str(e)) return redirect('inventory:transformation-detail', pk=pk) class TransformationCancelView(LoginRequiredMixin, View): """Отмена трансформации""" @transaction.atomic def post(self, request, pk): transformation = get_object_or_404(Transformation, pk=pk) try: TransformationService.cancel(transformation) messages.success(request, f'Трансформация {transformation.document_number} отменена') except ValidationError as e: messages.error(request, str(e)) return redirect('inventory:transformation-list')