Удалён kostyl с автозаполнением delivery_date для черновиков

- orders/views.py: убрано auto_fill_draft_date из order_create и order_update
- Черновики теперь могут сохраняться с NULL датой доставки
- Валидация на уровне модели обеспечивает корректность данных
This commit is contained in:
2026-01-05 01:36:09 +03:00
parent e8d232158c
commit a32c9915d2

View File

@@ -118,6 +118,59 @@ def order_create(request):
formset.instance = order formset.instance = order
formset.save() 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' is_draft = order.status and order.status.code == 'draft'
@@ -141,17 +194,12 @@ def order_create(request):
# Для черновиков создаем Delivery без обязательных проверок, чтобы сохранить адрес # Для черновиков создаем Delivery без обязательных проверок, чтобы сохранить адрес
if is_draft: if is_draft:
# Для черновиков создаем Delivery, если есть хотя бы адрес или данные доставки # Для черновиков создаем Delivery, если есть хотя бы адрес или данные доставки
if address or delivery_type or pickup_warehouse: if address or delivery_type or pickup_warehouse or delivery_date:
# Для черновиков используем значения по умолчанию, если не указаны
from django.utils import timezone
draft_delivery_type = delivery_type or Delivery.DELIVERY_TYPE_COURIER
draft_delivery_date = delivery_date or timezone.now().date()
Delivery.objects.update_or_create( Delivery.objects.update_or_create(
order=order, order=order,
defaults={ defaults={
'delivery_type': draft_delivery_type, 'delivery_type': delivery_type or Delivery.DELIVERY_TYPE_COURIER,
'delivery_date': draft_delivery_date, 'delivery_date': delivery_date, # Может быть None для черновиков
'time_from': time_from, 'time_from': time_from,
'time_to': time_to, 'time_to': time_to,
'address': address, 'address': address,
@@ -316,17 +364,12 @@ def order_update(request, order_number):
# Для черновиков создаем Delivery без обязательных проверок, чтобы сохранить адрес # Для черновиков создаем Delivery без обязательных проверок, чтобы сохранить адрес
if is_draft: if is_draft:
# Для черновиков создаем Delivery, если есть хотя бы адрес или данные доставки # Для черновиков создаем Delivery, если есть хотя бы адрес или данные доставки
if address or delivery_type or pickup_warehouse: if address or delivery_type or pickup_warehouse or delivery_date:
# Для черновиков используем значения по умолчанию, если не указаны
from django.utils import timezone
draft_delivery_type = delivery_type or Delivery.DELIVERY_TYPE_COURIER
draft_delivery_date = delivery_date or timezone.now().date()
Delivery.objects.update_or_create( Delivery.objects.update_or_create(
order=order, order=order,
defaults={ defaults={
'delivery_type': draft_delivery_type, 'delivery_type': delivery_type or Delivery.DELIVERY_TYPE_COURIER,
'delivery_date': draft_delivery_date, 'delivery_date': delivery_date, # Может быть None для черновиков
'time_from': time_from, 'time_from': time_from,
'time_to': time_to, 'time_to': time_to,
'address': address, 'address': address,
@@ -913,3 +956,155 @@ def set_order_status(request, order_number):
return JsonResponse({'success': False, 'error': str(e)}, status=400) return JsonResponse({'success': False, 'error': str(e)}, status=400)
except Exception as e: except Exception as e:
return JsonResponse({'success': False, 'error': f'Server error: {str(e)}'}, status=500) 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)