feat: Add transaction code generation for orders and update receipt templates
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
# Generated by Django 6.0.1 on 2026-04-09
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.utils.crypto import get_random_string
|
||||
|
||||
|
||||
TRANSACTION_CODE_PREFIX = "comal-"
|
||||
TRANSACTION_CODE_LENGTH = 32
|
||||
TRANSACTION_CODE_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
|
||||
|
||||
def populate_transaction_codes(apps, schema_editor):
|
||||
Order = apps.get_model("tienda", "Order")
|
||||
|
||||
for order in Order.objects.filter(transaction_code__isnull=True):
|
||||
while True:
|
||||
code = f"{TRANSACTION_CODE_PREFIX}{get_random_string(TRANSACTION_CODE_LENGTH, TRANSACTION_CODE_ALPHABET)}"
|
||||
if not Order.objects.filter(transaction_code=code).exists():
|
||||
order.transaction_code = code
|
||||
order.save(update_fields=["transaction_code"])
|
||||
break
|
||||
|
||||
|
||||
def noop_reverse(apps, schema_editor):
|
||||
pass
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("tienda", "0002_verificationcode_code_mode_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="order",
|
||||
name="transaction_code",
|
||||
field=models.CharField(blank=True, db_index=True, max_length=38, null=True, unique=True),
|
||||
),
|
||||
migrations.RunPython(populate_transaction_codes, noop_reverse),
|
||||
]
|
||||
+2
-1
@@ -15,7 +15,7 @@ class Recibo(FPDF):
|
||||
self.set_font('Arial', 'I', 8)
|
||||
self.cell(0, 10, f'Pagina {self.page_no()}', align='C')
|
||||
|
||||
def generar_recibo(cliente: str, total: float, objetos: list, metodo_pago: str):
|
||||
def generar_recibo(cliente: str, total: float, objetos: list, metodo_pago: str, transaction_code: str):
|
||||
pdf = Recibo()
|
||||
font_path = "/fonts/Roboto-Regular.ttf"
|
||||
pdf.add_font('Roboto', '', '/fonts/Roboto-Regular.ttf')
|
||||
@@ -24,6 +24,7 @@ def generar_recibo(cliente: str, total: float, objetos: list, metodo_pago: str):
|
||||
pdf.set_font('Roboto', size=12)
|
||||
|
||||
pdf.cell(0, 10, f"Cliente: {cliente}", ln=True)
|
||||
pdf.cell(0, 10, f"ID de transaccion: {transaction_code}", ln=True)
|
||||
pdf.cell(0, 10, f"")
|
||||
|
||||
DATA = []
|
||||
|
||||
+8
-2
@@ -57,12 +57,18 @@ def enviar_correo_recuperacion(email: str):
|
||||
|
||||
# Purchased items should be a list of dictionary, the dictionary must follow this tags: amount, product name, price (each)
|
||||
@shared_task
|
||||
def process_purchase(user_id: int, purchased_items: list, payment_method: str):
|
||||
def process_purchase(user_id: int, purchased_items: list, payment_method: str, transaction_code: str):
|
||||
user = User.objects.get(id=user_id)
|
||||
total = 0
|
||||
for i in purchased_items:
|
||||
total += i["price"]*i["amount"]
|
||||
pdf_data = pdf.generar_recibo(user.get_full_name(), total, purchased_items, payment_method)
|
||||
pdf_data = pdf.generar_recibo(
|
||||
user.get_full_name(),
|
||||
total,
|
||||
purchased_items,
|
||||
payment_method,
|
||||
transaction_code,
|
||||
)
|
||||
|
||||
email = EmailMessage(
|
||||
subject="Tu recibo de compra",
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
<div class="alert alert-success p-5">
|
||||
<h2 class="mb-3">¡Pago completado!</h2>
|
||||
<p class="mb-4">Tu pedido ha sido procesado correctamente.</p>
|
||||
{% if order and order.transaction_code %}
|
||||
<p class="mb-4"><strong>ID de transacción:</strong> {{ order.transaction_code }}</p>
|
||||
{% endif %}
|
||||
<a href="{% url 'index' %}" class="btn btn-primary">Volver a la tienda</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Recibo #</th>
|
||||
<th>ID Transacción</th>
|
||||
<th>Fecha</th>
|
||||
<th>Total</th>
|
||||
<th>Método</th>
|
||||
@@ -44,6 +45,7 @@
|
||||
{% for receipt in receipts %}
|
||||
<tr>
|
||||
<td>{{ receipt.id }}</td>
|
||||
<td>{{ receipt.transaction_code|default:"-" }}</td>
|
||||
<td>{{ receipt.created_at|date:"d/m/Y H:i" }}</td>
|
||||
<td>{{ receipt.total }}€</td>
|
||||
<td>{{ receipt.get_payment_method_display }}</td>
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
PAGE_SIZE = 20
|
||||
VAT_RATE = 0.21 # IVA 21%
|
||||
STOCK_RESERVATION_MINUTES = 5
|
||||
|
||||
TRANSACTION_CODE_PREFIX = "comal-"
|
||||
TRANSACTION_CODE_LENGTH = 32
|
||||
TRANSACTION_CODE_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
|
||||
# Restricciones de envío
|
||||
SHIPPING_COUNTRY = "España"
|
||||
|
||||
+15
-7
@@ -4,19 +4,22 @@ from django.contrib.auth import authenticate, login as auth_login, logout as aut
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib import messages
|
||||
from .models import User, Product, Category, Cart, CartItem, Image, Order, OrderItem, OrderMessage, ShippingAddress, VerificationCode
|
||||
from .models import User, Product, Category, Cart, CartItem, Image, Order, OrderItem, OrderMessage, ShippingAddress, StockReservation, StockReservationItem, VerificationCode
|
||||
from . import tasks
|
||||
from .vars import (
|
||||
PAGE_SIZE,
|
||||
VAT_RATE,
|
||||
SHIPPING_COUNTRY,
|
||||
ALMERIA_POSTAL_CODE_PREFIX,
|
||||
ALMERIA_MUNICIPALITIES_DISPLAY
|
||||
ALMERIA_MUNICIPALITIES_DISPLAY,
|
||||
STOCK_RESERVATION_MINUTES,
|
||||
)
|
||||
from django.conf import settings
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from decimal import Decimal, ROUND_HALF_UP
|
||||
from datetime import timedelta
|
||||
import stripe
|
||||
from django.db import models, transaction
|
||||
from django.core.cache import cache
|
||||
@@ -373,7 +376,12 @@ def create_order_from_cart(request, payment_method, payment_reference="", shippi
|
||||
cart.items.all().delete()
|
||||
|
||||
if request.user.is_authenticated and purchased_items:
|
||||
tasks.process_purchase.delay(request.user.id, purchased_items, payment_method)
|
||||
tasks.process_purchase.delay(
|
||||
request.user.id,
|
||||
purchased_items,
|
||||
payment_method,
|
||||
order.transaction_code,
|
||||
)
|
||||
|
||||
return order
|
||||
|
||||
@@ -785,13 +793,13 @@ def checkout_success(request: HttpRequest):
|
||||
payment_reference = request.session.get('stripe_session_id', "")
|
||||
shipping_address_id = request.session.get('selected_shipping_address_id')
|
||||
shipping_address = ShippingAddress.objects.filter(id=shipping_address_id, user=request.user).first()
|
||||
create_order_from_cart(request, Order.PAYMENT_STRIPE, payment_reference, shipping_address)
|
||||
order = create_order_from_cart(request, Order.PAYMENT_STRIPE, payment_reference, shipping_address)
|
||||
if 'stripe_session_id' in request.session:
|
||||
del request.session['stripe_session_id']
|
||||
if 'selected_shipping_address_id' in request.session:
|
||||
del request.session['selected_shipping_address_id']
|
||||
messages.success(request, "Pago realizado correctamente. ¡Gracias por tu compra!")
|
||||
return render(request, "tienda/checkout_success.html", {})
|
||||
return render(request, "tienda/checkout_success.html", {"order": order})
|
||||
|
||||
|
||||
def checkout_cancel(request: HttpRequest):
|
||||
@@ -957,7 +965,7 @@ def paypal_execute(request: HttpRequest):
|
||||
# Pago exitoso - crear pedido y limpiar el carrito
|
||||
shipping_address_id = request.session.get('selected_shipping_address_id')
|
||||
shipping_address = ShippingAddress.objects.filter(id=shipping_address_id, user=request.user).first()
|
||||
create_order_from_cart(request, Order.PAYMENT_PAYPAL, payment_id, shipping_address)
|
||||
order = create_order_from_cart(request, Order.PAYMENT_PAYPAL, payment_id, shipping_address)
|
||||
|
||||
# Limpiar la sesión
|
||||
if 'paypal_payment_id' in request.session:
|
||||
@@ -966,7 +974,7 @@ def paypal_execute(request: HttpRequest):
|
||||
del request.session['selected_shipping_address_id']
|
||||
|
||||
messages.success(request, "¡Pago realizado correctamente con PayPal! Gracias por tu compra.")
|
||||
return render(request, "tienda/checkout_success.html", {})
|
||||
return render(request, "tienda/checkout_success.html", {"order": order})
|
||||
else:
|
||||
error_message = payment.error.get("message", "Error desconocido")
|
||||
messages.error(request, f"Error al procesar el pago: {error_message}")
|
||||
|
||||
Reference in New Issue
Block a user