Реализован функционал создания временных комплектов на витрину из POS
- Добавлен API endpoint для создания временного комплекта из корзины - Реализован endpoint получения списка активных витрин - Создано модальное окно для настройки комплекта и выбора витрины - JavaScript логика: валидация корзины, отправка данных, очистка после успеха - Автоматическая генерация названия комплекта с датой и временем - Агрегация дубликатов товаров в корзине перед созданием - Резервирование компонентов на витрину через ShowcaseManager - Расчёт и отображение итоговой цены комплекта
This commit is contained in:
@@ -3,10 +3,15 @@ from django.shortcuts import render
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.http import JsonResponse
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from products.models import Product, ProductCategory, ProductKit
|
||||
from inventory.models import Showcase, Reservation
|
||||
from django.db import transaction
|
||||
from django.utils import timezone
|
||||
from decimal import Decimal
|
||||
import json
|
||||
|
||||
from products.models import Product, ProductCategory, ProductKit, KitItem
|
||||
from inventory.models import Showcase, Reservation, Warehouse
|
||||
from inventory.services import ShowcaseManager
|
||||
|
||||
|
||||
@login_required
|
||||
def pos_terminal(request):
|
||||
@@ -97,3 +102,156 @@ def showcase_items_api(request):
|
||||
'success': True,
|
||||
'showcases': showcases_list
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def get_showcases_api(request):
|
||||
"""
|
||||
API endpoint для получения списка активных витрин.
|
||||
Используется для выбора витрины при создании временного комплекта.
|
||||
"""
|
||||
showcases = Showcase.objects.filter(is_active=True).select_related('warehouse')
|
||||
|
||||
showcases_data = [{
|
||||
'id': s.id,
|
||||
'name': s.name,
|
||||
'warehouse_name': s.warehouse.name,
|
||||
'warehouse_id': s.warehouse.id
|
||||
} for s in showcases]
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'showcases': showcases_data
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["POST"])
|
||||
def create_temp_kit_to_showcase(request):
|
||||
"""
|
||||
API endpoint для создания временного комплекта из корзины POS
|
||||
и резервирования его на витрину.
|
||||
|
||||
Ожидаемый payload:
|
||||
{
|
||||
"kit_name": "Название комплекта",
|
||||
"showcase_id": 1,
|
||||
"items": [
|
||||
{"product_id": 1, "quantity": 2.0},
|
||||
{"product_id": 3, "quantity": 1.0}
|
||||
],
|
||||
"price_adjustment_type": "none", // optional
|
||||
"price_adjustment_value": 0 // optional
|
||||
}
|
||||
"""
|
||||
try:
|
||||
data = json.loads(request.body)
|
||||
|
||||
kit_name = data.get('kit_name', '').strip()
|
||||
showcase_id = data.get('showcase_id')
|
||||
items = data.get('items', [])
|
||||
price_adjustment_type = data.get('price_adjustment_type', 'none')
|
||||
price_adjustment_value = Decimal(str(data.get('price_adjustment_value', 0)))
|
||||
|
||||
# Валидация
|
||||
if not kit_name:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Необходимо указать название комплекта'
|
||||
}, status=400)
|
||||
|
||||
if not showcase_id:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Необходимо выбрать витрину'
|
||||
}, status=400)
|
||||
|
||||
if not items or len(items) == 0:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Корзина пуста. Добавьте товары для создания комплекта'
|
||||
}, status=400)
|
||||
|
||||
# Проверяем что витрина существует и активна
|
||||
try:
|
||||
showcase = Showcase.objects.select_related('warehouse').get(id=showcase_id, is_active=True)
|
||||
except Showcase.DoesNotExist:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Витрина не найдена или неактивна'
|
||||
}, status=404)
|
||||
|
||||
# Проверяем что все товары из корзины - это Product (не Kit)
|
||||
product_ids = [item['product_id'] for item in items]
|
||||
products = Product.objects.in_bulk(product_ids)
|
||||
|
||||
if len(products) != len(product_ids):
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Некоторые товары не найдены'
|
||||
}, status=400)
|
||||
|
||||
# Агрегируем дубликаты (если один товар добавлен несколько раз)
|
||||
aggregated_items = {}
|
||||
for item in items:
|
||||
product_id = item['product_id']
|
||||
quantity = Decimal(str(item['quantity']))
|
||||
|
||||
if product_id in aggregated_items:
|
||||
aggregated_items[product_id] += quantity
|
||||
else:
|
||||
aggregated_items[product_id] = quantity
|
||||
|
||||
# Создаём временный комплект и резервируем на витрину
|
||||
with transaction.atomic():
|
||||
# 1. Создаём ProductKit (is_temporary=True)
|
||||
kit = ProductKit.objects.create(
|
||||
name=kit_name,
|
||||
is_temporary=True,
|
||||
status='active',
|
||||
price_adjustment_type=price_adjustment_type,
|
||||
price_adjustment_value=price_adjustment_value
|
||||
)
|
||||
|
||||
# 2. Создаём KitItem для каждого товара из корзины
|
||||
for product_id, quantity in aggregated_items.items():
|
||||
KitItem.objects.create(
|
||||
kit=kit,
|
||||
product=products[product_id],
|
||||
quantity=quantity
|
||||
)
|
||||
|
||||
# 3. Пересчитываем цену комплекта
|
||||
kit.recalculate_base_price()
|
||||
|
||||
# 4. Резервируем комплект на витрину
|
||||
result = ShowcaseManager.reserve_kit_to_showcase(
|
||||
product_kit=kit,
|
||||
showcase=showcase,
|
||||
quantity=1
|
||||
)
|
||||
|
||||
if not result['success']:
|
||||
# Откатываем транзакцию через raise
|
||||
raise Exception(result['message'])
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'message': f'Временный комплект "{kit_name}" создан и зарезервирован на витрине "{showcase.name}"',
|
||||
'kit_id': kit.id,
|
||||
'kit_name': kit.name,
|
||||
'kit_price': str(kit.actual_price),
|
||||
'reservations_count': len(result['reservations'])
|
||||
})
|
||||
|
||||
except json.JSONDecodeError:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Неверный формат данных'
|
||||
}, status=400)
|
||||
except Exception as e:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': f'Ошибка при создании комплекта: {str(e)}'
|
||||
}, status=500)
|
||||
|
||||
Reference in New Issue
Block a user