Files
shopit-back/app/Domains/Ticketing/Ticket/documentacion

Dominio Ticket

Propósito

Genera, valida, consulta y exporta entradas asociadas a compras pagadas de productos o variantes ticketables.

Modelo

  • Ticket: pertenece a tenant y usuario, y conserva referencias al ítem de compra que lo generó, producto, variante y usuario escáner. La compra se obtiene a través de su ítem.
  • El nombre y la descripción se calculan dinámicamente desde el producto y la variante; los tickets no persisten una copia de esos textos.
  • ValidityTime: define ventanas absolutas o relativas de vigencia para fechas de evento y opciones de atributos.
  • ValidityTimeType: enum de estrategias de vigencia.

TicketValidityResolver deriva la vigencia desde la variante asociada. Las alternativas de un mismo atributo se combinan con OR y las dimensiones diferentes se combinan con AND. El modelo calcula si un ticket está vigente, vencido o usado, y resuelve sus fechas efectivas de inicio y fin sin persistir vigencias en el ticket.

Zona horaria y vigencias

La zona se configura por tenant mediante timezone (identificador IANA, por ejemplo America/Argentina/Buenos_Aires), configurado mediante seeders o base de datos y expuesto en la respuesta de tenants. Ese es el valor por defecto. La aplicación mantiene su zona global en UTC.

Las fechas y horas de EventDate y los horarios de time_window son locales al tenant. Las ventanas fixed_window se guardan en UTC. El resolver pasa la zona del tenant del producto al grupo de vigencia: antes de combinar fecha y hora, convierte el ancla a esa zona. Sin fecha de evento, utiliza el día local del instante consultado; contempla ventanas nocturnas. El inicio es inclusivo y el vencimiento exclusivo.

Para validar una ventana horaria directamente, pasar la zona explícitamente: $validityTime->isValid(now(), $tenant->timezone). Un ValidityTime aislado no tiene tenant; los métodos mantienen UTC como valor por defecto para usos sin contexto.

La migración 2026_09_21_040000_add_timezone_to_tenants.php asigna la zona inicial y reconstruye una sola vez las ventanas asociadas a fechas existentes, usando las fechas y horas locales originales. No modifica ventanas independientes. Los cambios posteriores de zona son responsabilidad del tenant: reinterpretan los horarios locales, pero no reescriben los instantes UTC guardados. Editar la fecha u horas de un evento sí vuelve a calcular su ventana. El rollback elimina la columna, sin deshacer la corrección de los instantes. Ya no se utiliza EVENT_TIMEZONE.

Flujo de generación

  1. Purchase emite PurchasePaid al confirmarse el pago.
  2. GenerateTicketsForPaidPurchase atiende el evento.
  3. TicketGeneratorService crea los tickets requeridos según ítems, cantidades y vigencia.
  4. Notification envía la confirmación de compra después de la generación y adjunta los tickets cuando existen.

Datos descartables para pruebas de carga

En ambientes local, testing, staging, homo u homologation, el comando siguiente crea tickets válidos, identidades scanner con tokens Sanctum y un dataset JSON importable por Postman:

php artisan load-test:tickets:prepare loadtest-evento \
    --tickets=40000 \
    --scanners=100 \
    --owners=1000 \
    --catalog-item=123 \
    --run=evento-001

El tenant debe existir y se recomienda que sea exclusivo para carga. Si no se indica --catalog-item, se usa el primer producto estándar del tenant con tickets habilitados. --variant es opcional; al indicarlo, su configuración de vigencia debe estar activa y ser resoluble. Sin variante, los tickets tienen vigencia irrestricta.

El archivo se escribe por defecto en storage/app/private/load-tests/ y contiene tokens secretos, por lo que no debe versionarse. Para limpiar el tenant después de la ejecución:

php artisan tenants:reset-transactions loadtest-evento --dry-run
php artisan tenants:reset-transactions loadtest-evento

Endpoints

Bajo /tenants/{tenant:codigo}, protegidos por auth:sanctum:

  • GET /tickets.
  • POST /tickets/pdf.

Bajo /v1/adminapp/tenant, protegido por auth:sanctum, adminapp.tenant y el menú adminapp.tickets:

  • GET /tickets, paginado y con búsqueda opcional mediante q. La respuesta incluye scanned_tickets y total_tickets para el tenant autenticado.

TicketPdfService genera la descarga y TicketResource/ValidityTimeResource definen las respuestas.

Dependencias y reglas

Depende de Purchase, Catalog, Tenant y Auth. La generación debe ser idempotente ante reintentos del evento. TicketNotAvailableException y TicketGenerationException separan indisponibilidad de errores de generación.

Conversión de datos históricos a UTC (homo / producción)

El comando no requiere migraciones adicionales ni crea una tabla de registro. Para convertir todos los registros de tipo fixed_window desde hora argentina:

# Vista previa: no escribe en la base
php artisan tickets:convert-validity-times-to-utc --all
# Aplicar a todos los fixed_window
php artisan tickets:convert-validity-times-to-utc --all --apply

También permite seleccionar IDs específicos (los siguientes son ejemplos):

php artisan tickets:convert-validity-times-to-utc --ids=41,42
php artisan tickets:convert-validity-times-to-utc --ids=41,42 --apply

--all y --ids son excluyentes. El origen por defecto es America/Argentina/Buenos_Aires; se puede indicar otra zona IANA con --source-timezone. Cada extremo conserva su fecha original y se convierte por separado, incluso si la ventana abarca varios días. Los límites nulos y los time_window no se modifican. La aplicación es transaccional.

No hay registro ni detección automática de conversiones anteriores: repetir --apply vuelve a convertir los valores actuales. La migración original 2026_09_21_040000_add_timezone_to_tenants.php ya convierte las ventanas asociadas a eventos; no ejecutar este comando sobre esas ventanas si ya fueron convertidas. --all incluye absolutamente todos los fixed_window, sin esa distinción.

Revertir una conversión

--reverse convierte los valores actuales desde UTC hacia la zona local, con el mismo alcance --all o --ids. Sin --apply sólo muestra la vista previa.

php artisan tickets:convert-validity-times-to-utc --all --reverse
php artisan tickets:convert-validity-times-to-utc --all --reverse --apply
php artisan tickets:convert-validity-times-to-utc --ids=41,42 --reverse --apply

Con --reverse, --source-timezone indica la zona local de destino (por defecto America/Argentina/Buenos_Aires). Las fechas de cada extremo y los límites nulos se respetan; los time_window no se modifican. Revierte una conversión si se seleccionan los mismos registros sin ediciones intermedias. No recupera una copia histórica ni detecta conversiones previas.