diff --git a/myproject/pos/static/pos/css/terminal.css b/myproject/pos/static/pos/css/terminal.css
index 1267790..caf8056 100644
--- a/myproject/pos/static/pos/css/terminal.css
+++ b/myproject/pos/static/pos/css/terminal.css
@@ -216,6 +216,17 @@ body {
color: white;
}
+/* Специальный стиль для активной кнопки витрины */
+.showcase-card.active {
+ background: #ff6600;
+ border-color: #ff6600;
+ border-width: 3px;
+ color: #000000;
+ font-weight: bold;
+ box-shadow: 0 6px 16px rgba(255, 102, 0, 0.6);
+ transform: scale(1.05);
+}
+
.category-card .card-body {
padding: 0.5rem;
display: flex;
diff --git a/myproject/pos/static/pos/js/terminal.js b/myproject/pos/static/pos/js/terminal.js
index cd38774..1eccf4c 100644
--- a/myproject/pos/static/pos/js/terminal.js
+++ b/myproject/pos/static/pos/js/terminal.js
@@ -2,8 +2,10 @@
const CATEGORIES = JSON.parse(document.getElementById('categoriesData').textContent);
const ITEMS = JSON.parse(document.getElementById('itemsData').textContent); // Единый массив товаров и комплектов
+const SHOWCASE_KITS = JSON.parse(document.getElementById('showcaseKitsData').textContent); // Витринные комплекты
let currentCategoryId = null;
+let isShowcaseView = false; // Флаг режима просмотра витринных букетов
const cart = new Map(); // "type-id" -> {id, name, price, qty, type}
function formatMoney(v) {
@@ -18,33 +20,14 @@ function renderCategories() {
const showcaseCol = document.createElement('div');
showcaseCol.className = 'col-6 col-sm-4 col-md-3 col-lg-2';
const showcaseCard = document.createElement('div');
- showcaseCard.className = 'card category-card showcase-card';
+ showcaseCard.className = 'card category-card showcase-card' + (isShowcaseView ? ' active' : '');
showcaseCard.style.backgroundColor = '#fff3cd';
showcaseCard.style.borderColor = '#ffc107';
- showcaseCard.onclick = async () => {
- try {
- const response = await fetch('/pos/api/showcase-items/');
- const data = await response.json();
-
- if (data.success && data.showcases.length > 0) {
- let message = '🌺 ВИТРИННЫЕ БУКЕТЫ\n\n';
-
- data.showcases.forEach(showcase => {
- message += `● ${showcase.name} (Склад: ${showcase.warehouse})\n`;
- showcase.items.forEach(item => {
- message += ` - ${item.product_name}: ${item.quantity} шт\n`;
- });
- message += '\n';
- });
-
- alert(message);
- } else {
- alert('Витрины пусты');
- }
- } catch (error) {
- console.error('Error fetching showcase items:', error);
- alert('Ошибка загрузки витринных букетов');
- }
+ showcaseCard.onclick = () => {
+ isShowcaseView = true;
+ currentCategoryId = null;
+ renderCategories();
+ renderProducts();
};
const showcaseBody = document.createElement('div');
showcaseBody.className = 'card-body';
@@ -60,9 +43,10 @@ function renderCategories() {
const allCol = document.createElement('div');
allCol.className = 'col-6 col-sm-4 col-md-3 col-lg-2';
const allCard = document.createElement('div');
- allCard.className = 'card category-card' + (currentCategoryId === null ? ' active' : '');
+ allCard.className = 'card category-card' + (currentCategoryId === null && !isShowcaseView ? ' active' : '');
allCard.onclick = () => {
currentCategoryId = null;
+ isShowcaseView = false;
renderCategories();
renderProducts();
};
@@ -82,9 +66,10 @@ function renderCategories() {
col.className = 'col-6 col-sm-4 col-md-3 col-lg-2';
const card = document.createElement('div');
- card.className = 'card category-card' + (currentCategoryId === cat.id ? ' active' : '');
+ card.className = 'card category-card' + (currentCategoryId === cat.id && !isShowcaseView ? ' active' : '');
card.onclick = () => {
currentCategoryId = cat.id;
+ isShowcaseView = false;
renderCategories();
renderProducts();
};
@@ -108,9 +93,17 @@ function renderProducts() {
grid.innerHTML = '';
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
- let filtered = currentCategoryId
- ? ITEMS.filter(item => (item.category_ids || []).includes(currentCategoryId))
- : ITEMS;
+ let filtered;
+
+ // Если выбран режим витрины - показываем витринные комплекты
+ if (isShowcaseView) {
+ filtered = SHOWCASE_KITS;
+ } else {
+ // Обычный режим - показываем товары и комплекты
+ filtered = currentCategoryId
+ ? ITEMS.filter(item => (item.category_ids || []).includes(currentCategoryId))
+ : ITEMS;
+ }
if (searchTerm) {
filtered = filtered.filter(item => item.name.toLowerCase().includes(searchTerm));
@@ -149,9 +142,17 @@ function renderProducts() {
const stock = document.createElement('div');
stock.className = 'product-stock';
- stock.textContent = item.in_stock ? 'В наличии' : 'Под заказ';
- if (!item.in_stock) {
- stock.style.color = '#dc3545';
+
+ // Для витринных комплектов показываем название витрины
+ if (item.type === 'showcase_kit') {
+ stock.textContent = `🌺 ${item.showcase_name}`;
+ stock.style.color = '#856404';
+ stock.style.fontWeight = 'bold';
+ } else {
+ stock.textContent = item.in_stock ? 'В наличии' : 'Под заказ';
+ if (!item.in_stock) {
+ stock.style.color = '#dc3545';
+ }
}
const sku = document.createElement('div');
diff --git a/myproject/pos/templates/pos/terminal.html b/myproject/pos/templates/pos/terminal.html
index 5a853fd..778f517 100644
--- a/myproject/pos/templates/pos/terminal.html
+++ b/myproject/pos/templates/pos/terminal.html
@@ -159,6 +159,7 @@
+
{% endblock %}
diff --git a/myproject/pos/views.py b/myproject/pos/views.py
index 973d91b..b86fd9f 100644
--- a/myproject/pos/views.py
+++ b/myproject/pos/views.py
@@ -13,6 +13,69 @@ from inventory.models import Showcase, Reservation, Warehouse
from inventory.services import ShowcaseManager
+def get_showcase_kits_for_pos():
+ """
+ Получает витринные комплекты для отображения в POS.
+ Возвращает список временных комплектов, которые зарезервированы на витринах.
+ """
+ # Получаем все уникальные комплекты, у которых есть резервы на витринах
+ showcase_reservations = Reservation.objects.filter(
+ showcase__isnull=False,
+ showcase__is_active=True,
+ status='reserved'
+ ).select_related('showcase', 'product').values(
+ 'showcase_id', 'showcase__name'
+ ).distinct()
+
+ # Собираем все kit_items, связанные с этими резервами
+ showcase_kits = []
+
+ # Получаем все временные комплекты с резервами на витринах
+ reserved_products = Reservation.objects.filter(
+ showcase__isnull=False,
+ showcase__is_active=True,
+ status='reserved'
+ ).values_list('product_id', flat=True).distinct()
+
+ # Находим комплекты, в которых есть эти товары
+ kits_with_showcase_items = ProductKit.objects.filter(
+ is_temporary=True,
+ status='active',
+ kit_items__product_id__in=reserved_products
+ ).prefetch_related('photos', 'kit_items__product').distinct()
+
+ for kit in kits_with_showcase_items:
+ # Проверяем что все компоненты этого комплекта зарезервированы на одной витрине
+ kit_product_ids = set(kit.kit_items.values_list('product_id', flat=True))
+
+ # Находим резервы для этих товаров
+ kit_reservations = Reservation.objects.filter(
+ product_id__in=kit_product_ids,
+ showcase__isnull=False,
+ showcase__is_active=True,
+ status='reserved'
+ ).select_related('showcase')
+
+ if kit_reservations.exists():
+ # Берём первую витрину (обычно комплект резервируется на одной)
+ showcase = kit_reservations.first().showcase
+
+ showcase_kits.append({
+ 'id': kit.id,
+ 'name': kit.name,
+ 'price': str(kit.actual_price),
+ 'category_ids': [], # Временные комплекты обычно без категорий
+ 'in_stock': True, # На витрине = в наличии
+ 'sku': kit.sku or '',
+ 'image': kit.photos.first().get_thumbnail_url() if kit.photos.exists() else None,
+ 'type': 'showcase_kit',
+ 'showcase_name': showcase.name,
+ 'showcase_id': showcase.id
+ })
+
+ return showcase_kits
+
+
@login_required
def pos_terminal(request):
"""
@@ -51,12 +114,16 @@ def pos_terminal(request):
'type': 'kit'
} for k in kits_qs]
+ # Получаем витринные комплекты (временные комплекты с резервами на витринах)
+ showcase_kits_data = get_showcase_kits_for_pos()
+
# Объединяем все позиции
all_items = products + kits
context = {
'categories_json': json.dumps(categories),
- 'items_json': json.dumps(all_items), # Единый массив товаров и комплектов
+ 'items_json': json.dumps(all_items),
+ 'showcase_kits_json': json.dumps(showcase_kits_data),
'title': 'POS Terminal',
}
return render(request, 'pos/terminal.html', context)