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:
@@ -47,6 +47,7 @@ class ShowcaseManager:
|
|||||||
'success': False,
|
'success': False,
|
||||||
'showcase_items': [],
|
'showcase_items': [],
|
||||||
'reservations': [],
|
'reservations': [],
|
||||||
|
'warnings': None,
|
||||||
'message': f'Витрина "{showcase.name}" не активна'
|
'message': f'Витрина "{showcase.name}" не активна'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,9 +64,23 @@ class ShowcaseManager:
|
|||||||
'success': False,
|
'success': False,
|
||||||
'showcase_items': [],
|
'showcase_items': [],
|
||||||
'reservations': [],
|
'reservations': [],
|
||||||
|
'warnings': None,
|
||||||
'message': f'Комплект "{product_kit.name}" не содержит компонентов'
|
'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 экземпляров
|
# Создаём N экземпляров
|
||||||
for _ in range(quantity):
|
for _ in range(quantity):
|
||||||
# 1. Создаём ShowcaseItem
|
# 1. Создаём ShowcaseItem
|
||||||
@@ -96,6 +111,7 @@ class ShowcaseManager:
|
|||||||
product_kit=product_kit,
|
product_kit=product_kit,
|
||||||
showcase_item=showcase_item, # Связь с экземпляром!
|
showcase_item=showcase_item, # Связь с экземпляром!
|
||||||
quantity=kit_item.quantity,
|
quantity=kit_item.quantity,
|
||||||
|
quantity_base=kit_item.quantity, # Для корректного учёта в Stock
|
||||||
status='reserved'
|
status='reserved'
|
||||||
)
|
)
|
||||||
all_reservations.append(reservation)
|
all_reservations.append(reservation)
|
||||||
@@ -113,6 +129,7 @@ class ShowcaseManager:
|
|||||||
'success': True,
|
'success': True,
|
||||||
'showcase_items': showcase_items,
|
'showcase_items': showcase_items,
|
||||||
'reservations': all_reservations,
|
'reservations': all_reservations,
|
||||||
|
'warnings': warnings if warnings else None,
|
||||||
'message': f'Создано {quantity} экз. комплекта "{product_kit.name}" на витрине "{showcase.name}"'
|
'message': f'Создано {quantity} экз. комплекта "{product_kit.name}" на витрине "{showcase.name}"'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,6 +138,7 @@ class ShowcaseManager:
|
|||||||
'success': False,
|
'success': False,
|
||||||
'showcase_items': [],
|
'showcase_items': [],
|
||||||
'reservations': [],
|
'reservations': [],
|
||||||
|
'warnings': None,
|
||||||
'message': f'Ошибка резервирования: {str(e)}'
|
'message': f'Ошибка резервирования: {str(e)}'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,7 +428,7 @@ class ShowcaseManager:
|
|||||||
product_kit=product_kit,
|
product_kit=product_kit,
|
||||||
showcase_item=showcase_item,
|
showcase_item=showcase_item,
|
||||||
status='reserved',
|
status='reserved',
|
||||||
defaults={'quantity': quantity_per_item},
|
defaults={'quantity': quantity_per_item, 'quantity_base': quantity_per_item},
|
||||||
)
|
)
|
||||||
if not created:
|
if not created:
|
||||||
reservation.quantity = (reservation.quantity or Decimal('0')) + quantity_per_item
|
reservation.quantity = (reservation.quantity or Decimal('0')) + quantity_per_item
|
||||||
|
|||||||
@@ -2065,10 +2065,10 @@ document.getElementById('confirmCreateTempKit').onclick = async () => {
|
|||||||
Зарезервировано компонентов: ${data.reservations_count}`;
|
Зарезервировано компонентов: ${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';
|
successMessage += '\n\n⚠️ ВНИМАНИЕ: Нехватка товара на складе!\n';
|
||||||
data.stock_warnings.forEach(warning => {
|
data.warnings.forEach(warning => {
|
||||||
successMessage += `\n• ${warning.product_name}: не хватает ${warning.overdraft} ед.`;
|
successMessage += `\n• ${warning}`;
|
||||||
});
|
});
|
||||||
successMessage += '\n\nПроверьте остатки и пополните склад.';
|
successMessage += '\n\nПроверьте остатки и пополните склад.';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1096,7 +1096,8 @@ def create_temp_kit_to_showcase(request):
|
|||||||
'kit_price': str(kit.actual_price),
|
'kit_price': str(kit.actual_price),
|
||||||
'reservations_count': len(result['reservations']),
|
'reservations_count': len(result['reservations']),
|
||||||
'showcase_item_ids': showcase_item_ids,
|
'showcase_item_ids': showcase_item_ids,
|
||||||
'available_count': created_count
|
'available_count': created_count,
|
||||||
|
'warnings': result.get('warnings')
|
||||||
})
|
})
|
||||||
|
|
||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError as e:
|
||||||
|
|||||||
Reference in New Issue
Block a user