# -*- coding: utf-8 -*- from django.shortcuts import render, redirect, get_object_or_404 from django.contrib import messages from django.core.paginator import Paginator from django.http import JsonResponse from django.views.decorators.http import require_http_methods from django.contrib.auth.decorators import login_required from django.core.exceptions import ValidationError from .models import Order, OrderItem, Address from .forms import OrderForm, OrderItemFormSet from .filters import OrderFilter from .services import DraftOrderService from .services.address_service import AddressService import json def order_list(request): """ Список всех заказов с фильтрацией и поиском Использует django-filter для фильтрации данных """ # Базовый queryset с оптимизацией запросов orders = Order.objects.select_related( 'customer', 'delivery_address', 'pickup_shop' ).all() # Применяем фильтры через django-filter order_filter = OrderFilter(request.GET, queryset=orders) # Сортировка filtered_orders = order_filter.qs.order_by('-created_at') # Пагинация paginator = Paginator(filtered_orders, 25) page_number = request.GET.get('page') page_obj = paginator.get_page(page_number) context = { 'filter': order_filter, 'page_obj': page_obj, 'status_choices': Order.STATUS_CHOICES, } return render(request, 'orders/order_list.html', context) def order_detail(request, pk): """Детальная информация о заказе""" order = get_object_or_404( Order.objects.select_related('customer', 'delivery_address', 'pickup_shop', 'modified_by') .prefetch_related('items__product', 'items__product_kit', 'payments__created_by'), pk=pk ) context = { 'order': order, } return render(request, 'orders/order_detail.html', context) def order_create(request): """Создание нового заказа""" if request.method == 'POST': form = OrderForm(request.POST) formset = OrderItemFormSet(request.POST) if form.is_valid() and formset.is_valid(): order = form.save(commit=False) # Обрабатываем адрес доставки if order.is_delivery: address = AddressService.process_address_from_form(order, form.cleaned_data) if address: # Если адрес не существует в БД, сохраняем его if not address.pk: address.save() order.delivery_address = address # Если нажата кнопка "Сохранить как черновик", создаем черновик if 'save_as_draft' in request.POST: order.status = 'draft' order.modified_by = request.user order.save() # Сохраняем позиции заказа formset.instance = order formset.save() # Пересчитываем итоговую сумму order.calculate_total() order.save() if order.is_draft(): messages.success(request, f'Черновик #{order.order_number} успешно создан!') else: messages.success(request, f'Заказ #{order.order_number} успешно создан!') return redirect('orders:order-detail', pk=order.pk) else: messages.error(request, 'Пожалуйста, исправьте ошибки в форме.') else: form = OrderForm() formset = OrderItemFormSet() context = { 'form': form, 'formset': formset, 'title': 'Создание заказа', 'button_text': 'Создать заказ', } return render(request, 'orders/order_form.html', context) def order_update(request, pk): """Редактирование заказа""" order = get_object_or_404(Order, pk=pk) if request.method == 'POST': form = OrderForm(request.POST, instance=order) formset = OrderItemFormSet(request.POST, instance=order) if form.is_valid() and formset.is_valid(): order = form.save(commit=False) # Если черновик финализируется if 'finalize_draft' in request.POST and order.is_draft(): try: order = DraftOrderService.finalize_draft(order.pk, request.user) messages.success(request, f'Черновик #{order.order_number} успешно завершен и переведен в статус "Новый"!') return redirect('orders:order-detail', pk=order.pk) except ValidationError as e: messages.error(request, f'Ошибка финализации: {str(e)}') form = OrderForm(instance=order) formset = OrderItemFormSet(instance=order) else: # Обрабатываем адрес доставки if order.is_delivery: address = AddressService.process_address_from_form(order, form.cleaned_data) if address: # Если адрес не существует в БД, сохраняем его if not address.pk: address.save() order.delivery_address = address else: # Если режим "без адреса", удаляем существующий адрес if order.delivery_address: old_address = order.delivery_address order.delivery_address = None # Удаляем старый адрес, если он больше не используется if old_address and not old_address.order: old_address.delete() else: # Если не доставка, удаляем адрес если он был if order.delivery_address: old_address = order.delivery_address order.delivery_address = None # Удаляем старый адрес if old_address and not old_address.order: old_address.delete() order.modified_by = request.user order.save() formset.save() # Пересчитываем итоговую сумму order.calculate_total() order.save() if order.is_draft(): messages.success(request, f'Черновик #{order.order_number} успешно обновлен!') else: messages.success(request, f'Заказ #{order.order_number} успешно обновлен!') return redirect('orders:order-detail', pk=order.pk) else: messages.error(request, 'Пожалуйста, исправьте ошибки в форме.') else: form = OrderForm(instance=order) formset = OrderItemFormSet(instance=order) context = { 'form': form, 'formset': formset, 'order': order, 'title': f'Редактирование {"черновика" if order.is_draft() else "заказа"} #{order.order_number}', 'button_text': 'Сохранить изменения', 'is_draft': order.is_draft(), } return render(request, 'orders/order_form.html', context) def order_delete(request, pk): """Удаление заказа с подтверждением""" order = get_object_or_404(Order, pk=pk) if request.method == 'POST': order_number = order.order_number order.delete() messages.success(request, f'Заказ #{order_number} успешно удален.') return redirect('orders:order-list') context = { 'order': order, } return render(request, 'orders/order_confirm_delete.html', context) # === AJAX ENDPOINTS === @require_http_methods(["POST"]) @login_required def autosave_draft_order(request, pk): """ AJAX endpoint для автосохранения черновика заказа. Принимает JSON с данными формы и обновляет черновик. Возвращает статус сохранения и время последнего сохранения. Пример запроса: { "customer": 1, "is_delivery": true, "delivery_address": 5, "delivery_date": "2024-01-15", "special_instructions": "Позвонить за час", "items": [ {"product_id": 10, "quantity": "2", "price": "500"}, {"product_kit_id": 5, "quantity": "1", "price": "1500"} ] } Ответ при успехе: { "success": true, "last_saved": "2024-01-10T15:30:45.123456", "order_id": 123, "order_number": "ORD-000123" } """ try: data = json.loads(request.body) # Проверяем существование заказа try: order = Order.objects.get(pk=pk) except Order.DoesNotExist: return JsonResponse({ 'success': False, 'error': 'Заказ не найден' }, status=404) # Используем DraftOrderService для обновления order = DraftOrderService.update_draft( order_id=pk, user=request.user, data=data ) # Обрабатываем позиции заказа, если они переданы if 'items' in data: # Удаляем существующие позиции order.items.all().delete() # Создаем новые позиции for item_data in data['items']: product_id = item_data.get('product_id') product_kit_id = item_data.get('product_kit_id') quantity = item_data.get('quantity') price = item_data.get('price') if product_id: DraftOrderService.add_item_to_draft( order_id=order.pk, product_id=product_id, quantity=quantity, price=price ) elif product_kit_id: DraftOrderService.add_item_to_draft( order_id=order.pk, product_kit_id=product_kit_id, quantity=quantity, price=price ) return JsonResponse({ 'success': True, 'last_saved': order.last_autosave_at.isoformat() if order.last_autosave_at else None, 'order_id': order.pk, 'order_number': order.order_number }) except ValidationError as e: return JsonResponse({ 'success': False, 'error': str(e) }, status=400) except json.JSONDecodeError: return JsonResponse({ 'success': False, 'error': 'Некорректный JSON' }, status=400) except Exception as e: return JsonResponse({ 'success': False, 'error': f'Ошибка сервера: {str(e)}' }, status=500) @require_http_methods(["POST"]) @login_required def create_draft_from_form(request): """ AJAX endpoint для создания черновика заказа из формы создания. Используется для автоматического создания черновика при первом изменении формы. После создания возвращает ID черновика для перенаправления. Пример запроса: { "customer": 1, "is_delivery": true, "delivery_date": "2024-01-15" } Ответ при успехе: { "success": true, "order_id": 123, "order_number": "ORD-000123", "redirect_url": "/orders/123/edit/" } """ try: data = json.loads(request.body) # Получаем обязательное поле - клиента customer_id = data.get('customer') if not customer_id: return JsonResponse({ 'success': False, 'error': 'Необходимо выбрать клиента' }, status=400) from customers.models import Customer try: customer = Customer.objects.get(pk=customer_id) except Customer.DoesNotExist: return JsonResponse({ 'success': False, 'error': 'Клиент не найден' }, status=404) # Создаем черновик через DraftOrderService order = DraftOrderService.create_draft( user=request.user, customer=customer, data=data ) # Обрабатываем позиции заказа, если они переданы if 'items' in data: for item_data in data['items']: product_id = item_data.get('product_id') product_kit_id = item_data.get('product_kit_id') quantity = item_data.get('quantity') price = item_data.get('price') if product_id: DraftOrderService.add_item_to_draft( order_id=order.pk, product_id=product_id, quantity=quantity, price=price ) elif product_kit_id: DraftOrderService.add_item_to_draft( order_id=order.pk, product_kit_id=product_kit_id, quantity=quantity, price=price ) return JsonResponse({ 'success': True, 'order_id': order.pk, 'order_number': order.order_number, 'redirect_url': f'/orders/{order.pk}/edit/' }) except ValidationError as e: return JsonResponse({ 'success': False, 'error': str(e) }, status=400) except json.JSONDecodeError: return JsonResponse({ 'success': False, 'error': 'Некорректный JSON' }, status=400) except Exception as e: return JsonResponse({ 'success': False, 'error': f'Ошибка сервера: {str(e)}' }, status=500) @require_http_methods(["GET"]) @login_required def get_customer_address_history(request): """ AJAX endpoint для получения истории адресов клиента. GET параметры: - customer_id: ID клиента Возвращает JSON со списком адресов из истории заказов клиента. """ try: customer_id = request.GET.get('customer_id') if not customer_id: return JsonResponse({ 'success': False, 'error': 'customer_id не указан' }, status=400) from customers.models import Customer try: customer = Customer.objects.get(pk=customer_id) except Customer.DoesNotExist: return JsonResponse({ 'success': False, 'error': 'Клиент не найден' }, status=404) # Получаем адреса из истории заказов addresses = AddressService.get_customer_address_history(customer) # Форматируем для отправки клиенту addresses_data = [ { 'id': addr.id, 'display': AddressService.format_address_for_display(addr), 'street': addr.street, 'building_number': addr.building_number, 'apartment_number': addr.apartment_number, 'entrance': addr.entrance, 'floor': addr.floor, 'intercom_code': addr.intercom_code, 'recipient_name': addr.recipient_name, 'recipient_phone': addr.recipient_phone, } for addr in addresses ] return JsonResponse({ 'success': True, 'addresses': addresses_data, 'count': len(addresses_data) }) except Exception as e: return JsonResponse({ 'success': False, 'error': f'Ошибка сервера: {str(e)}' }, status=500) # === ВРЕМЕННЫЕ КОМПЛЕКТЫ === # УДАЛЕНО: Логика создания временных комплектов перенесена в products.services.kit_service # Используйте API endpoint: products:api-temporary-kit-create