Files
NightSpire/home/views/store.py
T
Inna b3184fbdfe Divide home/views.py en el paquete home/views/ por secciones
El módulo monolítico (~3200 líneas) se convierte en un paquete cuyo
__init__.py re-exporta todas las vistas, de modo que `views.X`,
`from home.views import X` y la cadena 'home.views.not_found' (handler404)
siguen funcionando sin tocar urls.py.

Módulos por sección: _base (imports comunes + require_paid_stripe), auth,
pages, account, recruit, characters, store, points, guild, history,
players y payments. Cada submódulo hereda los imports con
`from ._base import *`; las dependencias entre secciones se resuelven con
imports explícitos (guild->auth, recruit/history->account).

Se eliminan dos definiciones duplicadas que quedaban muertas al cargar:
- get_character_image (primera copia, idéntica a la efectiva)
- store_novawow_success_view (stub sin SOAP, pisado por la versión real)

Validado con `manage.py check` (0 issues), resolución de las 100 rutas y
un chequeo estático de nombres no definidos por módulo.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 19:09:40 +00:00

174 lines
7.4 KiB
Python

from ._base import *
def store_novawow_cancel_view(request):
return render(request, 'store/store_novawow_cancel.html')
def store_novawow_view(request):
username = request.session.get('username')
if not username:
return redirect('login')
category_name = request.GET.get("category")
# Obtener personajes del usuario desde la base de datos acore_characters
with connections['acore_characters'].cursor() as cursor:
cursor.execute("""
SELECT name
FROM characters
WHERE account = (
SELECT id
FROM acore_auth.account
WHERE username = %s
)
""", [username])
characters = [row[0] for row in cursor.fetchall()]
# Obtener categorías e ítems
categories = Category.objects.prefetch_related("items").all()
store_data = {category.name: list(category.items.values()) for category in categories}
# Si se está solicitando una categoría específica, devolver solo los ítems de esa categoría
if category_name:
try:
category = Category.objects.get(name=category_name)
except Category.DoesNotExist:
return JsonResponse({'success': False, 'message': 'Categoría no encontrada'}, status=404)
return JsonResponse({'success': True, 'items': list(category.items.values())})
# Obtener carrito desde la sesión
carrito = request.session.get('carrito', [])
# Calcular el precio total con 2 decimales
total_price = sum(Decimal(item['precio']) for item in carrito)
total_price_with_iva = (total_price * Decimal('1.21')).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
if request.method == 'POST':
# Obtener datos del body
data = json.loads(request.body)
character_name = data.get('character')
carrito_cliente = data.get('carrito') or []
if not character_name:
return JsonResponse({'success': False, 'message': 'Por favor, selecciona un personaje.'})
# Verificar si el personaje pertenece al usuario
if character_name not in characters:
return JsonResponse({'success': False, 'message': 'No tienes permiso para este personaje.'})
if not carrito_cliente:
return JsonResponse({'success': False, 'message': 'El carrito está vacío.'})
# VALIDAR ÍTEMS Y PRECIOS CONTRA LA BASE DE DATOS (nunca confiar en el
# precio ni en los ítems que manda el cliente).
try:
ids_solicitados = [int(item['id']) for item in carrito_cliente]
except (KeyError, TypeError, ValueError):
return JsonResponse({'success': False, 'message': 'Carrito no válido.'})
items_db = {i.item_id: i for i in Item.objects.filter(item_id__in=ids_solicitados)}
carrito = [] # carrito saneado que se guardará en sesión
total_price = Decimal('0.00')
for item in carrito_cliente:
item_id = int(item['id'])
db_item = items_db.get(item_id)
if not db_item:
return JsonResponse({'success': False, 'message': f'Ítem no disponible en la tienda (ID {item_id}).'})
try:
cantidad = max(1, int(item.get('cantidad', 1)))
except (TypeError, ValueError):
cantidad = 1
total_price += db_item.price * cantidad
carrito.append({'id': item_id, 'nombre': db_item.name, 'cantidad': cantidad})
total_price_with_iva = (total_price * Decimal('1.21')).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
# Guardar en sesión el carrito YA VALIDADO (para la entrega tras el pago)
request.session['carrito'] = carrito
request.session.modified = True
# Descripción del producto a partir de los datos validados
product_description = ", ".join([f"{it['nombre']} x{it['cantidad']}" for it in carrito])
# Obtener detalles del usuario
with connections['acore_auth'].cursor() as cursor:
cursor.execute("SELECT id, email, last_ip FROM account WHERE username = %s", [username])
user_data = cursor.fetchone()
if not user_data:
return JsonResponse({'success': False, 'message': 'Cuenta no encontrada.'})
user_id, email, acore_ip = user_data # ID de la cuenta, email y la última IP
# Crear sesión de pago con Stripe
success_url = request.build_absolute_uri(reverse('store-novawow/success'))
cancel_url = request.build_absolute_uri(reverse('store-novawow/cancel'))
# Llamada corregida a create_checkout_session con todos los parámetros requeridos
stripe_response = create_checkout_session(
username=username,
email=email,
acore_ip=acore_ip,
account_id=user_id, # Pasar el account_id
amount=total_price_with_iva, # El precio con IVA
character_name=character_name,
success_url=success_url,
cancel_url=cancel_url,
product_name=f"Compra de ítems para el personaje: {character_name} : {product_description}"
)
if stripe_response["success"]:
request.session['store_novawow'] = character_name
return JsonResponse({
'success': True,
'session_id': stripe_response["session_id"],
'stripe_public_key': settings.STRIPE_PUBLIC_KEY
})
else:
return JsonResponse({'success': False, 'message': f'Error al iniciar el pago: {stripe_response["error"]}'})
return render(request, 'store/store_novawow.html', {
'characters': characters,
'store_data': store_data,
'categories': categories,
'total_price': total_price_with_iva
})
@require_paid_stripe(redirect_to='store-novawow')
def store_novawow_success_view(request):
# Procesar el comando SOAP tras el pago exitoso
character_name = request.session.get('store_novawow')
carrito = request.session.get('carrito', [])
if not character_name or not carrito:
return redirect('store-novawow')
# Contar correctamente los ítems por ID
item_counts = Counter(item["id"] for item in carrito)
# Generar la lista de ítems en formato "ID:Cantidad"
items_str = " ".join(f"{item_id}:{count}" for item_id, count in item_counts.items())
# Construir el comando SOAP
command = f".send items {character_name} 'Gracias por donar a Nova WoW' 'Aquí tienes tu donación' {items_str}"
# Ejecutar el comando SOAP
response = execute_soap_command(command)
# Limpiar la sesión después del envío
request.session.pop('store_novawow', None)
request.session.pop('carrito', None)
if response is None or response.strip() == "":
message = f"Los ítems han sido añadidos al personaje {character_name} correctamente."
return render(request, 'store/store_novawow_success.html', {'message': message})
else:
return render(request, 'store/store_novawow_success.html', {'message': f"Error: {response}"})
def store_novawow_buscar_categoria_view(request, categoria):
try:
# Buscar la categoría desde el parámetro de la URL
category = Category.objects.get(name=categoria)
items = category.items.all().values("id", "name", "price", "image_url")
return JsonResponse({"success": True, "items": list(items)})
except Category.DoesNotExist:
return JsonResponse({"success": False, "message": "Categoría no encontrada"}, status=404)