Добавлена функциональность витрин для POS: модели, сервисы, UI
- Создана модель Showcase (витрина) привязанная к складу - Расширена Reservation для поддержки витринных резервов - Добавлены поля в OrderItem для маркировки витринных продаж - Реализован ShowcaseManager с методами резервирования, продажи и разбора - Обновлён админ-интерфейс для управления витринами - Добавлена кнопка Витрина в POS (категории) и API для просмотра - Добавлена кнопка На витрину в панели действий POS - Миграции готовы к применению
This commit is contained in:
@@ -14,6 +14,48 @@ function renderCategories() {
|
||||
const grid = document.getElementById('categoryGrid');
|
||||
grid.innerHTML = '';
|
||||
|
||||
// Кнопка "Витрина" - первая в ряду
|
||||
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.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('Ошибка загрузки витринных букетов');
|
||||
}
|
||||
};
|
||||
const showcaseBody = document.createElement('div');
|
||||
showcaseBody.className = 'card-body';
|
||||
const showcaseName = document.createElement('div');
|
||||
showcaseName.className = 'category-name';
|
||||
showcaseName.innerHTML = '<i class="bi bi-flower1"></i> <strong>ВИТРИНА</strong>';
|
||||
showcaseBody.appendChild(showcaseName);
|
||||
showcaseCard.appendChild(showcaseBody);
|
||||
showcaseCol.appendChild(showcaseCard);
|
||||
grid.appendChild(showcaseCol);
|
||||
|
||||
// Кнопка "Все"
|
||||
const allCol = document.createElement('div');
|
||||
allCol.className = 'col-6 col-sm-4 col-md-3 col-lg-2';
|
||||
@@ -241,6 +283,11 @@ function clearCart() {
|
||||
|
||||
document.getElementById('clearCart').onclick = clearCart;
|
||||
|
||||
// Кнопка "На витрину" - функционал будет добавлен позже
|
||||
document.getElementById('addToShowcaseBtn').onclick = () => {
|
||||
alert('Функционал "На витрину" будет реализован позже');
|
||||
};
|
||||
|
||||
// Заглушки для функционала (будет реализовано позже)
|
||||
document.getElementById('checkoutNow').onclick = async () => {
|
||||
alert('Функционал будет подключен позже: создание заказа и списание со склада.');
|
||||
|
||||
@@ -57,7 +57,8 @@
|
||||
<div class="card-body p-2">
|
||||
<div class="row g-2">
|
||||
<div class="col-4">
|
||||
<button class="btn btn-outline-secondary rounded-3 w-100" style="height: 60px;">
|
||||
<button class="btn btn-outline-warning rounded-3 w-100" id="addToShowcaseBtn" style="height: 60px;">
|
||||
<i class="bi bi-flower1"></i><br>На витрину
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
|
||||
@@ -6,4 +6,5 @@ app_name = 'pos'
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.pos_terminal, name='terminal'),
|
||||
path('api/showcase-items/', views.showcase_items_api, name='showcase-items-api'),
|
||||
]
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
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
|
||||
import json
|
||||
|
||||
|
||||
@@ -52,3 +55,45 @@ def pos_terminal(request):
|
||||
'title': 'POS Terminal',
|
||||
}
|
||||
return render(request, 'pos/terminal.html', context)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def showcase_items_api(request):
|
||||
"""
|
||||
API endpoint для получения витринных букетов.
|
||||
Возвращает комплекты, зарезервированные на активных витринах.
|
||||
"""
|
||||
# Получаем все активные резервы на витринах
|
||||
showcase_reservations = Reservation.objects.filter(
|
||||
showcase__isnull=False,
|
||||
showcase__is_active=True,
|
||||
status='reserved'
|
||||
).select_related('showcase', 'product').prefetch_related('product__photos')
|
||||
|
||||
# Группируем по витринам
|
||||
showcases_dict = {}
|
||||
for res in showcase_reservations:
|
||||
showcase_id = res.showcase.id
|
||||
if showcase_id not in showcases_dict:
|
||||
showcases_dict[showcase_id] = {
|
||||
'id': showcase_id,
|
||||
'name': res.showcase.name,
|
||||
'warehouse': res.showcase.warehouse.name,
|
||||
'items': []
|
||||
}
|
||||
|
||||
# Добавляем товар в список
|
||||
showcases_dict[showcase_id]['items'].append({
|
||||
'product_id': res.product.id,
|
||||
'product_name': res.product.name,
|
||||
'quantity': str(res.quantity),
|
||||
'image': res.product.photos.first().get_thumbnail_url() if res.product.photos.exists() else None,
|
||||
})
|
||||
|
||||
showcases_list = list(showcases_dict.values())
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'showcases': showcases_list
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user