feat: Add stock management features including stock reservations and updates to cart and checkout processes
This commit is contained in:
@@ -12,6 +12,17 @@
|
||||
{% if cart_items %}
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-8">
|
||||
{% if stock_issues %}
|
||||
<div class="alert alert-warning">
|
||||
<strong>Hay productos con stock insuficiente:</strong>
|
||||
<ul class="mb-0 mt-2">
|
||||
{% for issue in stock_issues %}
|
||||
<li>{{ issue.product_name }} - disponible: {{ issue.available }}, en carrito: {{ issue.requested }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div class="mt-2">Actualiza las cantidades antes de continuar al pago.</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<table class="table">
|
||||
@@ -20,6 +31,7 @@
|
||||
<th>Producto</th>
|
||||
<th>Precio (sin IVA)</th>
|
||||
<th>Cantidad</th>
|
||||
<th>Stock</th>
|
||||
<th>Subtotal (con IVA)</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
@@ -39,10 +51,17 @@
|
||||
<td>
|
||||
<form method="post" action="{% url 'update_cart_item' item.id %}" class="d-flex align-items-center" style="max-width: 150px;">
|
||||
{% csrf_token %}
|
||||
<input type="number" name="quantity" value="{{ item.quantity }}" min="1" class="form-control form-control-sm me-2" style="width: 70px;">
|
||||
<input type="number" name="quantity" value="{{ item.quantity }}" min="1" max="{{ item.product.stock }}" class="form-control form-control-sm me-2" style="width: 70px;">
|
||||
<button type="submit" class="btn btn-sm btn-primary">Actualizar</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
{% if item.product.stock > 0 %}
|
||||
{{ item.product.stock }}
|
||||
{% else %}
|
||||
<span class="text-danger">0</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="price">{{ item.get_subtotal_with_vat|format_price }} €</td>
|
||||
<td>
|
||||
<form method="post" action="{% url 'remove_from_cart' item.id %}" style="display: inline;">
|
||||
@@ -82,7 +101,7 @@
|
||||
<strong class="price">{{ cart.get_total_with_vat|format_price }} €</strong>
|
||||
</div>
|
||||
<div class="d-grid gap-2">
|
||||
<a href="{% url 'checkout' %}" class="btn btn-primary btn-lg">Proceder al Pago</a>
|
||||
<a href="{% url 'checkout' %}" class="btn btn-primary btn-lg {% if stock_issues %}disabled{% endif %}" {% if stock_issues %}aria-disabled="true"{% endif %}>Proceder al Pago</a>
|
||||
<a href="{% url 'index' %}" class="btn btn-outline-secondary">Continuar Comprando</a>
|
||||
<form method="post" action="{% url 'clear_cart' %}">
|
||||
{% csrf_token %}
|
||||
|
||||
@@ -49,6 +49,23 @@
|
||||
</div>
|
||||
|
||||
{% if cart_items %}
|
||||
{% if stock_issues %}
|
||||
<div class="alert alert-warning">
|
||||
<strong>No hay stock suficiente para algunos productos:</strong>
|
||||
<ul class="mb-0 mt-2">
|
||||
{% for issue in stock_issues %}
|
||||
<li>{{ issue.product_name }} - disponible: {{ issue.available }}, en carrito: {{ issue.requested }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div class="mt-2">Vuelve al carrito y ajusta las cantidades antes de pagar.</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="alert alert-info">
|
||||
Al pulsar en pagar se reservará tu stock durante <strong>{{ reservation_minutes }} minutos</strong>.
|
||||
Si el pago no se completa en ese tiempo, la reserva se cancelará automáticamente.
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title mb-3">1) Selecciona la dirección de envío</h5>
|
||||
@@ -80,6 +97,7 @@
|
||||
<th>Producto</th>
|
||||
<th class="text-end">Precio (sin IVA)</th>
|
||||
<th class="text-end">Cantidad</th>
|
||||
<th class="text-end">Stock actual</th>
|
||||
<th class="text-end">Subtotal (con IVA)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -89,6 +107,7 @@
|
||||
<td>{{ item.product.name }}</td>
|
||||
<td class="text-end">{{ item.product.price|format_price }}€</td>
|
||||
<td class="text-end">{{ item.quantity }}</td>
|
||||
<td class="text-end">{{ item.product.stock }}</td>
|
||||
<td class="text-end">{{ item.get_subtotal_with_vat|format_price }}€</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@@ -118,7 +137,7 @@
|
||||
class="btn btn-primary payment-btn"
|
||||
data-config-url="/tienda/config/"
|
||||
data-session-url="/tienda/create-checkout-session/"
|
||||
{% if not addresses %}disabled{% endif %}>
|
||||
{% if not addresses or stock_issues %}disabled{% endif %}>
|
||||
💳 Pagar con Stripe
|
||||
</button>
|
||||
|
||||
@@ -126,7 +145,7 @@
|
||||
id="paypal-button"
|
||||
class="btn btn-warning payment-btn"
|
||||
data-payment-url="{% url 'create_paypal_payment' %}"
|
||||
{% if not addresses %}disabled{% endif %}>
|
||||
{% if not addresses or stock_issues %}disabled{% endif %}>
|
||||
🅿️ Pagar con PayPal
|
||||
</button>
|
||||
</div>
|
||||
@@ -139,7 +158,9 @@
|
||||
|
||||
<script>
|
||||
// Manejo del botón de PayPal
|
||||
document.getElementById('paypal-button').addEventListener('click', async function(e) {
|
||||
const paypalButton = document.getElementById('paypal-button');
|
||||
if (paypalButton) {
|
||||
paypalButton.addEventListener('click', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const shippingAddressSelect = document.getElementById('shipping-address');
|
||||
@@ -202,5 +223,6 @@
|
||||
button.innerHTML = originalText;
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -46,6 +46,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stock -->
|
||||
<div class="mb-3">
|
||||
<label for="stock" class="form-label">Stock disponible <span class="text-danger">*</span></label>
|
||||
<input type="number" class="form-control" id="stock" name="stock" required
|
||||
min="0" step="1" placeholder="0">
|
||||
<div class="form-text">Cantidad máxima que podrán comprar los clientes.</div>
|
||||
</div>
|
||||
|
||||
<!-- Categoría -->
|
||||
<div class="mb-3">
|
||||
<label for="category" class="form-label">Categoría <span class="text-danger">*</span></label>
|
||||
|
||||
@@ -46,6 +46,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stock -->
|
||||
<div class="mb-3">
|
||||
<label for="stock" class="form-label">Stock disponible <span class="text-danger">*</span></label>
|
||||
<input type="number" class="form-control" id="stock" name="stock" required
|
||||
min="0" step="1" value="{{ producto.stock }}" placeholder="0">
|
||||
<div class="form-text">Cantidad máxima que podrán comprar los clientes.</div>
|
||||
</div>
|
||||
|
||||
<!-- Categoría -->
|
||||
<div class="mb-3">
|
||||
<label for="category" class="form-label">Categoría <span class="text-danger">*</span></label>
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
<th>Categoría</th>
|
||||
<th class="text-end">Precio (sin IVA)</th>
|
||||
<th class="text-end">Precio (con IVA)</th>
|
||||
<th class="text-end">Stock</th>
|
||||
<th class="text-end">Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -53,6 +54,7 @@
|
||||
<td>{{ producto.category.name }}</td>
|
||||
<td class="text-end">{{ producto.price|format_price }}€</td>
|
||||
<td class="text-end text-success"><strong>{{ producto.get_price_with_vat|format_price }}€</strong></td>
|
||||
<td class="text-end">{{ producto.stock }}</td>
|
||||
<td class="text-end">
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<a href="{% url 'editar_producto' producto.id %}" class="btn btn-outline-primary btn-sm">Editar</a>
|
||||
|
||||
@@ -39,14 +39,21 @@
|
||||
<div id="descripcion">
|
||||
{{ product.briefdesc }}
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
{% if product.stock > 0 %}
|
||||
<span class="badge bg-success">Stock disponible: {{ product.stock }}</span>
|
||||
{% else %}
|
||||
<span class="badge bg-danger">Sin stock</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<form method="post" action="{% url 'add_to_cart' product.id %}" class="mt-4">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<label for="quantity" class="form-label">Cantidad:</label>
|
||||
<input type="number" name="quantity" id="quantity" value="1" min="1" class="form-control" style="max-width: 100px;">
|
||||
<input type="number" name="quantity" id="quantity" value="1" min="1" max="{{ product.stock }}" class="form-control" style="max-width: 100px;" {% if product.stock == 0 %}disabled{% endif %}>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-lg">🛒 Agregar al Carrito</button>
|
||||
<button type="submit" class="btn btn-primary btn-lg" {% if product.stock == 0 %}disabled{% endif %}>🛒 Agregar al Carrito</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user