Fix Product filtering and add kit disassembly functionality

Fixed:
- Replace is_active with status='active' for Product filtering in IncomingModelForm
- Product model uses status field instead of is_active

Added:
- Showcase field to ProductKit for tracking showcase placement
- product_kit field to Reservation for tracking kit-specific reservations
- Disassemble button in POS terminal for showcase kits
- API endpoint for kit disassembly (release reservations, mark discontinued)
- Improved reservation filtering when dismantling specific kits

Changes:
- ShowcaseManager now links reservations to specific kit instances
- POS terminal modal shows disassemble button in edit mode
- Kit disassembly properly updates stock aggregates

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-20 23:03:47 +03:00
parent d3060176c0
commit ff0756498c
11 changed files with 221 additions and 14 deletions

View File

@@ -716,7 +716,8 @@ def create_temp_kit_to_showcase(request):
status='active',
price_adjustment_type=price_adjustment_type,
price_adjustment_value=price_adjustment_value,
sale_price=sale_price
sale_price=sale_price,
showcase=showcase
)
# 2. Создаём KitItem для каждого товара из корзины
@@ -911,3 +912,73 @@ def update_product_kit(request, kit_id):
return JsonResponse({'success': False, 'error': 'Неверный формат данных'}, status=400)
except Exception as e:
return JsonResponse({'success': False, 'error': f'Ошибка: {str(e)}'}, status=500)
@login_required
@require_http_methods(["POST"])
def disassemble_product_kit(request, kit_id):
"""
Разбирает витринный комплект: освобождает резервы и устанавливает статус 'discontinued'.
Args:
request: HTTP запрос
kit_id: ID комплекта для разбора
Returns:
JSON: {
'success': bool,
'released_count': int,
'message': str,
'error': str (если failed)
}
"""
try:
# Получаем комплект с витриной (только временные комплекты)
kit = ProductKit.objects.select_related('showcase').get(id=kit_id, is_temporary=True)
# Проверяем, что комплект ещё не разобран
if kit.status == 'discontinued':
return JsonResponse({
'success': False,
'error': 'Комплект уже разобран (статус: Снят)'
}, status=400)
# Проверяем, что у комплекта есть привязанная витрина
if not kit.showcase:
return JsonResponse({
'success': False,
'error': 'Комплект не привязан к витрине'
}, status=400)
# Освобождаем резервы и устанавливаем статус
# ShowcaseManager.dismantle_from_showcase уже использует transaction.atomic()
result = ShowcaseManager.dismantle_from_showcase(
showcase=kit.showcase,
product_kit=kit
)
if not result['success']:
return JsonResponse({
'success': False,
'error': result['message']
}, status=400)
# Устанавливаем статус комплекта 'discontinued'
kit.discontinue(user=request.user)
return JsonResponse({
'success': True,
'released_count': result['released_count'],
'message': f'Комплект "{kit.name}" разобран. Статус изменён на "Снят".'
})
except ProductKit.DoesNotExist:
return JsonResponse({
'success': False,
'error': 'Комплект не найден'
}, status=404)
except Exception as e:
return JsonResponse({
'success': False,
'error': f'Ошибка при разборе: {str(e)}'
}, status=500)