From 6c19c9e0939f85d2e6f3719fb806f5618f79e5c4 Mon Sep 17 00:00:00 2001 From: Andrey Smakotin Date: Mon, 8 Dec 2025 18:56:14 +0300 Subject: [PATCH] POS deferred order feature --- myproject/orders/views.py | 50 ++++++++++++++++++++- myproject/pos/static/pos/js/terminal.js | 58 +++++++++++++++++++++++++ myproject/pos/urls.py | 2 + myproject/pos/views.py | 56 ++++++++++++++++++++++++ 4 files changed, 165 insertions(+), 1 deletion(-) diff --git a/myproject/orders/views.py b/myproject/orders/views.py index fc4bdfb..ce98ffb 100644 --- a/myproject/orders/views.py +++ b/myproject/orders/views.py @@ -107,6 +107,24 @@ def order_create(request): initial_data = {} preselected_customer = None customer_id = request.GET.get('customer') + + # Проверяем, есть ли черновик из POS + draft_token = request.GET.get('draft') + draft_items = [] + + 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: + # Загружаем клиента из черновика + customer_id = draft_data.get('customer_id') + draft_items = draft_data.get('items', []) + + # Удаляем черновик из кэша (одноразовый токен) + cache.delete(cache_key) + if customer_id: try: from customers.models import Customer @@ -116,7 +134,37 @@ def order_create(request): pass form = OrderForm(initial=initial_data) - formset = OrderItemFormSet() + + # Создаем formset с предзаполненными товарами из черновика + if draft_items: + from products.models import Product, ProductKit + initial_formset_data = [] + + for item in draft_items: + item_data = { + 'quantity': item['quantity'], + 'price': item['price'], + 'is_custom_price': False, + } + + if item['type'] == 'product': + try: + product = Product.objects.get(id=item['id']) + item_data['product'] = product.id + initial_formset_data.append(item_data) + except Product.DoesNotExist: + pass + elif item['type'] in ['kit', 'showcase_kit']: + try: + kit = ProductKit.objects.get(id=item['id']) + item_data['product_kit'] = kit.id + initial_formset_data.append(item_data) + except ProductKit.DoesNotExist: + pass + + formset = OrderItemFormSet(initial=initial_formset_data) + else: + formset = OrderItemFormSet() context = { 'form': form, diff --git a/myproject/pos/static/pos/js/terminal.js b/myproject/pos/static/pos/js/terminal.js index d86c63b..1389ce8 100644 --- a/myproject/pos/static/pos/js/terminal.js +++ b/myproject/pos/static/pos/js/terminal.js @@ -1984,3 +1984,61 @@ setupInfiniteScroll(); // Установка infinite scroll // Установить фокус на строку поиска document.getElementById('searchInput').focus(); + +// ===== ОТЛОЖЕННЫЙ ЗАКАЗ ===== + +/** + * Открывает страницу создания заказа с предзаполненными товарами и клиентом + */ +async function createDeferredOrder() { + // Проверяем, что корзина не пуста + if (cart.size === 0) { + alert('Корзина пуста! Добавьте товары в корзину.'); + return; + } + + try { + // Собираем данные для черновика + const items = Array.from(cart.values()).map(item => ({ + type: item.type, + id: item.id, + quantity: item.qty, + price: item.price + })); + + const customer = selectedCustomer || SYSTEM_CUSTOMER; + + const draftData = { + customer_id: customer.id, + items: items + }; + + // Отправляем на сервер + const response = await fetch('/pos/api/create-draft/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': getCsrfToken() + }, + body: JSON.stringify(draftData) + }); + + const result = await response.json(); + + if (result.success) { + // Открываем в новой вкладке + window.open(`/orders/create/?draft=${result.token}`, '_blank'); + } else { + alert(`Ошибка: ${result.error}`); + } + } catch (error) { + console.error('Ошибка при создании отложенного заказа:', error); + alert('Произошла ошибка при создании черновика заказа'); + } +} + +// Обработчик кнопки "ОТЛОЖЕННЫЙ заказ" +const scheduleLaterBtn = document.getElementById('scheduleLater'); +if (scheduleLaterBtn) { + scheduleLaterBtn.addEventListener('click', createDeferredOrder); +} diff --git a/myproject/pos/urls.py b/myproject/pos/urls.py index b673d79..638adf7 100644 --- a/myproject/pos/urls.py +++ b/myproject/pos/urls.py @@ -21,6 +21,8 @@ urlpatterns = [ path('api/showcase-kits/', views.get_showcase_kits_api, name='showcase-kits-api'), # Добавить витринный комплект в корзину с блокировкой [POST] path('api/showcase-kits//add-to-cart/', views.add_showcase_kit_to_cart, name='add-showcase-kit-to-cart'), + # Сохранить черновик заказа для передачи в orders/create/ [POST] + path('api/create-draft/', views.create_order_draft, name='create-order-draft'), # Снять блокировку витринного комплекта при удалении из корзины [POST] path('api/showcase-kits//remove-from-cart/', views.remove_showcase_kit_from_cart, name='remove-showcase-kit-from-cart'), # Получить детали комплекта для редактирования [GET] diff --git a/myproject/pos/views.py b/myproject/pos/views.py index dfe4536..f06523d 100644 --- a/myproject/pos/views.py +++ b/myproject/pos/views.py @@ -1416,3 +1416,59 @@ def pos_checkout(request): except Exception as e: logger.error(f'Ошибка при проведении продажи POS: {str(e)}', exc_info=True) return JsonResponse({'success': False, 'error': f'Ошибка: {str(e)}'}, status=500) + + +@login_required +@require_http_methods(["POST"]) +def create_order_draft(request): + """ + Создает черновик заказа из корзины POS и сохраняет в Redis. + Возвращает токен для передачи в orders/create/. + + Payload (JSON): + { + "customer_id": int, + "items": [ + {"type": "product"|"kit"|"showcase_kit", "id": int, "quantity": float, "price": float}, + ... + ] + } + + Response: + { + "success": true, + "token": "abc123..." + } + """ + from django.core.cache import cache + import secrets + + try: + data = json.loads(request.body) + customer_id = data.get('customer_id') + items = data.get('items', []) + + if not items: + return JsonResponse({'success': False, 'error': 'Корзина пуста'}, status=400) + + # Генерируем уникальный токен + token = secrets.token_urlsafe(16) + + # Сохраняем в Redis с TTL 1 час + cache_key = f'pos_draft:{token}' + draft_data = { + 'customer_id': customer_id, + 'items': items, + } + cache.set(cache_key, draft_data, timeout=3600) # 1 час + + return JsonResponse({ + 'success': True, + 'token': token + }) + + except json.JSONDecodeError: + return JsonResponse({'success': False, 'error': 'Неверный формат JSON'}, status=400) + except Exception as e: + logger.error(f'Ошибка при создании черновика заказа: {str(e)}', exc_info=True) + return JsonResponse({'success': False, 'error': f'Ошибка: {str(e)}'}, status=500)