Files
octopus/myproject/orders/views.py
Andrey Smakotin ddbb4f963b Исправлена отображение полей адреса и стили формы заказа
## Основные изменения:

### 1. Исправлена логика выбора режима адреса
- Переместил функцию initAddressModeToggle() из jQuery блока в отдельную функцию
- Теперь инициализация адреса работает независимо от jQuery
- Добавлены подробные логи в консоль для отладки ([ADDRESS MODE] префикс)

### 2. Добавлены CSS классы для управления видимостью
- address-history-mode: display: none !important (по умолчанию скрыт)
- address-new-mode: display: none !important (по умолчанию скрыт)
- .visible класс переводит элементы на display: block !important
- Использование classList.add/remove вместо inline styles

### 3. Исправлены стили полей формы (OrderForm)
- Добавлена явная обработка для Select полей - получают form-select
- Поле "Статус" и другие Select теперь имеют правильные стили Bootstrap
- Разделена логика для RadioSelect, Select и остальных полей

### 4. Улучшена отладка
- Добавлены console.log сообщения на каждом этапе инициализации
- Префикс [ADDRESS MODE] помогает отличить логи системы адреса от других

## Технические детали:

- Address сервис использует метод format_address_for_display() для красивого вывода
- AJAX endpoint get_customer_address_history() загружает адреса клиента
- Три режима адреса: history (из истории), new (новый адрес), empty (без адреса)
- Режим empty выбирается по умолчанию

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 02:49:25 +03:00

484 lines
18 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- 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)
# Проверяем, что это черновик
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"])
@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