Fix cart lock validation and error handling improvements

## 1. Add cart lock validation to sell_from_showcase()
- Prevent selling showcase kits locked in another cashier's cart
- Check cart_lock_expires_at before allowing direct sales
- Return clear error message with lock holder's name and time remaining
- File: inventory/services/showcase_manager.py

## 2. Improve error handling in POS create_temp_kit_to_showcase()
- Add detailed logging for all error types (JSON, validation, generic)
- Provide user-friendly error messages instead of generic 500
- Log full context (kit name, showcase ID, items, user) for debugging
- Categorize errors: stock issues, integrity, locks, not found
- File: pos/views.py

## 3. Fix critical bug in create_temporary_kit()
- Replace non-existent is_active field with status='active'
- Affects 3 locations: kit creation, product lookup, kit duplication
- This was causing 500 errors when creating temporary kits from order edit
- File: products/services/kit_service.py

## 4. Improve error handling in create_temporary_kit_api()
- Add comprehensive logging for order creation endpoint
- Provide specific error messages for common failure scenarios
- Help diagnose issues when creating kits from order editing UI
- File: products/views/api_views.py

These changes complete the Soft Lock system and fix the 500 error issue.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-21 00:24:59 +03:00
parent 33e33ecbac
commit 08a5527ba7
7 changed files with 135 additions and 16 deletions

View File

@@ -144,7 +144,7 @@ class ShowcaseManager:
reservations = Reservation.objects.filter(
showcase=showcase,
status='reserved'
).select_related('product')
).select_related('product', 'locked_by_user')
if not reservations.exists():
return {
@@ -153,6 +153,26 @@ class ShowcaseManager:
'message': f'На витрине "{showcase.name}" нет зарезервированных товаров'
}
# Проверяем блокировки корзины (Soft Lock)
# Если комплект заблокирован в корзине другого кассира, запрещаем продажу
active_locks = reservations.filter(
cart_lock_expires_at__gt=timezone.now(),
cart_lock_expires_at__isnull=False
)
if active_locks.exists():
lock = active_locks.first()
time_left = (lock.cart_lock_expires_at - timezone.now()).total_seconds() / 60
locker_name = lock.locked_by_user.username if lock.locked_by_user else 'неизвестный кассир'
return {
'success': False,
'order': None,
'message': f'Комплект заблокирован в корзине кассира "{locker_name}". '
f'Блокировка истечёт через {int(time_left)} мин. '
f'Дождитесь освобождения или попросите кассира удалить букет из корзины.'
}
# Получаем статус "Завершён" для POS-продаж
completed_status = OrderStatus.objects.filter(
code='completed',