Удалён kostyl с автозаполнением delivery_date для черновиков
- orders/views.py: убрано auto_fill_draft_date из order_create и order_update - Черновики теперь могут сохраняться с NULL датой доставки - Валидация на уровне модели обеспечивает корректность данных
This commit is contained in:
@@ -118,6 +118,59 @@ def order_create(request):
|
||||
formset.instance = order
|
||||
formset.save()
|
||||
|
||||
# === Обработка витринных комплектов из POS ===
|
||||
# Если заказ создан из POS с showcase_kit, обрабатываем ShowcaseItem
|
||||
draft_token = request.GET.get('draft')
|
||||
if draft_token:
|
||||
from django.core.cache import cache
|
||||
cache_key = f'pos_draft:{draft_token}'
|
||||
draft_data = cache.get(cache_key)
|
||||
|
||||
if draft_data and draft_data.get('items'):
|
||||
from inventory.models import ShowcaseItem
|
||||
from products.models import ProductKit
|
||||
|
||||
# Проверяем статус заказа для выбора стратегии
|
||||
is_final_positive = order.status and order.status.is_positive_end
|
||||
|
||||
for draft_item in draft_data['items']:
|
||||
if draft_item.get('type') == 'showcase_kit':
|
||||
showcase_item_ids = draft_item.get('showcase_item_ids', [])
|
||||
kit_id = draft_item.get('id')
|
||||
|
||||
if not showcase_item_ids or not kit_id:
|
||||
continue
|
||||
|
||||
# Находим соответствующий OrderItem
|
||||
kit = ProductKit.objects.get(id=kit_id)
|
||||
order_item = order.items.filter(
|
||||
product_kit=kit,
|
||||
is_from_showcase=True
|
||||
).first()
|
||||
|
||||
if not order_item:
|
||||
continue
|
||||
|
||||
# Загружаем ShowcaseItem
|
||||
showcase_items = list(ShowcaseItem.objects.filter(
|
||||
id__in=showcase_item_ids
|
||||
))
|
||||
|
||||
if not showcase_items:
|
||||
continue
|
||||
|
||||
# Выбираем стратегию в зависимости от статуса заказа
|
||||
if is_final_positive:
|
||||
# Прямая продажа (заказ сразу в completed - редкий случай)
|
||||
from inventory.services.showcase_manager import ShowcaseManager
|
||||
result = ShowcaseManager.sell_showcase_items(showcase_items, order_item)
|
||||
if not result['success']:
|
||||
raise ValidationError(result['message'])
|
||||
else:
|
||||
# Резервирование под отложенный заказ
|
||||
for showcase_item in showcase_items:
|
||||
showcase_item.reserve_for_order(order_item)
|
||||
|
||||
# Проверяем, является ли заказ черновиком
|
||||
is_draft = order.status and order.status.code == 'draft'
|
||||
|
||||
@@ -141,17 +194,12 @@ def order_create(request):
|
||||
# Для черновиков создаем Delivery без обязательных проверок, чтобы сохранить адрес
|
||||
if is_draft:
|
||||
# Для черновиков создаем Delivery, если есть хотя бы адрес или данные доставки
|
||||
if address or delivery_type or pickup_warehouse:
|
||||
# Для черновиков используем значения по умолчанию, если не указаны
|
||||
from django.utils import timezone
|
||||
draft_delivery_type = delivery_type or Delivery.DELIVERY_TYPE_COURIER
|
||||
draft_delivery_date = delivery_date or timezone.now().date()
|
||||
|
||||
if address or delivery_type or pickup_warehouse or delivery_date:
|
||||
Delivery.objects.update_or_create(
|
||||
order=order,
|
||||
defaults={
|
||||
'delivery_type': draft_delivery_type,
|
||||
'delivery_date': draft_delivery_date,
|
||||
'delivery_type': delivery_type or Delivery.DELIVERY_TYPE_COURIER,
|
||||
'delivery_date': delivery_date, # Может быть None для черновиков
|
||||
'time_from': time_from,
|
||||
'time_to': time_to,
|
||||
'address': address,
|
||||
@@ -316,17 +364,12 @@ def order_update(request, order_number):
|
||||
# Для черновиков создаем Delivery без обязательных проверок, чтобы сохранить адрес
|
||||
if is_draft:
|
||||
# Для черновиков создаем Delivery, если есть хотя бы адрес или данные доставки
|
||||
if address or delivery_type or pickup_warehouse:
|
||||
# Для черновиков используем значения по умолчанию, если не указаны
|
||||
from django.utils import timezone
|
||||
draft_delivery_type = delivery_type or Delivery.DELIVERY_TYPE_COURIER
|
||||
draft_delivery_date = delivery_date or timezone.now().date()
|
||||
|
||||
if address or delivery_type or pickup_warehouse or delivery_date:
|
||||
Delivery.objects.update_or_create(
|
||||
order=order,
|
||||
defaults={
|
||||
'delivery_type': draft_delivery_type,
|
||||
'delivery_date': draft_delivery_date,
|
||||
'delivery_type': delivery_type or Delivery.DELIVERY_TYPE_COURIER,
|
||||
'delivery_date': delivery_date, # Может быть None для черновиков
|
||||
'time_from': time_from,
|
||||
'time_to': time_to,
|
||||
'address': address,
|
||||
@@ -913,3 +956,155 @@ def set_order_status(request, order_number):
|
||||
return JsonResponse({'success': False, 'error': str(e)}, status=400)
|
||||
except Exception as e:
|
||||
return JsonResponse({'success': False, 'error': f'Server error: {str(e)}'}, status=500)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["POST"])
|
||||
def create_order_from_pos(request):
|
||||
"""
|
||||
Создаёт отложенный заказ (черновик) из POS.
|
||||
Сразу создаёт Order со статусом 'draft' и резервирует ShowcaseItem.
|
||||
|
||||
Payload (JSON):
|
||||
{
|
||||
"customer_id": int,
|
||||
"items": [
|
||||
{
|
||||
"type": "product"|"kit"|"showcase_kit",
|
||||
"id": int,
|
||||
"quantity": float,
|
||||
"price": float,
|
||||
"sales_unit_id": int, // для product
|
||||
"showcase_item_ids": [int, ...] // для showcase_kit
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"success": true,
|
||||
"order_number": int,
|
||||
"message": "Заказ #123 создан (черновик)"
|
||||
}
|
||||
"""
|
||||
from .services.order_status_service import OrderStatusService
|
||||
from customers.models import Customer
|
||||
from products.models import Product, ProductKit
|
||||
from inventory.models import ShowcaseItem
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
data = json.loads(request.body)
|
||||
customer_id = data.get('customer_id')
|
||||
items = data.get('items', [])
|
||||
|
||||
if not customer_id:
|
||||
return JsonResponse({'success': False, 'error': 'Не указан клиент'}, status=400)
|
||||
|
||||
if not items:
|
||||
return JsonResponse({'success': False, 'error': 'Корзина пуста'}, status=400)
|
||||
|
||||
with transaction.atomic():
|
||||
# 1. Получаем клиента
|
||||
try:
|
||||
customer = Customer.objects.get(id=customer_id)
|
||||
except Customer.DoesNotExist:
|
||||
return JsonResponse({'success': False, 'error': 'Клиент не найден'}, status=404)
|
||||
|
||||
# 2. Получаем статус 'Черновик'
|
||||
draft_status = OrderStatusService.get_draft_status()
|
||||
if not draft_status:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Статус "Черновик" не найден. Выполните миграции.'
|
||||
}, status=500)
|
||||
|
||||
# 3. Создаём заказ со статусом 'draft'
|
||||
order = Order.objects.create(
|
||||
customer=customer,
|
||||
status=draft_status,
|
||||
modified_by=request.user
|
||||
)
|
||||
|
||||
# 4. Создаём OrderItem и резервируем ShowcaseItem
|
||||
for item_data in items:
|
||||
item_type = item_data.get('type')
|
||||
item_id = item_data.get('id')
|
||||
quantity = item_data.get('quantity', 1)
|
||||
price = item_data.get('price', 0)
|
||||
|
||||
if item_type == 'product':
|
||||
# Обычный товар
|
||||
product = Product.objects.get(id=item_id)
|
||||
sales_unit_id = item_data.get('sales_unit_id')
|
||||
|
||||
OrderItem.objects.create(
|
||||
order=order,
|
||||
product=product,
|
||||
sales_unit_id=sales_unit_id,
|
||||
quantity=quantity,
|
||||
price=price,
|
||||
is_custom_price=False
|
||||
)
|
||||
|
||||
elif item_type == 'kit':
|
||||
# Обычный комплект
|
||||
kit = ProductKit.objects.get(id=item_id)
|
||||
|
||||
OrderItem.objects.create(
|
||||
order=order,
|
||||
product_kit=kit,
|
||||
quantity=quantity,
|
||||
price=price,
|
||||
is_custom_price=False
|
||||
)
|
||||
|
||||
elif item_type == 'showcase_kit':
|
||||
# Витринный комплект - сразу резервируем ShowcaseItem
|
||||
kit = ProductKit.objects.get(id=item_id)
|
||||
showcase_item_ids = item_data.get('showcase_item_ids', [])
|
||||
|
||||
if not showcase_item_ids:
|
||||
continue
|
||||
|
||||
# Создаём OrderItem с флагом is_from_showcase
|
||||
order_item = OrderItem.objects.create(
|
||||
order=order,
|
||||
product_kit=kit,
|
||||
quantity=len(showcase_item_ids),
|
||||
price=price,
|
||||
is_custom_price=False,
|
||||
is_from_showcase=True
|
||||
)
|
||||
|
||||
# Резервируем ShowcaseItem: in_cart → reserved
|
||||
showcase_items = ShowcaseItem.objects.filter(
|
||||
id__in=showcase_item_ids,
|
||||
status='in_cart',
|
||||
locked_by_user=request.user
|
||||
)
|
||||
|
||||
for showcase_item in showcase_items:
|
||||
showcase_item.reserve_for_order(order_item)
|
||||
|
||||
# 5. Пересчитываем стоимость заказа
|
||||
order.calculate_total()
|
||||
order.update_payment_status()
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'order_number': order.order_number,
|
||||
'message': f'Заказ #{order.order_number} создан (черновик)'
|
||||
})
|
||||
|
||||
except json.JSONDecodeError:
|
||||
return JsonResponse({'success': False, 'error': 'Неверный формат JSON'}, status=400)
|
||||
except Product.DoesNotExist:
|
||||
return JsonResponse({'success': False, 'error': 'Товар не найден'}, status=404)
|
||||
except ProductKit.DoesNotExist:
|
||||
return JsonResponse({'success': False, 'error': 'Комплект не найден'}, status=404)
|
||||
except Exception as e:
|
||||
logger.error(f'Ошибка при создании отложенного заказа из POS: {str(e)}', exc_info=True)
|
||||
return JsonResponse({'success': False, 'error': f'Ошибка: {str(e)}'}, status=500)
|
||||
|
||||
Reference in New Issue
Block a user