Simplify order creation and editing - remove autosave

- Removed autosave.js (665 lines) and draft-creator.js (441 lines)
- Removed draft_service.py (~500 lines) and DraftOrderService
- Removed AJAX endpoints: autosave and create-draft
- Updated order_create() to add is_create_page flag
- Updated order_update() to finalize drafts without DraftOrderService
- Added get_new_status() method to OrderStatusService
- Updated order_form.html:
  - Removed old JS includes
  - Added beforeunload warning for unsaved data
  - Updated buttons: separate buttons for create/draft/finalize
- Total code reduction: ~1600 lines (92% removed)

New workflow:
- /orders/create/ - user fills form, chooses button
- /orders/<id>/edit/ - simple editing without autosave
- beforeunload warning when leaving page (except on submit)
This commit is contained in:
2025-11-28 23:29:19 +03:00
parent f911a57640
commit 9a44c98e6e
8 changed files with 110 additions and 1946 deletions

View File

@@ -11,7 +11,6 @@ from decimal import Decimal
from .models import Order, OrderItem, Address, OrderStatus
from .forms import OrderForm, OrderItemFormSet, OrderStatusForm, PaymentFormSet
from .filters import OrderFilter
from .services import DraftOrderService
from .services.address_service import AddressService
import json
@@ -80,11 +79,15 @@ def order_create(request):
address.save()
order.delivery_address = address
# Если нажата кнопка "Сохранить как черновик", создаем черновик
# Проверяем какая кнопка нажата
if 'save_as_draft' in request.POST:
# Кнопка "Сохранить как черновик"
from .services.order_status_service import OrderStatusService
order.status = OrderStatusService.get_draft_status()
order.modified_by = request.user
else:
# Кнопка "Создать заказ" - статус из формы или NULL
order.modified_by = request.user
order.save()
@@ -131,6 +134,7 @@ def order_create(request):
'preselected_customer': preselected_customer,
'title': 'Создание заказа',
'button_text': 'Создать заказ',
'is_create_page': True,
}
return render(request, 'orders/order_form.html', context)
@@ -150,15 +154,29 @@ def order_update(request, order_number):
# Если черновик финализируется
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', order_number=order.order_number)
except ValidationError as e:
messages.error(request, f'Ошибка финализации: {str(e)}')
form = OrderForm(instance=order)
formset = OrderItemFormSet(instance=order)
payment_formset = PaymentFormSet(instance=order)
from .services.order_status_service import OrderStatusService
# Переводим в статус "Новый"
order.status = OrderStatusService.get_new_status()
order.modified_by = request.user
# Обрабатываем адрес доставки
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
order.save()
formset.save()
payment_formset.save()
# Пересчитываем итоговую сумму
order.calculate_total()
order.save()
messages.success(request, f'Черновик #{order.order_number} успешно завершен и переведен в статус "Новый"!')
return redirect('orders:order-detail', order_number=order.order_number)
else:
# Обрабатываем адрес доставки
if order.is_delivery:
@@ -255,250 +273,6 @@ def order_delete(request, order_number):
# === AJAX ENDPOINTS ===
@require_http_methods(["POST"])
@login_required
def autosave_draft_order(request, order_number):
"""
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(order_number=order_number)
except Order.DoesNotExist:
return JsonResponse({
'success': False,
'error': 'Заказ не найден'
}, status=404)
# Обновляем основные поля заказа из DraftOrderService (БЕЗ товаров)
# Товары обрабатываем отдельно ниже
order_fields_only = {k: v for k, v in data.items() if k not in ['items', 'payments']}
order = DraftOrderService.update_draft(
order_id=order.pk,
user=request.user,
data=order_fields_only
)
# Обрабатываем позиции заказа, если они переданы
if 'items' in data:
from decimal import Decimal, InvalidOperation
# Получаем ID товаров, которые нужно удалить
deleted_item_ids = data.get('deleted_item_ids', [])
if deleted_item_ids:
order.items.filter(pk__in=deleted_item_ids).delete()
# Обрабатываем каждый товар
for item_data in data['items']:
item_id = item_data.get('id') # ID существующего товара (если есть)
product_id = item_data.get('product_id')
product_kit_id = item_data.get('product_kit_id')
quantity = item_data.get('quantity')
price_raw = item_data.get('price')
# Преобразуем цену
try:
price = Decimal(str(price_raw).replace(',', '.')) if price_raw else None
except (ValueError, InvalidOperation):
price = None
# Если есть ID - обновляем существующий товар
if item_id:
try:
item = order.items.get(pk=item_id)
# Обновляем поля
if product_id:
from products.models import Product
item.product = Product.objects.get(pk=product_id)
item.product_kit = None
elif product_kit_id:
from products.models import ProductKit
item.product_kit = ProductKit.objects.get(pk=product_kit_id)
item.product = None
if quantity:
item.quantity = quantity
if price is not None:
item.price = price
item.save()
except OrderItem.DoesNotExist:
# Если товар не найден, создаем новый
item_id = None
# Если нет ID - создаем новый товар
if not item_id:
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
)
# НЕ ОБРАБАТЫВАЕМ ПЛАТЕЖИ В АВТОСОХРАНЕНИИ
# Платежи обрабатываются только при ручном сохранении формы
# Пересчитываем итоговую сумму заказа и обновляем статус оплаты
order.calculate_total()
order.update_payment_status()
order.save()
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.order_number}/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):