diff --git a/myproject/pos/static/pos/js/terminal.js b/myproject/pos/static/pos/js/terminal.js index 8c67a24..322e2c4 100644 --- a/myproject/pos/static/pos/js/terminal.js +++ b/myproject/pos/static/pos/js/terminal.js @@ -826,6 +826,69 @@ document.getElementById('customerSelectBtn').addEventListener('click', () => { alert('Функция выбора клиента будет реализована позже'); }); +// Смена склада +const changeWarehouseBtn = document.getElementById('changeWarehouseBtn'); +if (changeWarehouseBtn) { + changeWarehouseBtn.addEventListener('click', () => { + const modal = new bootstrap.Modal(document.getElementById('selectWarehouseModal')); + modal.show(); + }); +} + +// Обработка выбора склада из списка +document.addEventListener('click', async (e) => { + const warehouseItem = e.target.closest('.warehouse-item'); + if (!warehouseItem) return; + + const warehouseId = warehouseItem.dataset.warehouseId; + const warehouseName = warehouseItem.dataset.warehouseName; + + // Проверяем, есть ли товары в корзине + if (cart.size > 0) { + const confirmed = confirm(`При смене склада корзина будет очищена.\n\nПереключиться на склад "${warehouseName}"?`); + if (!confirmed) return; + } + + try { + // Отправляем запрос на смену склада + const response = await fetch(`/pos/api/set-warehouse/${warehouseId}/`, { + method: 'POST', + headers: { + 'X-CSRFToken': getCsrfToken() + } + }); + + const data = await response.json(); + + if (data.success) { + // Перезагружаем страницу для обновления данных + location.reload(); + } else { + alert(`Ошибка: ${data.error}`); + } + } catch (error) { + console.error('Ошибка при смене склада:', error); + alert('Произошла ошибка при смене склада'); + } +}); + +// Вспомогательная функция для получения CSRF токена +function getCsrfToken() { + const name = 'csrftoken'; + let cookieValue = null; + if (document.cookie && document.cookie !== '') { + const cookies = document.cookie.split(';'); + for (let i = 0; i < cookies.length; i++) { + const cookie = cookies[i].trim(); + if (cookie.substring(0, name.length + 1) === (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; +} + // Инициализация renderCategories(); renderProducts(); diff --git a/myproject/pos/templates/pos/terminal.html b/myproject/pos/templates/pos/terminal.html index 1b1efe6..11d5798 100644 --- a/myproject/pos/templates/pos/terminal.html +++ b/myproject/pos/templates/pos/terminal.html @@ -32,6 +32,21 @@
+ + {% if current_warehouse %} +
+
+
+ Склад: + {{ current_warehouse.name }} +
+ +
+
+ {% endif %} +
@@ -231,6 +246,42 @@
+ + + {% endblock %} {% block extra_js %} diff --git a/myproject/pos/urls.py b/myproject/pos/urls.py index fa027bb..ee43333 100644 --- a/myproject/pos/urls.py +++ b/myproject/pos/urls.py @@ -6,6 +6,7 @@ app_name = 'pos' urlpatterns = [ path('', views.pos_terminal, name='terminal'), + path('api/set-warehouse//', views.set_warehouse, name='set-warehouse'), path('api/showcase-items/', views.showcase_items_api, name='showcase-items-api'), path('api/get-showcases/', views.get_showcases_api, name='get-showcases-api'), path('api/showcase-kits/', views.get_showcase_kits_api, name='showcase-kits-api'), diff --git a/myproject/pos/views.py b/myproject/pos/views.py index 1f951d0..2da73cc 100644 --- a/myproject/pos/views.py +++ b/myproject/pos/views.py @@ -14,6 +14,38 @@ from inventory.models import Showcase, Reservation, Warehouse from inventory.services import ShowcaseManager +def get_pos_warehouse(request): + """ + Получить текущий склад для POS из сессии или выбрать дефолтный. + Логика выбора: + 1. Если в сессии есть pos_warehouse_id - используем его + 2. Иначе берем склад с is_default=True + 3. Если нет is_default - берем первый активный + 4. Если нет активных складов - None + """ + warehouse_id = request.session.get('pos_warehouse_id') + + if warehouse_id: + try: + return Warehouse.objects.get(id=warehouse_id, is_active=True) + except Warehouse.DoesNotExist: + # Склад был удален или деактивирован - сбрасываем сессию + request.session.pop('pos_warehouse_id', None) + + # Ищем склад по умолчанию + warehouse = Warehouse.objects.filter(is_active=True, is_default=True).first() + + if not warehouse: + # Берем любой первый активный + warehouse = Warehouse.objects.filter(is_active=True).first() + + # Сохраняем в сессию для следующих запросов + if warehouse: + request.session['pos_warehouse_id'] = warehouse.id + + return warehouse + + def get_showcase_kits_for_pos(): """ Получает витринные комплекты для отображения в POS. @@ -124,9 +156,27 @@ def pos_terminal(request): Tablet-friendly POS screen prototype. Shows categories and all items (products + kits) for quick tap-to-add. Оптимизировано: убрана стартовая загрузка витрин, только thumbnail фото. + Работает только с одним выбранным складом. """ from products.models import ProductPhoto, ProductKitPhoto + # Получаем текущий склад для POS + current_warehouse = get_pos_warehouse(request) + + if not current_warehouse: + # Нет активных складов - показываем ошибку + from django.contrib import messages + messages.error(request, 'Нет активных складов. Обратитесь к администратору.') + context = { + 'categories_json': json.dumps([]), + 'items_json': json.dumps([]), + 'showcase_kits_json': json.dumps([]), + 'current_warehouse': None, + 'warehouses': [], + 'title': 'POS Terminal', + } + return render(request, 'pos/terminal.html', context) + categories_qs = ProductCategory.objects.filter(is_active=True) # Prefetch для первого фото товаров @@ -195,16 +245,52 @@ def pos_terminal(request): # Объединяем все позиции all_items = products + kits + + # Список всех активных складов для модалки выбора + warehouses = Warehouse.objects.filter(is_active=True).order_by('-is_default', 'name') + warehouses_list = [{ + 'id': w.id, + 'name': w.name, + 'is_default': w.is_default + } for w in warehouses] context = { 'categories_json': json.dumps(categories), 'items_json': json.dumps(all_items), 'showcase_kits_json': json.dumps([]), # Пустой массив - загрузка по API + 'current_warehouse': { + 'id': current_warehouse.id, + 'name': current_warehouse.name + }, + 'warehouses': warehouses_list, 'title': 'POS Terminal', } return render(request, 'pos/terminal.html', context) +@login_required +@require_http_methods(["POST"]) +def set_warehouse(request, warehouse_id): + """ + Установить текущий склад для POS. + Сохраняет выбор в сессию. + """ + try: + warehouse = Warehouse.objects.get(id=warehouse_id, is_active=True) + request.session['pos_warehouse_id'] = warehouse.id + + return JsonResponse({ + 'success': True, + 'warehouse_id': warehouse.id, + 'warehouse_name': warehouse.name + }) + except Warehouse.DoesNotExist: + return JsonResponse({ + 'success': False, + 'error': 'Склад не найден или неактивен' + }, status=404) + + @login_required @require_http_methods(["GET"]) def showcase_items_api(request):