Добавлена поддержка черновиков заказов (Этап 2/3): AJAX endpoints и views

Добавлены AJAX endpoints:
- autosave_draft_order: endpoint для автосохранения черновиков

Модифицированы views:
- order_create: поддержка создания черновиков через кнопку 'save_as_draft'
- order_update: поддержка обновления и финализации черновиков через DraftOrderService

Добавлены URL:
- /orders/<pk>/autosave/ для автосохранения черновиков

Следующий этап: JavaScript модуль автосохранения и UI

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-08 21:20:29 +03:00
parent dc8604732f
commit 961a5d52da
2 changed files with 152 additions and 9 deletions

View File

@@ -11,6 +11,9 @@ urlpatterns = [
path('<int:pk>/edit/', views.order_update, name='order-update'),
path('<int:pk>/delete/', views.order_delete, name='order-delete'),
# AJAX endpoints
path('<int:pk>/autosave/', views.autosave_draft_order, name='order-autosave'),
# Временные комплекты
path('temporary-kits/create/', views.create_temporary_kit, name='temporary-kit-create'),
]

View File

@@ -5,10 +5,14 @@ from django.core.paginator import Paginator
from django.http import JsonResponse
from django.db import transaction
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
from .forms import OrderForm, OrderItemFormSet
from .filters import OrderFilter
from .services import DraftOrderService
from products.models import ProductKit, KitItem, Product
import json
def order_list(request):
@@ -64,6 +68,12 @@ def order_create(request):
if form.is_valid() and formset.is_valid():
order = form.save(commit=False)
# Если нажата кнопка "Сохранить как черновик", создаем черновик
if 'save_as_draft' in request.POST:
order.status = 'draft'
order.modified_by = request.user
order.save()
# Сохраняем позиции заказа
@@ -74,6 +84,9 @@ def order_create(request):
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:
@@ -101,13 +114,30 @@ def order_update(request, pk):
formset = OrderItemFormSet(request.POST, instance=order)
if form.is_valid() and formset.is_valid():
order = form.save()
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:
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:
@@ -120,8 +150,9 @@ def order_update(request, pk):
'form': form,
'formset': formset,
'order': order,
'title': f'Редактирование заказа #{order.order_number}',
'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)
@@ -144,6 +175,115 @@ def order_delete(request, pk):
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)
# Проверяем, что это черновик
if not order.is_draft():
return JsonResponse({
'success': False,
'error': 'Можно автосохранять только черновики'
}, status=400)
# Используем 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"])