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>
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user