Implementacion Inicial de la Tienda

This commit is contained in:
adevopg
2025-02-15 23:36:48 +01:00
parent fa8a0e0891
commit 41c3c6ae91
12 changed files with 420 additions and 125 deletions
@@ -0,0 +1,49 @@
# Generated by Django 5.1.4 on 2025-02-15 18:04
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('home', '0007_maintenancemode'),
]
operations = [
migrations.CreateModel(
name='Category',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, unique=True)),
],
),
migrations.RemoveField(
model_name='storecategory',
name='parent_category',
),
migrations.RemoveField(
model_name='storeitem',
name='category',
),
migrations.CreateModel(
name='Item',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('price', models.DecimalField(decimal_places=2, max_digits=10)),
('item_id', models.IntegerField(unique=True)),
('image_url', models.URLField()),
('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='home.category')),
],
),
migrations.DeleteModel(
name='Cart',
),
migrations.DeleteModel(
name='StoreCategory',
),
migrations.DeleteModel(
name='StoreItem',
),
]
+11 -20
View File
@@ -62,34 +62,25 @@ class TransferPrice(models.Model):
def __str__(self):
return f"Precio de transferencia: {self.price} EUR"
class StoreCategory(models.Model):
name = models.CharField(max_length=100)
parent_category = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE)
class Category(models.Model):
name = models.CharField(max_length=100, unique=True)
def __str__(self):
return self.name
class StoreItem(models.Model):
class Item(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="items")
name = models.CharField(max_length=200)
item_id = models.IntegerField()
category = models.ForeignKey(StoreCategory, on_delete=models.CASCADE)
image_url = models.URLField()
price = models.DecimalField(max_digits=6, decimal_places=2) # Precio en euros
price = models.DecimalField(max_digits=10, decimal_places=2) # Precio en euros
item_id = models.IntegerField(unique=True) # ID del ítem en el juego
image_url = models.URLField() # URL de la imagen del ítem
def __str__(self):
return self.name
class Cart(models.Model):
username = models.CharField(max_length=150) # Para almacenar el nombre de usuario
item = models.ForeignKey(StoreItem, on_delete=models.CASCADE)
added_at = models.DateTimeField(auto_now_add=True)
def total_price(self):
return self.item.price
class RenamePrice(models.Model):
price = models.DecimalField(max_digits=5, decimal_places=2, default=2.00)
@@ -0,0 +1,149 @@
<!-- main -->
<div class="main-page">
<div class="middle-content middle-content-index">
<div class="body-content">
<div class="title-contenat"><h1>Tienda de {{ NOMBRE_SERVIDOR }}</h1></div>
<div class="box-content">
<!-- Selección de personaje -->
<div class="body-box-content">
<script src="https://js.stripe.com/v3/"></script>
<label for="personaje" class="yellow-info">Selecciona tu personaje:</label>
<select id="personaje" class="form-select">
<option value="" selected disabled>Seleccione un personaje</option>
{% for character in characters %}
<option class="priest big-font" value="{{ character }}">{{ character }}</option>
{% endfor %}
</select>
</div>
<!-- Categorías -->
<div class="body-box-content">
<label for="categoria" class="yellow-info">Selecciona una categoría:</label>
<select id="categoria" class="form-select">
{% for category in categories %}
<option value="{{ category.name }}">{{ category.name }}</option>
{% endfor %}
</select>
</div>
<!-- Lista de ítems -->
<div class="body-box-content" id="items-container"></div>
<!-- Carrito -->
<div class="title-content-h3">
<h3 class="big-font">Carrito de Compras</h3>
</div>
<div class="body-box-content">
<ul id="carrito" class="list-group"></ul>
<p><strong>Total:</strong> <span id="total">0</span> monedas</p>
<p><strong>Total con IVA (21%):</strong> <span id="totalIVA">0</span> monedas</p>
<button class="real-info-box green-info" id="comprar">Finalizar Compra</button>
</div>
</div>
</div>
</div>
</div>
<script>
const carrito = [];
const itemsContainer = document.getElementById("items-container");
const carritoLista = document.getElementById("carrito");
const totalElement = document.getElementById("total");
const totalIVAElement = document.getElementById("totalIVA");
function cargarItems(categoria) {
itemsContainer.innerHTML = "";
fetch(`/es/store-novawow/buscar/categoria/${categoria}/`)
.then(response => response.json())
.then(data => {
if (data.success) {
data.items.forEach(item => {
const itemHTML = `
<div class="body-box-content">
<div class="noticia-contenido">
<img src="${item.image_url}" alt="${item.name}" class="item-img">
<h3>${item.name}</h3>
<p>Precio: ${item.price} monedas</p>
<button class="real-info-box green-info" onclick="agregarAlCarrito(${item.id}, '${item.name}', ${item.price})">Añadir al carrito</button>
</div>
</div>
`;
itemsContainer.innerHTML += itemHTML;
});
} else {
itemsContainer.innerHTML = "No hay ítems disponibles para esta categoría.";
}
})
.catch(error => console.error("Error al cargar los ítems:", error));
}
function agregarAlCarrito(id, nombre, precio) {
const personajeSeleccionado = document.getElementById("personaje").value;
if (!personajeSeleccionado) {
alert("¡Por favor, selecciona un personaje antes de añadir un ítem al carrito!");
return;
}
carrito.push({ id, nombre, precio });
actualizarCarrito();
}
function actualizarCarrito() {
carritoLista.innerHTML = "";
let total = 0;
carrito.forEach((item, index) => {
total += item.precio;
const itemCarrito = `<li class="real-info-box">${item.nombre} - ${item.precio} monedas <button class="real-info-box red-info" onclick="eliminarDelCarrito(${index})">Eliminar</button></li>`;
carritoLista.innerHTML += itemCarrito;
});
totalElement.textContent = total;
totalIVAElement.textContent = (total * 1.21).toFixed(2);
}
function eliminarDelCarrito(index) {
carrito.splice(index, 1);
actualizarCarrito();
}
document.getElementById("categoria").addEventListener("change", (e) => {
cargarItems(e.target.value);
});
document.getElementById("comprar").addEventListener("click", () => {
const personajeSeleccionado = document.getElementById("personaje").value;
if (!personajeSeleccionado) {
alert("¡Por favor, selecciona un personaje antes de continuar!");
return;
}
if (carrito.length === 0) {
alert("El carrito está vacío");
} else {
// Crear la sesión de pago con Stripe
fetch('/es/store-novawow/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
character: personajeSeleccionado, // Añadir el personaje seleccionado
carrito: carrito, // Si necesitas enviar los ítems del carrito, también puedes incluirlos aquí
}),
})
.then(response => response.json())
.then(data => {
if (data.success) {
const stripe = Stripe(data.stripe_public_key);
stripe.redirectToCheckout({ sessionId: data.session_id });
} else {
alert(data.message);
}
})
.catch(error => console.error("Error al crear la sesión de pago:", error));
}
});
cargarItems("Bolsas");
</script>
@@ -1,34 +0,0 @@
<div class="main-page">
<div class="store-categories">
{% for category in categories %}
<h2>{{ category.name }}</h2>
<ul>
{% for item in category.storeitem_set.all %}
<li>
<img src="{{ item.image_url }}" alt="{{ item.name }}">
<p>{{ item.name }}</p>
<p>{{ item.price }} €</p>
<button class="add-to-cart" data-item-id="{{ item.id }}">Añadir</button>
</li>
{% endfor %}
</ul>
{% endfor %}
</div>
<div class="cart">
<h2>Carrito</h2>
<ul>
{% for cart_item in cart_items %}
<li>
<p>{{ cart_item.item.name }}</p>
<p>{{ cart_item.item.price }} €</p>
<button class="remove-from-cart" data-cart-id="{{ cart_item.id }}">Eliminar</button>
</li>
{% endfor %}
</ul>
<p>Total: {{ total_price }} €</p>
<script src="{{ URL_PRINCIPAL }}/static/nw-themes/nw-ryu/nw-js-handlers/store_response.js"></script>
<script src="https://js.stripe.com/v3/"></script>
<button id="checkout-button">Pagar</button>
</div>
</div>
@@ -3,7 +3,7 @@
{% include 'partials/head.html' %}
{% include 'partials/header.html' %}
{% include 'partials/video.html' %}
{% include 'partials/store_novawow.html' %}
{% include 'partials/store/store_novawow.html' %}
{% include 'partials/social.html' %}
{% include 'partials/footer.html' %}
{% include 'partials/final.html' %}
@@ -0,0 +1,9 @@
<!DOCTYPE html>
{% include 'partials/head.html' %}
{% include 'partials/header.html' %}
{% include 'partials/video.html' %}
{% include 'partials/store/store_novawow_cancel.html' %}
{% include 'partials/social.html' %}
{% include 'partials/footer.html' %}
{% include 'partials/final.html' %}
@@ -0,0 +1,9 @@
<!DOCTYPE html>
{% include 'partials/head.html' %}
{% include 'partials/header.html' %}
{% include 'partials/video.html' %}
{% include 'partials/store/store_novawow_success.html' %}
{% include 'partials/social.html' %}
{% include 'partials/footer.html' %}
{% include 'partials/final.html' %}
+3
View File
@@ -63,7 +63,10 @@ urlpatterns = [
path('transfer-character/' , views.transfer_character_view, name='transfer-character'),
path('transfer-success/' , views.transfer_success_view, name='transfer-success'),
path('transfer-cancel/' , views.transfer_cancel_view, name='transfer-cancel'),
path('store-novawow/buscar/categoria/<str:categoria>/', views.store_novawow_buscar_categoria_view, name='store_novawow/buscar/categoria/'),
path('store-novawow/' , views.store_novawow_view, name='store-novawow'),
path('store-novawow/success', views.store_novawow_success_view, name='store-novawow/success'),
path('store-novawow/cancel', views.store_novawow_cancel_view, name='store-novawow/cancel'),
path('maintenance/', views.maintenance_view, name='maintenance'),
+98 -70
View File
@@ -4,6 +4,7 @@ import gmpy2
import binascii
import os
import re
import json
from django.shortcuts import render, redirect
from django.urls import reverse
from django.http import HttpResponse, JsonResponse
@@ -12,7 +13,7 @@ from django.contrib import messages
from django.contrib.auth import logout
from django import forms
from .pagos_stripe import create_checkout_session
from .models import Noticia, ClienteCategoria, ServerSelection, RecruitAFriend, DownloadClientPage, ContentCreator, RecruitReward, ClaimedReward, AccountActivation, SecurityToken, GuildRenameSettings, VoteSite, VoteLog, HomeApiPoints, UnstuckHistory, ReviveHistory, RenamePrice, CustomizePrice, ChangeRacePrice, ChangeFactionPrice, LevelUpPrice, GoldPrice, TransferPrice, StoreCategory, StoreItem, Cart
from .models import Noticia, ClienteCategoria, ServerSelection, RecruitAFriend, DownloadClientPage, ContentCreator, RecruitReward, ClaimedReward, AccountActivation, SecurityToken, GuildRenameSettings, VoteSite, VoteLog, HomeApiPoints, UnstuckHistory, ReviveHistory, RenamePrice, CustomizePrice, ChangeRacePrice, ChangeFactionPrice, LevelUpPrice, GoldPrice, TransferPrice, Category, Item
from django.views.decorators.csrf import csrf_exempt
import logging
from datetime import datetime, timedelta
@@ -1164,7 +1165,7 @@ def rename_character_view(request):
price_obj = RenamePrice.objects.first()
price = price_obj.price if price_obj else 2.00
# Ejecutar el comando SOAP
# Ejecutar el comando SOAP De prueba haber si el servidor funciona antes de proceder al pago
command = f".server info"
response = execute_soap_command(command)
@@ -1723,6 +1724,8 @@ def transfer_character_view(request):
return render(request, 'account/transfer_character.html', {'characters': characters, 'price': price})
def transfer_success_view(request):
transfer_data = request.session.get('transfer_data')
if not transfer_data:
@@ -1745,88 +1748,113 @@ def transfer_success_view(request):
def transfer_cancel_view(request):
return render(request, 'account/transfer_cancel.html')
from decimal import Decimal
from django.http import JsonResponse
def store_novawow_success_view(request):
return render(request, 'store/store_novawow_success.html')
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")
categories = StoreCategory.objects.filter(parent_category=None)
items = StoreItem.objects.all()
cart_items = Cart.objects.filter(username=username)
# 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()]
total_price = sum([item.total_price() for item in cart_items])
# 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:
items_in_category = Category.objects.get(name=category_name).items.values()
return JsonResponse({'success': True, 'items': list(items_in_category)})
return render(request, 'account/store_novawow.html', {
# Si el carrito está en la sesión
carrito = request.session.get('carrito', [])
total_price = sum(item['price'] for item in carrito) * Decimal('1.21') # Incluir IVA
if request.method == 'POST':
# Obtener datos del body
data = json.loads(request.body)
character_name = data.get('character')
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 renombrar este personaje.'})
# 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'))
stripe_response = create_checkout_session(total_price, success_url=success_url, cancel_url=cancel_url)
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,
'items': items,
'cart_items': cart_items,
'total_price': total_price,
})
def add_to_cart(request):
username = request.session.get('username')
if not username:
return redirect('login')
if request.method == 'POST':
item_id = request.POST.get('item_id')
item = StoreItem.objects.get(id=item_id)
Cart.objects.create(username=username, item=item)
return JsonResponse({'success': True, 'message': f'{item.name} añadido al carrito.'})
return JsonResponse({'success': False, 'message': 'Error al añadir al carrito.'})
def remove_from_cart(request):
username = request.session.get('username')
if not username:
return redirect('login')
if request.method == 'POST':
cart_id = request.POST.get('cart_id')
Cart.objects.filter(id=cart_id, username=username).delete()
return JsonResponse({'success': True, 'message': 'Artículo eliminado del carrito.'})
return JsonResponse({'success': False, 'message': 'Error al eliminar del carrito.'})
def checkout_view(request):
username = request.session.get('username')
if not username:
return redirect('login')
cart_items = Cart.objects.filter(username=username)
total_price = sum([item.total_price() for item in cart_items])
success_url = request.build_absolute_uri(reverse('checkout_success'))
cancel_url = request.build_absolute_uri(reverse('checkout_cancel'))
stripe_response = create_checkout_session(total_price, success_url, cancel_url)
if stripe_response["success"]:
return JsonResponse({'success': True, 'session_id': stripe_response["session_id"]})
'total_price': total_price
})
return JsonResponse({'success': False, 'message': 'Error al procesar el pago.'})
def store_novawow_success_view(request):
# Procesar el comando SOAP tras el pago exitoso
character_name = request.session.get('store_novawow')
if not character_name:
return redirect('store-novawow')
def checkout_success(request):
username = request.session.get('username')
if not username:
return redirect('login')
command = f".send item {character_name}"
response = execute_soap_command(command)
cart_items = Cart.objects.filter(username=username)
if response is None or response.strip() == "":
message = f"El personaje {character_name} se a anadido los items de la compra."
request.session.pop('store_novawow', None) # Limpiar la sesión
return render(request, 'store/store_success.html', {'message': message})
else:
return render(request, 'store/store_success.html', {'message': f"Error: {response}"})
for cart_item in cart_items:
command = f".send item {cart_item.item.item_id} {cart_item.item.name} {username}"
execute_soap_command(command)
cart_item.delete()
return render(request, 'account/checkout_success.html', {'message': 'Compra procesada con éxito.'})
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)
def checkout_cancel(request):
username = request.session.get('username')
if not username:
return redirect('login')
return render(request, 'account/checkout_cancel.html', {'message': 'Compra cancelada.'})
def revive_character_view(request):
username = request.session.get('username')
@@ -0,0 +1,91 @@
$(document).ready(function () {
$("#comprar").on("click", async function (e) {
e.preventDefault();
$("#store-response").empty();
let button = $(this);
let originalText = button.html();
button.html("Procesando pago...");
button.prop("disabled", true);
let personaje = $("#personaje").val();
if (!personaje) {
$("#store-response").append('<span class="red-form-response">Selecciona un personaje antes de comprar.</span>').hide().slideDown();
button.html(originalText);
button.prop("disabled", false);
return;
}
let carrito = [];
$("#carrito li").each(function () {
let itemData = $(this).data("item");
carrito.push({
item_id: itemData.item_id,
name: itemData.name,
price: itemData.price
});
});
if (carrito.length === 0) {
$("#store-response").append('<span class="red-form-response">El carrito está vacío.</span>').hide().slideDown();
button.html(originalText);
button.prop("disabled", false);
return;
}
// Realizar la solicitud para crear la sesión de pago en el backend
try {
const response = await fetch("{% url 'create_checkout_session' %}", {
method: "POST",
body: new URLSearchParams({
personaje: personaje,
carrito: JSON.stringify(carrito)
})
});
const data = await response.json();
if (data.success) {
window.location.href = data.stripe_url; // Redirigir a la página de pago de Stripe
} else {
$("#store-response").append(`<span class="red-form-response">${data.message}</span>`).hide().slideDown();
}
} catch (error) {
console.error("Error al procesar el pago:", error);
$("#store-response").append('<span class="red-form-response">Error al procesar el pago.</span>').hide().slideDown();
}
button.html(originalText);
button.prop("disabled", false);
});
});
$("#categoria").on("change", function () {
let categoriaSeleccionada = $(this).val();
if (categoriaSeleccionada) {
$.ajax({
url: "{% url 'get_items_by_category' %}",
method: "GET",
data: { category: categoriaSeleccionada },
success: function (data) {
$("#items-container").empty();
if (data.items.length > 0) {
data.items.forEach(function (item) {
let itemHtml = `
<div class="item">
<img src="${item.image_url}" alt="${item.name}" />
<h4>${item.name}</h4>
<p>Price: ${item.price} EUR</p>
<button class="add-to-cart" data-item-id="${item.item_id}">Add to Cart</button>
</div>
`;
$("#items-container").append(itemHtml);
});
} else {
$("#items-container").append('<p>No items found in this category.</p>');
}
},
error: function () {
alert("Error loading items.");
}
});
}
});