conventional-commit

feat(inventory): introduce stock deficit notifications and base quantity tracking

- Added `quantity_base` field to reservation model for precise inventory calculations
- Implemented non-blocking stock deficit warnings during kit creation process
- Enhanced API responses with warning details for frontend display
- Updated terminal interface to show formatted stock shortage alerts

BREAKING CHANGE: API response structure now includes `warnings` array instead of previous stock warning format
This commit is contained in:
2026-01-04 16:18:57 +03:00
parent a03f3df086
commit b7db4cd162
3 changed files with 24 additions and 5 deletions

View File

@@ -47,6 +47,7 @@ class ShowcaseManager:
'success': False,
'showcase_items': [],
'reservations': [],
'warnings': None,
'message': f'Витрина "{showcase.name}" не активна'
}
@@ -63,9 +64,23 @@ class ShowcaseManager:
'success': False,
'showcase_items': [],
'reservations': [],
'warnings': None,
'message': f'Комплект "{product_kit.name}" не содержит компонентов'
}
# Проверяем дефицит компонентов (предупреждение, не блокировка)
warnings = []
for kit_item in kit_items:
product = kit_item.product
if not product:
continue
qty_needed = (kit_item.quantity or Decimal('1')) * quantity
stock = Stock.objects.filter(product=product, warehouse=warehouse).first()
free_qty = stock.quantity_free if stock else Decimal('0')
if free_qty < qty_needed:
deficit = qty_needed - free_qty
warnings.append(f"{product.name}: не хватает {deficit} шт.")
# Создаём N экземпляров
for _ in range(quantity):
# 1. Создаём ShowcaseItem
@@ -96,6 +111,7 @@ class ShowcaseManager:
product_kit=product_kit,
showcase_item=showcase_item, # Связь с экземпляром!
quantity=kit_item.quantity,
quantity_base=kit_item.quantity, # Для корректного учёта в Stock
status='reserved'
)
all_reservations.append(reservation)
@@ -113,6 +129,7 @@ class ShowcaseManager:
'success': True,
'showcase_items': showcase_items,
'reservations': all_reservations,
'warnings': warnings if warnings else None,
'message': f'Создано {quantity} экз. комплекта "{product_kit.name}" на витрине "{showcase.name}"'
}
@@ -121,6 +138,7 @@ class ShowcaseManager:
'success': False,
'showcase_items': [],
'reservations': [],
'warnings': None,
'message': f'Ошибка резервирования: {str(e)}'
}
@@ -410,7 +428,7 @@ class ShowcaseManager:
product_kit=product_kit,
showcase_item=showcase_item,
status='reserved',
defaults={'quantity': quantity_per_item},
defaults={'quantity': quantity_per_item, 'quantity_base': quantity_per_item},
)
if not created:
reservation.quantity = (reservation.quantity or Decimal('0')) + quantity_per_item

View File

@@ -2065,10 +2065,10 @@ document.getElementById('confirmCreateTempKit').onclick = async () => {
Зарезервировано компонентов: ${data.reservations_count}`;
// Если есть предупреждение о нехватке товара - добавляем его
if (data.stock_warning && data.stock_warnings && data.stock_warnings.length > 0) {
if (data.warnings && data.warnings.length > 0) {
successMessage += '\n\n⚠ ВНИМАНИЕ: Нехватка товара на складе!\n';
data.stock_warnings.forEach(warning => {
successMessage += `\n${warning.product_name}: не хватает ${warning.overdraft} ед.`;
data.warnings.forEach(warning => {
successMessage += `\n${warning}`;
});
successMessage += '\n\nПроверьте остатки и пополните склад.';
}

View File

@@ -1096,7 +1096,8 @@ def create_temp_kit_to_showcase(request):
'kit_price': str(kit.actual_price),
'reservations_count': len(result['reservations']),
'showcase_item_ids': showcase_item_ids,
'available_count': created_count
'available_count': created_count,
'warnings': result.get('warnings')
})
except json.JSONDecodeError as e: