Cheaf Docs
Orders

Pre-Orden (Beta)

Documentación del proceso de creación de una pre-orden en el sistema

Guía de Operaciones - Pre-Ventas (Pre-Sale)

📋 Índice

  1. ¿Qué es Pre-Venta?
  2. ¿Cómo Funciona?
  3. Configuración de Pre-Venta
  4. Subida de Stock
  5. Procesamiento de Pre-Órdenes
  6. Cancelación Automática de Pre-Órdenes Expiradas
  7. Monitoreo y Reportes

🎯 ¿Qué es Pre-Venta?

La Pre-Venta es una funcionalidad que permite a los usuarios hacer pedidos antes de que el stock esté disponible. Esto es especialmente útil para tiendas como Krispy Kreme que reciben stock fresco diariamente.

Beneficios

  • ✅ Para el Usuario: Asegura su pedido antes de que se agote el stock
  • ✅ Para la Tienda: Conoce la demanda antes de preparar el stock
  • ✅ Para Cheaf: Mejora la experiencia del usuario y reduce cancelaciones

🔄 ¿Cómo Funciona?

Flujo General

1. Se configura las tiendas con la cantidad de stock que se subirá en preventa
   ↓
2. Cron job ejecuta comando "upload_presale_stock"
   ↓
3. Sistema sube stock SOLO a tiendas con configuración de pre-venta
   ↓
4. Usuario ve tienda con "Pre-Venta Disponible" y stock disponible
   ↓
5. Usuario hace pedido (stock > 0)
   ↓
6. Si es dentro de ventana de pre-venta, se crea como "Pre-Orden"
   ↓
7. Pre-orden queda PENDIENTE (sin cobro, sin delivery, sin Deliverect)
   → Orden con status "Requested" (3)
   ↓
8. Krispy Kreme sube stock REAL mediante su sistema
   ↓
9. Sistema procesa pre-órdenes automáticamente (FIFO):
   - Cobra al usuario
   - Configura delivery (si aplica)
   - Envía a Deliverect (si aplica)
   → Orden permanece en status "Requested" (3)
   ↓
10. Tienda cambia manualmente el status a "Booked" cuando está lista

Notas Importantes:

  • El cobro NO es inmediato. Se cobra cuando Krispy Kreme sube el stock real.
  • El delivery y Deliverect se procesan cuando se sube el stock real, no al crear la pre-orden.
  • Los cupones y créditos se aplican de manera normal al crear la pre-orden (no se difieren).
  • Las órdenes en pre-venta se marcan con la bandera is_presale = True para identificarlas fácilmente en queries y reportes.

Ventana de Pre-Venta

La ventana de pre-venta se cierra automáticamente cuando la tienda llega a su hora de apertura (collect_after).

  • Ventana Abierta: Los usuarios pueden hacer pre-órdenes

    • Desde: presale_opening_time (ej: 4:00 PM)
    • Hasta: collect_after (hora de apertura de la tienda, ej: 10:00 AM del día siguiente)
  • Ventana Cerrada: Los usuarios NO pueden hacer pre-órdenes

    • Cuando: current_time >= collect_after (la tienda ya abrió)
    • Las órdenes normales siguen procesándose sin problema
    • Solo la opción de pre-venta aparece cerrada en el frontend

Ejemplo de Timeline:

16:00 (4 PM)  → Pre-venta ABIERTA ✅
22:00 (10 PM) → Pre-venta ABIERTA ✅
09:59 AM      → Pre-venta ABIERTA ✅ (tienda aún no abre)
10:00 AM      → Pre-venta CERRADA ❌ (tienda abrió - collect_after)
11:00 AM      → Pre-venta CERRADA ❌ (órdenes normales disponibles)

⚙️ Configuración de Pre-Venta

Paso 1: Habilitar Pre-Venta en una Tienda

  1. Ir al Admin de Django → Restaurants → Stores

  2. Buscar y seleccionar la tienda que deseas configurar

  3. Dentro de la pantalla de la tienda, ir a la sección "Pre Sale Configurations"

  4. Configurar los campos:

    • Presale Opening Time: Hora de apertura (formato 24h, ej: 16:00:00)
    • Enabled: ✅ Marcar para activar
  5. Guardar

Paso 2: Configurar Stock por Menú

  1. Ir al Admin de Django → Restaurants → Menus

  2. Buscar el menú de la tienda

  3. Editar el menú:

    • Presale Stock Amount: Cantidad de stock que se subirá (ej: 50)
    • Si es 0, ese menú NO participará en pre-venta
  4. Guardar

Ejemplo de Configuración

Tienda: Krispy Kreme Centro

  • Presale Opening Time: 16:00:00 (4:00 PM)
  • Enabled: ✅ Sí

Menús:

  • Docena Original Glazed → Presale Stock Amount: 50
  • Media Docena Surtida → Presale Stock Amount: 30
  • Café Americano → Presale Stock Amount: 0 (no participa)

📦 Subida de Stock

Proceso de Subida de Stock

Importante: Krispy Kreme sube sus paquetes mediante su propio sistema interno. El comando upload_presale_stock NO crea los paquetes, solo:

  • ✅ Toma el stock configurado en presale_stock_amount
  • ✅ Lo agrega al stock actual del menú
  • ✅ Solo afecta tiendas con configuración de pre-venta activa

Opción 1: Subida Automática (Recomendado)

El comando se ejecuta automáticamente cada 15 minutos mediante un cron job. Esto permite detectar la ventana de apertura de pre-venta de cada tienda.

Configuración en Cron:

# Ejecuta cada 15 minutos para detectar ventanas de apertura
*/15 * * * * /path/to/command upload_presale_stock

¿Cómo funciona?

  • El cron se ejecuta cada 15 minutos
  • Verifica qué tiendas tienen su ventana de pre-venta abierta
  • Solo procesa tiendas con PreSaleConfiguration activa (enabled = True)
  • Solo afecta menús con presale_stock_amount > 0

Opción 2: Subida Manual

Usar el comando manual cuando sea necesario:

# Subir stock para todas las tiendas configuradas
make manage cmd="upload_presale_stock --force"

# Subir stock para una tienda específica
make manage cmd="upload_presale_stock --force --store-id 123"

# Modo prueba (no hace cambios reales)
make manage cmd="upload_presale_stock --force --dry-run"

# Sin enviar notificaciones a favoritos
make manage cmd="upload_presale_stock --force --skip-favorites"

¿Qué Hace la Subida de Stock?

  1. ✅ Identifica tiendas elegibles: Solo tiendas con PreSaleConfiguration activa
  2. ✅ Agrega stock: Suma el presale_stock_amount al stock actual de cada menú
  3. ✅ Registra cambios: Crea entrada en historial (RestaurantMenuLog)
  4. ✅ Actualiza Firestore: Sincroniza con la app móvil
  5. ✅ Envía notificaciones: Push a usuarios con la tienda en favoritos

Ejemplo:

  • Stock actual: 10 unidades
  • Presale Stock Amount: 50 unidades
  • Resultado: Stock nuevo = 60 unidades

🔄 Procesamiento de Pre-Órdenes

¿Cuándo se Procesan?

Las pre-órdenes se procesan automáticamente cuando Krispy Kreme sube stock real mediante su sistema interno.

¿Qué Sucede al Procesar?

Cuando se procesa una pre-orden, el sistema ejecuta:

  1. ✅ Cobra al usuario con el token de pago guardado
  2. ✅ Configura delivery si el pedido es para entrega
  3. ✅ Envía a Deliverect si la tienda tiene integración activa

Importante:

  • El usuario NO recibe notificación cuando se procesa la pre-orden.
  • Las órdenes se crean con status "Requested" (3) y permanecen en ese status después de procesarse.

Orden de Procesamiento

Las pre-órdenes se procesan en orden FIFO (First In, First Out):

  • La primera pre-orden creada se procesa primero
  • La segunda pre-orden se procesa después, y así sucesivamente
  • Esto garantiza equidad para todos los usuarios

Estados de Pre-Orden

Estado Pre-OrdenStatus OrdenDescripciónAcción
PendingRequested (3)En espera de stock realNinguna - Pre-orden en cola
ProcessingRequested (3)Siendo procesadaCobrando y configurando delivery
CompletedRequested (3)Procesada exitosamente✅ Orden lista
Failed - PaymentRequested (3)Falló el pago❌ Se envía email a soporte
Failed - DeliveryRequested (3)Falló el delivery❌ Se envía email a soporte
Failed - ProcessingRequested (3)Error general❌ Se envía email a soporte

Nota: Todas las pre-órdenes mantienen el status "Requested" (3) independientemente de su estado de procesamiento. La tienda debe cambiar manualmente el status a "Booked" cuando el pedido esté listo para recoger.


❌ Cancelación Automática de Pre-Órdenes Expiradas

Resumen

El comando cancel_expired_preorders cancela automáticamente las pre-órdenes que permanecen pendientes 30 minutos después de la hora de apertura real de la tienda (collect_after). Se ejecuta cada 15 minutos para garantizar la cancelación oportuna.

Comando: cancel_expired_preorders

Ubicación: app/orders/management/commands/cancel_expired_preorders.py

Frecuencia: Cada 15 minutos vía cron job

¿Cómo Funciona?

1. Selección de Tiendas

  • Encuentra todas las tiendas con pre-órdenes pendientes (PreOrder.status = 'pending')
  • Obtiene el horario de la tienda para hoy
  • Recupera la hora collect_after (hora de apertura real)

2. Cálculo de Expiración

  • Tiempo de Expiración = collect_after + 30 minutos (en zona horaria de la tienda)
  • Lógica de Cancelación = Retroactiva (cancela TODAS las pre-órdenes expiradas)
    • Si hora actual > tiempo de expiración → Cancelar
    • Sin restricción de ventana de tiempo → Captura cualquier cancelación perdida

3. Cancelación de Pre-Orden

Para cada pre-orden expirada:

  • Cambia PreOrder.status a failed_processing
  • Establece PreOrder.processed_at a la marca de tiempo actual
  • Cambia Order.status a 0 (Cancelada)
  • Registra la cancelación para auditoría

4. Lógica de Cancelación Retroactiva

Ejemplo de Timeline:

Tienda abre (collect_after): 10:00 AM (zona horaria de la tienda)
Tiempo de expiración: 10:30 AM (collect_after + 30 min)

El cron se ejecuta a las:
- 10:15 AM ✗ Aún no expirado (15 min después de abrir)
- 10:30 AM ✓ Expirado, cancela pre-órdenes
- 10:45 AM ✓ Aún expirado, cancela las restantes
- 11:00 AM ✓ Aún expirado, cancela las restantes
- 12:00 PM ✓ Aún expirado, cancela las restantes (retroactivo)

Punto Clave: El comando es retroactivo - cancela TODAS las pre-órdenes pendientes que pasaron el tiempo de expiración, sin importar cuándo se ejecute el cron.

Argumentos del Comando

--force

Fuerza la cancelación sin importar el tiempo (para pruebas)

make manage cmd="cancel_expired_preorders --force"

--store-id <ID>

Procesa solo una tienda específica (para pruebas)

make manage cmd="cancel_expired_preorders --store-id 123"

--dry-run

Simula el comando sin hacer cambios reales

make manage cmd="cancel_expired_preorders --dry-run"

Argumentos Combinados

# Probar tienda específica sin hacer cambios
make manage cmd="cancel_expired_preorders --store-id 123 --dry-run"

# Forzar cancelación para tienda específica
make manage cmd="cancel_expired_preorders --store-id 123 --force"

Configuración del Cron Job

Agregar al crontab para ejecutar cada 15 minutos:

# Editar crontab
crontab -e

# Agregar esta línea (ajustar ruta según sea necesario)
*/15 * * * * cd /path/to/cheaf-admin && /path/to/venv/bin/python manage.py cancel_expired_preorders >> /var/log/cheaf/cancel_expired_preorders.log 2>&1

¿Por Qué Cancelar Después de 30 Minutos?

Las pre-órdenes que permanecen pendientes 30 minutos después de que la tienda abre indican:

  1. ❌ Stock no subido - La tienda no recibió stock
  2. ❌ Procesamiento fallido - El procesador de pre-órdenes encontró un error
  3. ❌ Tienda cerrada - La tienda no abrió según lo programado

En todos los casos, es mejor cancelar y permitir el reembolso que dejar a los clientes esperando indefinidamente.

Logs y Monitoreo

Niveles de Log:

  • INFO: Operación normal, cancelaciones de pre-órdenes
  • WARNING: Tiendas omitidas, sin horario o collect_after
  • ERROR: Fallos en el proceso de cancelación

Ejemplos de Logs:

INFO: Cancel expired pre-orders command started. Force: False, Store ID: None, Dry Run: False
INFO: Store 123 (Krispy Kreme Centro) collect_after: 10:00:00, expiration: 10:30:00
INFO: Cancelled expired PreOrder #456 (Order #789) for store 123
INFO: Cancel expired pre-orders command completed. Processed: 1, Cancelled: 3, Errors: 0

📊 Monitoreo y Reportes

Ver Pre-Órdenes en el Admin

  1. Ir al Admin de Django → Orders → Pre orders
  2. Filtrar por:
    • Status: Estado de la pre-orden (Pending, Completed, Failed, etc.)
    • Payment provider: Proveedor de pago (Conekta, MercadoPago, Stripe)
    • Created at: Fecha de creación
  3. Buscar por:
    • ID de pre-orden
    • ID de orden
    • ID del usuario

Información Importante

Cada pre-orden muestra:

  • Order: Número de orden asociada
  • Status: Estado actual
  • Created At: Cuándo se creó
  • Processed At: Cuándo se procesó (si aplica)
  • Payment Provider: Proveedor de pago (Conekta, MercadoPago, etc.)