POS deferred order feature
This commit is contained in:
@@ -107,6 +107,24 @@ def order_create(request):
|
|||||||
initial_data = {}
|
initial_data = {}
|
||||||
preselected_customer = None
|
preselected_customer = None
|
||||||
customer_id = request.GET.get('customer')
|
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:
|
if customer_id:
|
||||||
try:
|
try:
|
||||||
from customers.models import Customer
|
from customers.models import Customer
|
||||||
@@ -116,7 +134,37 @@ def order_create(request):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
form = OrderForm(initial=initial_data)
|
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 = {
|
context = {
|
||||||
'form': form,
|
'form': form,
|
||||||
|
|||||||
@@ -1984,3 +1984,61 @@ setupInfiniteScroll(); // Установка infinite scroll
|
|||||||
|
|
||||||
// Установить фокус на строку поиска
|
// Установить фокус на строку поиска
|
||||||
document.getElementById('searchInput').focus();
|
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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ urlpatterns = [
|
|||||||
path('api/showcase-kits/', views.get_showcase_kits_api, name='showcase-kits-api'),
|
path('api/showcase-kits/', views.get_showcase_kits_api, name='showcase-kits-api'),
|
||||||
# Добавить витринный комплект в корзину с блокировкой [POST]
|
# Добавить витринный комплект в корзину с блокировкой [POST]
|
||||||
path('api/showcase-kits/<int:kit_id>/add-to-cart/', views.add_showcase_kit_to_cart, name='add-showcase-kit-to-cart'),
|
path('api/showcase-kits/<int:kit_id>/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]
|
# Снять блокировку витринного комплекта при удалении из корзины [POST]
|
||||||
path('api/showcase-kits/<int:kit_id>/remove-from-cart/', views.remove_showcase_kit_from_cart, name='remove-showcase-kit-from-cart'),
|
path('api/showcase-kits/<int:kit_id>/remove-from-cart/', views.remove_showcase_kit_from_cart, name='remove-showcase-kit-from-cart'),
|
||||||
# Получить детали комплекта для редактирования [GET]
|
# Получить детали комплекта для редактирования [GET]
|
||||||
|
|||||||
@@ -1416,3 +1416,59 @@ def pos_checkout(request):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f'Ошибка при проведении продажи POS: {str(e)}', exc_info=True)
|
logger.error(f'Ошибка при проведении продажи POS: {str(e)}', exc_info=True)
|
||||||
return JsonResponse({'success': False, 'error': f'Ошибка: {str(e)}'}, status=500)
|
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)
|
||||||
|
|||||||
Reference in New Issue
Block a user