Расширена debug страница для отслеживания статусов ShowcaseItem
- inventory/templates/inventory/debug_page.html: добавлена секция ShowcaseItem * Таблица с полями: ID, Название, Статус, OrderItem, Locked By * Цветовые индикаторы статусов (available/in_cart/reserved/sold) * Ссылки на связанные OrderItem - inventory/views/debug_views.py: добавлены данные ShowcaseItem в контекст * showcase_items queryset с select_related для оптимизации * Статистика по статусам ShowcaseItem - Инструмент для тестирования lifecycle витринных букетов
This commit is contained in:
@@ -69,6 +69,11 @@
|
|||||||
.status-reserved { background-color: #fff3cd; }
|
.status-reserved { background-color: #fff3cd; }
|
||||||
.status-converted { background-color: #d1ecf1; }
|
.status-converted { background-color: #d1ecf1; }
|
||||||
.status-released { background-color: #d4edda; }
|
.status-released { background-color: #d4edda; }
|
||||||
|
.showcase-available { background-color: #d4edda; }
|
||||||
|
.showcase-in-cart { background-color: #fff3cd; }
|
||||||
|
.showcase-reserved { background-color: #ffeaa7; }
|
||||||
|
.showcase-sold { background-color: #d1ecf1; }
|
||||||
|
.showcase-dismantled { background-color: #f8d7da; }
|
||||||
.inactive-row { background-color: #f8d7da; opacity: 0.7; }
|
.inactive-row { background-color: #f8d7da; opacity: 0.7; }
|
||||||
.section-card {
|
.section-card {
|
||||||
border: 1px solid #dee2e6;
|
border: 1px solid #dee2e6;
|
||||||
@@ -367,7 +372,92 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ПРОДАЖИ (Sale) -->
|
<!-- ВИТРИННЫЕ ЭКЗЕМПЛЯРЫ (ShowcaseItem) -->
|
||||||
|
<div class="section-card">
|
||||||
|
<h3>🌺 Витринные экземпляры ShowcaseItem ({{ showcase_items.count }})</h3>
|
||||||
|
<div class="summary-box">
|
||||||
|
<strong>Available:</strong> {{ showcase_items_stats.available|default:0 }}
|
||||||
|
</div>
|
||||||
|
<div class="summary-box">
|
||||||
|
<strong>In Cart:</strong> {{ showcase_items_stats.in_cart|default:0 }}
|
||||||
|
</div>
|
||||||
|
<div class="summary-box">
|
||||||
|
<strong>Reserved:</strong> {{ showcase_items_stats.reserved|default:0 }}
|
||||||
|
</div>
|
||||||
|
<div class="summary-box">
|
||||||
|
<strong>Sold:</strong> {{ showcase_items_stats.sold|default:0 }}
|
||||||
|
</div>
|
||||||
|
<div class="summary-box">
|
||||||
|
<strong>Dismantled:</strong> {{ showcase_items_stats.dismantled|default:0 }}
|
||||||
|
</div>
|
||||||
|
<div class="table-responsive mt-2">
|
||||||
|
<table class="table table-sm table-bordered table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Комплект</th>
|
||||||
|
<th>Витрина</th>
|
||||||
|
<th>Статус</th>
|
||||||
|
<th>Заказ</th>
|
||||||
|
<th>Продано</th>
|
||||||
|
<th>Создан</th>
|
||||||
|
<th>Обновлён</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for item in showcase_items %}
|
||||||
|
<tr class="
|
||||||
|
{% if item.status == 'available' %}showcase-available
|
||||||
|
{% elif item.status == 'in_cart' %}showcase-in-cart
|
||||||
|
{% elif item.status == 'reserved' %}showcase-reserved
|
||||||
|
{% elif item.status == 'sold' %}showcase-sold
|
||||||
|
{% elif item.status == 'dismantled' %}showcase-dismantled
|
||||||
|
{% endif %}
|
||||||
|
">
|
||||||
|
<td>{{ item.id }}</td>
|
||||||
|
<td>
|
||||||
|
<strong>{{ item.product_kit.name }}</strong>
|
||||||
|
{% if item.product_kit.is_temporary %}
|
||||||
|
<span class="badge bg-warning text-dark" title="Временный комплект из POS">TEMP</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>{{ item.showcase.name }}</td>
|
||||||
|
<td>
|
||||||
|
{% if item.status == 'available' %}
|
||||||
|
<span class="badge bg-success">Доступен</span>
|
||||||
|
{% elif item.status == 'in_cart' %}
|
||||||
|
<span class="badge bg-warning text-dark">В корзине</span>
|
||||||
|
{% elif item.status == 'reserved' %}
|
||||||
|
<span class="badge bg-warning" style="background-color: #f39c12 !important;">Зарезервирован</span>
|
||||||
|
{% elif item.status == 'sold' %}
|
||||||
|
<span class="badge bg-info">Продан</span>
|
||||||
|
{% elif item.status == 'dismantled' %}
|
||||||
|
<span class="badge bg-danger">Разобран</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="badge bg-secondary">{{ item.get_status_display }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if item.sold_order_item %}
|
||||||
|
<strong>{{ item.sold_order_item.order.order_number }}</strong>
|
||||||
|
<small class="text-muted">(#{{ item.sold_order_item.id }})</small>
|
||||||
|
{% else %}
|
||||||
|
<span class="text-muted">-</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="text-muted-small">
|
||||||
|
{% if item.sold_at %}{{ item.sold_at|date:"d.m.Y H:i:s" }}{% else %}-{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="text-muted-small">{{ item.created_at|date:"d.m.Y H:i" }}</td>
|
||||||
|
<td class="text-muted-small">{{ item.updated_at|date:"d.m.Y H:i:s" }}</td>
|
||||||
|
</tr>
|
||||||
|
{% empty %}
|
||||||
|
<tr><td colspan="8" class="text-center text-muted">Нет витринных экземпляров</td></tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="section-card">
|
<div class="section-card">
|
||||||
<h3>💰 Продажи Sale ({{ sales.count }})</h3>
|
<h3>💰 Продажи Sale ({{ sales.count }})</h3>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ from django.shortcuts import render
|
|||||||
from django.db.models import Q, Sum, Count
|
from django.db.models import Q, Sum, Count
|
||||||
from inventory.models import (
|
from inventory.models import (
|
||||||
StockBatch, Stock, Reservation, Sale, SaleBatchAllocation, WriteOff,
|
StockBatch, Stock, Reservation, Sale, SaleBatchAllocation, WriteOff,
|
||||||
WriteOffDocument, WriteOffDocumentItem, IncomingDocument, IncomingDocumentItem
|
WriteOffDocument, WriteOffDocumentItem, IncomingDocument, IncomingDocumentItem,
|
||||||
|
ShowcaseItem
|
||||||
)
|
)
|
||||||
from orders.models import Order
|
from orders.models import Order
|
||||||
from products.models import Product
|
from products.models import Product
|
||||||
@@ -52,6 +53,11 @@ def debug_inventory_page(request):
|
|||||||
incoming_document_items = IncomingDocumentItem.objects.select_related('product', 'document__warehouse').order_by('-id')
|
incoming_document_items = IncomingDocumentItem.objects.select_related('product', 'document__warehouse').order_by('-id')
|
||||||
orders = Order.objects.prefetch_related('items').order_by('-created_at')
|
orders = Order.objects.prefetch_related('items').order_by('-created_at')
|
||||||
|
|
||||||
|
# Витринные экземпляры
|
||||||
|
showcase_items = ShowcaseItem.objects.select_related(
|
||||||
|
'product_kit', 'showcase', 'sold_order_item__order'
|
||||||
|
).order_by('-updated_at')
|
||||||
|
|
||||||
# Применяем фильтры
|
# Применяем фильтры
|
||||||
if product_id:
|
if product_id:
|
||||||
product = Product.objects.filter(id=product_id).first()
|
product = Product.objects.filter(id=product_id).first()
|
||||||
@@ -91,6 +97,8 @@ def debug_inventory_page(request):
|
|||||||
allocations = allocations.filter(sale__order=order)
|
allocations = allocations.filter(sale__order=order)
|
||||||
# Фильтруем только этот заказ в таблице заказов
|
# Фильтруем только этот заказ в таблице заказов
|
||||||
orders = orders.filter(id=order.id)
|
orders = orders.filter(id=order.id)
|
||||||
|
# Фильтруем витринные экземпляры по заказу
|
||||||
|
showcase_items = showcase_items.filter(sold_order_item__order=order)
|
||||||
else:
|
else:
|
||||||
order = None
|
order = None
|
||||||
|
|
||||||
@@ -120,6 +128,16 @@ def debug_inventory_page(request):
|
|||||||
incoming_documents = incoming_documents[:50]
|
incoming_documents = incoming_documents[:50]
|
||||||
incoming_document_items = incoming_document_items[:100]
|
incoming_document_items = incoming_document_items[:100]
|
||||||
orders = orders[:50]
|
orders = orders[:50]
|
||||||
|
showcase_items = showcase_items[:100]
|
||||||
|
|
||||||
|
# Статистика по статусам ShowcaseItem
|
||||||
|
showcase_items_stats = ShowcaseItem.objects.aggregate(
|
||||||
|
available=Count('id', filter=Q(status='available')),
|
||||||
|
in_cart=Count('id', filter=Q(status='in_cart')),
|
||||||
|
reserved=Count('id', filter=Q(status='reserved')),
|
||||||
|
sold=Count('id', filter=Q(status='sold')),
|
||||||
|
dismantled=Count('id', filter=Q(status='dismantled')),
|
||||||
|
)
|
||||||
|
|
||||||
# Списки для фильтров
|
# Списки для фильтров
|
||||||
products = Product.objects.filter(archived_at__isnull=True).order_by('name')[:200]
|
products = Product.objects.filter(archived_at__isnull=True).order_by('name')[:200]
|
||||||
@@ -137,6 +155,8 @@ def debug_inventory_page(request):
|
|||||||
'incoming_documents': incoming_documents,
|
'incoming_documents': incoming_documents,
|
||||||
'incoming_document_items': incoming_document_items,
|
'incoming_document_items': incoming_document_items,
|
||||||
'orders': orders,
|
'orders': orders,
|
||||||
|
'showcase_items': showcase_items,
|
||||||
|
'showcase_items_stats': showcase_items_stats,
|
||||||
'products': products,
|
'products': products,
|
||||||
'warehouses': warehouses,
|
'warehouses': warehouses,
|
||||||
'selected_product': product,
|
'selected_product': product,
|
||||||
|
|||||||
Reference in New Issue
Block a user