refactor(ticket): implement fixed validity time generation for event date and time windows in TicketGeneratorService
This commit is contained in:
29
app/Domains/Attachable/documentacion/README.md
Normal file
29
app/Domains/Attachable/documentacion/README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Dominio Attachable
|
||||
|
||||
## Propósito
|
||||
|
||||
Centraliza el almacenamiento y la metadata de archivos adjuntos. Acepta archivos subidos o contenido Base64, los persiste en S3 y registra su tipo, MIME, extensión, tamaño, nombre original y clave única.
|
||||
|
||||
## Componentes principales
|
||||
|
||||
- `Models/Attachment.php`: representa un adjunto y genera URL temporales de acceso.
|
||||
- `Services/AttachmentService.php`: almacena, copia y elimina archivos, compensando en S3 si falla la escritura en base de datos.
|
||||
- `Enums/AttachmentType.php`: clasifica imágenes, videos, PDF, audio, documentos y otros archivos.
|
||||
- `Exceptions/AttachmentStorageException.php`: expresa fallos propios del almacenamiento.
|
||||
|
||||
## Flujo principal
|
||||
|
||||
1. El consumidor entrega un `UploadedFile` o una cadena Base64 y un directorio.
|
||||
2. El servicio valida el contenido, detecta MIME/extensión y genera una clave UUID.
|
||||
3. El archivo se guarda en el disco `s3`.
|
||||
4. Se crea el registro `Attachment`; ante error se elimina el objeto que había sido subido.
|
||||
|
||||
## API y dependencias
|
||||
|
||||
No expone rutas HTTP propias. Lo consumen otros dominios, especialmente `Catalog` y `Tenant`. Depende de Laravel Storage, Symfony Mime y del modelo `Attachment`.
|
||||
|
||||
## Consideraciones
|
||||
|
||||
- El directorio no puede quedar vacío después de normalizarlo.
|
||||
- La eliminación se considera fallida si S3 no confirma el borrado.
|
||||
- Las URL generadas son temporales; el vencimiento predeterminado es de 10 minutos.
|
||||
36
app/Domains/Auth/documentacion/README.md
Normal file
36
app/Domains/Auth/documentacion/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Dominio Auth
|
||||
|
||||
## Propósito
|
||||
|
||||
Gestiona identidad y acceso de usuarios de la tienda y del panel administrativo: registro, inicio y cierre de sesión, perfil, autenticación con Google y recuperación de contraseña.
|
||||
|
||||
## Modelo y servicios
|
||||
|
||||
- `User`: usuario autenticable, asociado a tenant, rol, intentos de acceso y categorías habilitadas para escaneo.
|
||||
- `LoginAttempt` y `ResetPasswordAttempt`: trazabilidad de accesos y recuperación de contraseña.
|
||||
- `PasswordLoginService`: autentica tienda y AdminApp, incluyendo bloqueo por intentos.
|
||||
- `RegisterUserService` y `ProfileService`: alta y edición del usuario.
|
||||
- `ResetPasswordAttemptService`: crea, valida y consume códigos de recuperación.
|
||||
- `GoogleAuthService`: redirección, callback e intercambio de código para Google OAuth.
|
||||
- `AdminAppContextService`: carga el contexto requerido por un usuario administrativo.
|
||||
|
||||
## Endpoints públicos
|
||||
|
||||
- `POST /register`, `POST /login` y `POST /logout`.
|
||||
- `GET /me` y `PUT /me`, protegidos por `auth:sanctum`.
|
||||
- Creación, validación y aplicación de intentos de recuperación bajo `/password`.
|
||||
- `POST /auth/google/exchange` para canjear el código de autenticación.
|
||||
- `POST /v1/adminapp/login` y consulta del usuario administrativo dentro del grupo autenticado de AdminApp.
|
||||
|
||||
## Validación y respuestas
|
||||
|
||||
Los `FormRequest` validan cada operación. `UserResource` y `AdminAppMeResource` definen las representaciones de salida. Los endpoints sensibles aplican `auth:sanctum` y límites de frecuencia.
|
||||
|
||||
## Dependencias y eventos
|
||||
|
||||
Se relaciona con `Tenant` y `Authorization`; el registro y la recuperación disparan flujos atendidos por `Notification`. El carrito invitado puede integrarse al usuario autenticado mediante el dominio `Cart`.
|
||||
|
||||
## Consideraciones
|
||||
|
||||
- La resolución del tenant forma parte de la autenticación y no debe omitirse.
|
||||
- Los cambios en reglas de login deben conservar los límites de intentos y el manejo de `AccountLockedException`.
|
||||
26
app/Domains/Authorization/documentacion/README.md
Normal file
26
app/Domains/Authorization/documentacion/README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Dominio Authorization
|
||||
|
||||
## Propósito
|
||||
|
||||
Define el esquema de roles y permisos usado para autorizar funcionalidades de la aplicación.
|
||||
|
||||
## Componentes principales
|
||||
|
||||
- `Enums/RoleCode.php`: códigos de roles conocidos por el sistema.
|
||||
- `Models/Role.php`: rol con relaciones hacia permisos, usuarios y menús.
|
||||
- `Models/Permission.php`: permiso asignable a uno o más roles.
|
||||
- `Models/RolePermission.php`: entidad de asociación entre rol y permiso.
|
||||
|
||||
## API
|
||||
|
||||
No expone controladores ni rutas propias. Su información se consume desde autenticación, menús, políticas y middleware de autorización.
|
||||
|
||||
## Relaciones relevantes
|
||||
|
||||
- `Role` tiene muchos usuarios del dominio `Auth`.
|
||||
- Roles y permisos mantienen una relación muchos-a-muchos.
|
||||
- Los roles determinan los menús disponibles mediante el dominio `Menu`.
|
||||
|
||||
## Consideraciones
|
||||
|
||||
Los códigos definidos en `RoleCode` funcionan como contrato entre datos persistidos y lógica de aplicación. Al agregar un rol o permiso se deben revisar seeds, asociaciones y consumidores.
|
||||
28
app/Domains/Bootstrap/documentacion/README.md
Normal file
28
app/Domains/Bootstrap/documentacion/README.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Dominio Bootstrap
|
||||
|
||||
## Propósito
|
||||
|
||||
Entrega la configuración inicial que necesitan la tienda y el panel administrativo antes de renderizar su interfaz.
|
||||
|
||||
## Flujos
|
||||
|
||||
- `TenantBootstrapService` resuelve un tenant desde el dominio solicitado y carga su información pública.
|
||||
- `AdminAppBootstrapService` prepara el contexto inicial del panel administrativo para el tenant autenticado.
|
||||
- Los controladores invocables transforman el resultado mediante `TenantResource` o `AdminAppBootstrapResource`.
|
||||
|
||||
## Endpoints
|
||||
|
||||
- `GET /tenants/bootstrap/{dominio}`: bootstrap público de la tienda.
|
||||
- Endpoint de bootstrap bajo `/v1/adminapp`, protegido por `auth:sanctum` y `adminapp.tenant`.
|
||||
|
||||
## Validación
|
||||
|
||||
`TenantBootstrapRequest` valida el dominio recibido. `AdminAppBootstrapRequest` reutiliza ese contrato para el panel.
|
||||
|
||||
## Dependencias
|
||||
|
||||
Depende principalmente de `Tenant` para resolver y cargar la tienda, y de los dominios que aportan datos al contexto administrativo.
|
||||
|
||||
## Consideraciones
|
||||
|
||||
Este dominio es un agregador de lectura. Debe mantenerse liviano y delegar la obtención de cada dato al dominio propietario.
|
||||
32
app/Domains/Cart/documentacion/README.md
Normal file
32
app/Domains/Cart/documentacion/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Dominio Cart
|
||||
|
||||
## Propósito
|
||||
|
||||
Gestiona el carrito activo de un tenant tanto para visitantes como para usuarios autenticados.
|
||||
|
||||
## Modelo
|
||||
|
||||
- `Cart`: pertenece a un tenant y opcionalmente a un usuario; calcula el total y permite agregar, actualizar o quitar ítems.
|
||||
- `CartItem`: referencia un `CatalogItem` y, opcionalmente, una `Variant`; expone la selección efectiva.
|
||||
|
||||
## Servicios
|
||||
|
||||
- `CartService`: obtiene el carrito, modifica ítems y administra la cookie del token invitado.
|
||||
- `GuestCartMergeService`: incorpora el carrito invitado al usuario cuando este se autentica.
|
||||
|
||||
## Endpoints
|
||||
|
||||
Bajo `/tenants/{tenant:codigo}`:
|
||||
|
||||
- `GET /cart`.
|
||||
- `POST /cart/items`.
|
||||
- `PATCH /cart/items/{cartItem}`.
|
||||
- `DELETE /cart/items/{cartItem}`.
|
||||
|
||||
## Contratos
|
||||
|
||||
`AddCartItemRequest` y `UpdateCartItemQuantityRequest` validan selección y cantidad. `CartResource` y `CartItemResource` estabilizan la respuesta pública.
|
||||
|
||||
## Dependencias y reglas
|
||||
|
||||
Depende de `Catalog` para productos y variantes, de `Tenant` para aislar datos y de `Auth` cuando existe usuario. Toda operación debe comprobar que carrito e ítem pertenecen al tenant actual.
|
||||
33
app/Domains/Catalog/documentacion/README.md
Normal file
33
app/Domains/Catalog/documentacion/README.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Dominio Catalog
|
||||
|
||||
## Propósito
|
||||
|
||||
Modela y publica la oferta comercial del tenant: productos, variantes, categorías, marcas, atributos, inventario, bundles y grupos destacados.
|
||||
|
||||
## Modelo
|
||||
|
||||
- `CatalogItem` es la raíz del producto y se relaciona con tenant, categoría, marca, inventario, variantes, atributos, adjuntos y grupos destacados.
|
||||
- `Variant`, `ItemAttribute`, `Attribute`, `AttributeOption` y `VariantDefinition` describen opciones comercializables.
|
||||
- `Inventory` administra stock disponible, reservado y comprado.
|
||||
- `Category` soporta jerarquía y categorías globales o propias del tenant.
|
||||
- `FeaturedGroup` y `FeaturedItem` organizan secciones destacadas.
|
||||
- `BundleComponent` representa los componentes de un paquete.
|
||||
|
||||
## Servicios
|
||||
|
||||
- `CatalogService`: alta, búsqueda, detalle, listado por categoría y eliminación.
|
||||
- `CatalogInventoryService`: consulta, reserva, libera y confirma inventario.
|
||||
- `FeaturedGroupService`: pagina los ítems destacados para la tienda.
|
||||
- `OnTicketFeaturedGroupService`: administra grupos destacados del panel para sitios de tickets.
|
||||
|
||||
## Endpoints de tienda
|
||||
|
||||
Bajo `/tenants/{tenant:codigo}` se publican catálogo, búsqueda, categoría, detalle, alta de ítems y paginación de grupos destacados.
|
||||
|
||||
## Endpoints administrativos
|
||||
|
||||
Bajo `/v1/adminapp/tenant/featured-groups`, con `auth:sanctum` y `adminapp.tenant`, se listan, crean y actualizan grupos destacados.
|
||||
|
||||
## Dependencias y reglas
|
||||
|
||||
Usa `Attachable` para imágenes/archivos, `Tenant` para aislamiento y `Ticket`/`Event` para vigencia y fechas. `Cart` y `Purchase` consumen sus precios, variantes e inventario. Los cambios de stock deben pasar por `CatalogInventoryService` para conservar reservas y disponibilidad.
|
||||
28
app/Domains/Event/documentacion/README.md
Normal file
28
app/Domains/Event/documentacion/README.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Dominio Event
|
||||
|
||||
## Propósito
|
||||
|
||||
Administra la configuración temporal de un tenant orientado a eventos y sus fechas disponibles.
|
||||
|
||||
## Componentes
|
||||
|
||||
- `Models/EventDate.php`: fecha del evento con inicio, fin, tenant y variantes asociadas.
|
||||
- `Services/EventService.php`: obtiene y actualiza la configuración de evento del tenant.
|
||||
- `Controllers/AdminApp/EventController.php`: consulta y modificación desde AdminApp.
|
||||
- `UpdateEventRequest`: valida datos y reglas cruzadas de fechas.
|
||||
- `EventResource`: serializa la configuración de salida.
|
||||
|
||||
## Endpoints
|
||||
|
||||
Bajo `/v1/adminapp/tenant/event`, protegidos por `auth:sanctum` y `adminapp.tenant`:
|
||||
|
||||
- `GET`: obtiene la configuración.
|
||||
- `PUT`: actualiza la configuración.
|
||||
|
||||
## Dependencias
|
||||
|
||||
Depende de `Tenant`. Las fechas se vinculan con variantes de `Catalog`, que a su vez pueden generar tickets.
|
||||
|
||||
## Consideraciones
|
||||
|
||||
El archivo `routes/api.php` no publica operaciones adicionales. Al modificar fechas debe mantenerse la validación de orden y coherencia temporal de `UpdateEventRequest`.
|
||||
25
app/Domains/Forms/documentacion/README.md
Normal file
25
app/Domains/Forms/documentacion/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Dominio Forms
|
||||
|
||||
## Propósito
|
||||
|
||||
Provee catálogos y opciones auxiliares para construir formularios del panel administrativo. Es un dominio de lectura que compone datos pertenecientes a otros dominios.
|
||||
|
||||
## Formularios disponibles
|
||||
|
||||
- `EventFormService`: devuelve redes sociales disponibles y las URL configuradas para el tenant.
|
||||
- `SaleFormService`: expone los estados admitidos para compras con sus etiquetas de presentación.
|
||||
- `StaffFormService`: lista categorías raíz que pueden asignarse al personal del tenant.
|
||||
|
||||
Cada servicio tiene un controlador invocable y un `JsonResource` específico. `SocialMediaOptionResource` representa las opciones de redes sociales.
|
||||
|
||||
## Endpoints
|
||||
|
||||
Bajo `/v1/adminapp/forms`, con `auth:sanctum` y `adminapp.tenant`:
|
||||
|
||||
- `GET /event`.
|
||||
- `GET /sale`.
|
||||
- `GET /staff`.
|
||||
|
||||
## Dependencias
|
||||
|
||||
Compone datos de `Tenant`, `Purchase` y `Catalog`. No debe duplicar reglas de negocio: las listas y estados canónicos siguen perteneciendo a sus dominios de origen.
|
||||
29
app/Domains/Integration/documentacion/README.md
Normal file
29
app/Domains/Integration/documentacion/README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Dominio Integration
|
||||
|
||||
## Propósito
|
||||
|
||||
Gestiona integraciones externas disponibles y su configuración por tenant. Incluye correo y pagos mediante Telepagos.
|
||||
|
||||
## Modelo y seguridad
|
||||
|
||||
- `Integration`: definición global de una integración.
|
||||
- `TenantIntegration`: configuración y credenciales de una integración para un tenant.
|
||||
- `EncryptedIntegrationData`: cast que protege los datos sensibles persistidos.
|
||||
- `TenantIntegrationService`: consulta y configura integraciones del tenant.
|
||||
|
||||
## Servicios externos
|
||||
|
||||
- `BaseIntegrationService`: base para resolver configuración, URL y cliente del tenant.
|
||||
- `MailService`: envío de correo usando la integración configurada.
|
||||
- `TelepagosIntegrationService`: autenticación, caché de token, generación de QR y consulta de cobros.
|
||||
- `TelepagosWebhookService`: procesa notificaciones recibidas desde Telepagos.
|
||||
|
||||
## Endpoints
|
||||
|
||||
- CRUD global bajo `/integrations`.
|
||||
- Consulta y configuración por tenant bajo `/{tenant_code}/integrations`.
|
||||
- `POST /webhooks/telepagos/{tenant_codigo}` para notificaciones del proveedor.
|
||||
|
||||
## Dependencias y reglas
|
||||
|
||||
Se integra con `Tenant` y con el checkout de `Purchase`. `Notification` utiliza `MailService`. Las credenciales no deben exponerse en respuestas ni logs; los webhooks deben validar su contrato antes de alterar una compra.
|
||||
25
app/Domains/Logging/documentacion/README.md
Normal file
25
app/Domains/Logging/documentacion/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Dominio Logging
|
||||
|
||||
## Propósito
|
||||
|
||||
Registra cambios relevantes de valores en modelos de negocio, indicando tenant, atributo, valor anterior/nuevo, fecha y actor.
|
||||
|
||||
## Componentes
|
||||
|
||||
- `Models/ValueChange.php`: entrada persistida del historial, relacionada polimórficamente con el objeto modificado.
|
||||
- `Models/Concerns/LogsValueChanges.php`: trait reutilizable que escucha actualizaciones del modelo.
|
||||
- `Enums/ValueChangeActorType.php`: distingue cambios realizados por usuario o por el sistema.
|
||||
|
||||
## Uso
|
||||
|
||||
Un modelo consumidor debe:
|
||||
|
||||
1. Usar el trait `LogsValueChanges`.
|
||||
2. Declarar la propiedad `loggedAttributes` con los atributos auditables.
|
||||
3. Implementar `valueChangeTenantCode()`.
|
||||
|
||||
El trait solo registra atributos configurados que efectivamente cambiaron. Si existe un usuario autenticado lo asocia al cambio; en caso contrario marca al sistema como actor.
|
||||
|
||||
## API y dependencias
|
||||
|
||||
No expone rutas HTTP. `Purchase` lo utiliza para auditar cambios de estado y `Sale` consulta esas modificaciones para reportes.
|
||||
24
app/Domains/MailTest/documentacion/README.md
Normal file
24
app/Domains/MailTest/documentacion/README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Dominio MailTest
|
||||
|
||||
## Propósito
|
||||
|
||||
Ofrece una operación técnica para verificar la configuración de correo de un tenant sin ejecutar un flujo funcional real.
|
||||
|
||||
## Componentes
|
||||
|
||||
- `MailTestController`: endpoint invocable de envío.
|
||||
- `SendTestMailRequest`: valida destinatario y contenido requerido.
|
||||
- `MailTestService`: coordina el envío de prueba.
|
||||
- `TestMail`: mailable utilizado para construir el mensaje.
|
||||
|
||||
## Endpoint
|
||||
|
||||
- `POST /{tenant_code}/mail-test/send`.
|
||||
|
||||
## Dependencias
|
||||
|
||||
Usa la configuración de correo del dominio `Integration` y resuelve el tenant indicado.
|
||||
|
||||
## Consideraciones
|
||||
|
||||
Es una herramienta de diagnóstico. Debe restringirse o deshabilitarse en entornos donde no corresponda exponer envíos de prueba, y nunca debe registrar credenciales.
|
||||
24
app/Domains/Menu/documentacion/README.md
Normal file
24
app/Domains/Menu/documentacion/README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Dominio Menu
|
||||
|
||||
## Propósito
|
||||
|
||||
Define menús disponibles y permite configurar su contenido para cada tenant y rol.
|
||||
|
||||
## Modelo
|
||||
|
||||
- `Menu`: definición global de una entrada de menú y su tipo de contenido.
|
||||
- `TenantMenu`: configuración específica por tenant, incluyendo contenido estático cuando corresponde.
|
||||
- `MenuRole`: asociación entre menú y rol autorizado.
|
||||
|
||||
## Servicios
|
||||
|
||||
`TenantMenuService::configure()` crea o actualiza atómicamente la configuración de un menú para un tenant. Solo conserva `static_content` cuando el menú fue definido como contenido estático.
|
||||
|
||||
## Endpoints
|
||||
|
||||
- Recurso REST `/menues` mediante `MenuController`.
|
||||
- `POST /{tenant_code}/menues/{menu_code}` para configurar un menú del tenant.
|
||||
|
||||
## Dependencias y reglas
|
||||
|
||||
Depende de `Tenant` y `Authorization`. Los códigos de menú y tenant forman la identidad lógica de la configuración; el contenido enviado debe respetar el tipo definido por `Menu`.
|
||||
26
app/Domains/Notification/documentacion/README.md
Normal file
26
app/Domains/Notification/documentacion/README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Dominio Notification
|
||||
|
||||
## Propósito
|
||||
|
||||
Orquesta notificaciones de negocio por correo a partir de eventos de otros dominios.
|
||||
|
||||
## Eventos atendidos
|
||||
|
||||
- `UserRegistered`: dispara el correo de bienvenida.
|
||||
- `PasswordResetRequested`: envía el código de recuperación si el intento sigue pendiente.
|
||||
- `PurchasePaid`: envía la confirmación de pago.
|
||||
- `TicketsAvailable`: informa y entrega la disponibilidad de tickets.
|
||||
|
||||
## Componentes
|
||||
|
||||
Los listeners `SendWelcomeEmail`, `SendPasswordResetEmail`, `SendPurchasePaidEmail` y `SendTicketsAvailableEmail` delegan en `NotificationMailService`. Este servicio carga el contexto necesario, renderiza las vistas y envía mediante `Integration/MailService`.
|
||||
|
||||
## API y dependencias
|
||||
|
||||
No expone rutas HTTP. Consume datos de `Auth`, `Tenant`, `Purchase` y `Ticket`, y delega la entrega al dominio `Integration`.
|
||||
|
||||
## Consideraciones
|
||||
|
||||
- Los listeners reciben identificadores y vuelven a cargar los modelos, evitando transportar entidades obsoletas.
|
||||
- La recuperación no se envía si el intento dejó de estar pendiente.
|
||||
- Los handlers deben permanecer idempotentes o tolerantes a reintentos de cola.
|
||||
33
app/Domains/Purchase/documentacion/README.md
Normal file
33
app/Domains/Purchase/documentacion/README.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Dominio Purchase
|
||||
|
||||
## Propósito
|
||||
|
||||
Implementa el ciclo de compra y checkout: crea una compra desde el carrito, toma una instantánea de sus ítems, reserva inventario, permite ediciones, inicia el pago y confirma, cancela o vence la operación.
|
||||
|
||||
## Modelo
|
||||
|
||||
- `Purchase`: raíz de la compra; estados `created`, `pending_payment`, `paid`, `cancelled`, `rejected` y `expired`.
|
||||
- `PurchaseItem`: snapshot del producto o variante, cantidad, precio y total al comprar.
|
||||
- `TelepagosQr` y `TelepagosPayment`: datos del QR e intentos/resultados del proveedor.
|
||||
- `PurchasePaid`: evento emitido una sola vez al pasar a pagada bajo bloqueo transaccional.
|
||||
|
||||
## Servicios de checkout
|
||||
|
||||
`CheckoutService` es la fachada estable. Delega en:
|
||||
|
||||
- `StartCheckoutService`: inicia la compra desde el carrito.
|
||||
- `EditCheckoutService`: modifica cliente o cantidades antes del cierre.
|
||||
- `CompleteCheckoutService`: completa, envía a revisión o confirma el pago.
|
||||
- `ReleaseCheckoutService`: cancela, vence y procesa vencimientos pendientes.
|
||||
- `SourceCartService`: sincroniza, restaura o finaliza el carrito fuente.
|
||||
- `CatalogSelectionResolver` y `PurchaseItemSnapshotFactory`: resuelven selecciones y generan snapshots.
|
||||
|
||||
`UserPurchaseLimitService` controla límites de compra y `CheckoutService` conserva el punto de entrada para controladores e integraciones.
|
||||
|
||||
## Endpoints
|
||||
|
||||
Bajo `/tenants/{tenant:codigo}/compras`, con `auth:sanctum`: listado, inicio, detalle, edición de ítems, datos del cliente, intención de pago, finalización, revisión y cancelación.
|
||||
|
||||
## Dependencias y reglas
|
||||
|
||||
Depende de `Cart`, `Catalog`, `Tenant`, `Auth` e `Integration`; emite eventos consumidos por `Ticket` y `Notification`. Los cambios de estado e inventario deben ser transaccionales y usar los servicios del checkout, no actualizaciones directas del modelo.
|
||||
28
app/Domains/Sale/documentacion/README.md
Normal file
28
app/Domains/Sale/documentacion/README.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Dominio Sale
|
||||
|
||||
## Propósito
|
||||
|
||||
Provee consultas administrativas y exportaciones de ventas confirmadas, además del historial de modificaciones auditadas.
|
||||
|
||||
## Componentes
|
||||
|
||||
- `AdminAppSaleService`: pagina ventas, calcula totales y obtiene colecciones para exportación; también consulta modificaciones.
|
||||
- `AdminAppSalePdfService`: genera descargas PDF de ventas y de cambios.
|
||||
- `AdminAppSaleIndexRequest`: valida filtros del listado y la exportación.
|
||||
- `SaleResource` y `SaleModificationResource`: representan ventas e historial para AdminApp.
|
||||
- `SaleController`: entrada HTTP del panel.
|
||||
|
||||
## Endpoints
|
||||
|
||||
Bajo `/v1/adminapp/tenant`, protegidos por `auth:sanctum` y `adminapp.tenant`:
|
||||
|
||||
- `GET /sales` y `GET /sales/pdf`.
|
||||
- `GET /sales/modifications` y `GET /sales/modifications/pdf`.
|
||||
|
||||
## Dependencias
|
||||
|
||||
Consume compras de `Purchase`, datos del tenant y entradas de `Logging`. No es dueño del estado de una compra; cualquier mutación debe ejecutarse en `Purchase`.
|
||||
|
||||
## Consideraciones
|
||||
|
||||
La consulta paginada y la colección de exportación deben aplicar los mismos filtros para evitar diferencias entre pantalla y PDF.
|
||||
18
app/Domains/Shared/documentacion/README.md
Normal file
18
app/Domains/Shared/documentacion/README.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Dominio Shared
|
||||
|
||||
## Propósito
|
||||
|
||||
Contiene contratos técnicos reutilizables que no pertenecen a un único dominio funcional.
|
||||
|
||||
## Componentes
|
||||
|
||||
- `Enums/FieldType.php`: tipos de campos dinámicos y helpers para determinar si admiten opciones estáticas o dinámicas.
|
||||
- `Rules/ImageOrBase64Rule.php`: regla de validación para aceptar una imagen subida o codificada en Base64.
|
||||
|
||||
## API
|
||||
|
||||
No posee modelos persistentes, controladores ni rutas. Sus elementos se importan desde requests y servicios de otros dominios.
|
||||
|
||||
## Criterio de pertenencia
|
||||
|
||||
Solo deben incorporarse aquí conceptos verdaderamente transversales. Una regla o enum con significado de negocio específico debe permanecer en su dominio propietario.
|
||||
20
app/Domains/Staff/documentacion/README.md
Normal file
20
app/Domains/Staff/documentacion/README.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Dominio Staff
|
||||
|
||||
## Propósito
|
||||
|
||||
Administra usuarios de personal de un tenant y las categorías que tienen habilitadas para operar o escanear.
|
||||
|
||||
## Componentes
|
||||
|
||||
- `AdminAppStaffController`: listado, alta, modificación y baja.
|
||||
- `StaffService`: aplica el alcance por tenant, busca personal y sincroniza sus datos/asignaciones.
|
||||
- `StoreStaffRequest` y `UpdateStaffRequest`: validan cada operación.
|
||||
- `StaffResource`: representación de salida para AdminApp.
|
||||
|
||||
## Endpoints
|
||||
|
||||
Recurso REST `/v1/adminapp/tenant/staff`, excepto detalle individual, protegido por `auth:sanctum` y `adminapp.tenant`.
|
||||
|
||||
## Dependencias y reglas
|
||||
|
||||
Usa `Auth/User` como entidad de personal, `Authorization` para su rol, `Catalog/Category` para asignaciones y `Tenant` para aislamiento. Toda búsqueda, edición o borrado debe comprobar que el usuario pertenece al tenant autenticado.
|
||||
23
app/Domains/StorageTest/documentacion/README.md
Normal file
23
app/Domains/StorageTest/documentacion/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Dominio StorageTest
|
||||
|
||||
## Propósito
|
||||
|
||||
Expone operaciones técnicas para comprobar la escritura en S3 y la generación de URL temporales.
|
||||
|
||||
## Componentes
|
||||
|
||||
- `S3TestController`: recibe solicitudes de carga y URL temporal.
|
||||
- `S3TestService`: almacena un archivo de prueba y genera el enlace firmado.
|
||||
- `StoreS3TestFileRequest`: valida la carga.
|
||||
- `GenerateS3TemporaryUrlRequest`: valida ruta y tiempo de expiración.
|
||||
|
||||
## Endpoints
|
||||
|
||||
Bajo `/storage-test/s3`:
|
||||
|
||||
- `POST /upload`.
|
||||
- `GET /temporary-url`.
|
||||
|
||||
## Consideraciones
|
||||
|
||||
Es infraestructura de diagnóstico, no una API funcional de archivos. Debe restringirse por entorno o autorización. Para adjuntos de negocio se debe usar el dominio `Attachable`.
|
||||
30
app/Domains/Tenant/documentacion/README.md
Normal file
30
app/Domains/Tenant/documentacion/README.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Dominio Tenant
|
||||
|
||||
## Propósito
|
||||
|
||||
Es la raíz del modelo multi-tenant. Gestiona organizaciones/sitios, tipos de web, redes sociales, logos y extras configurables por sitio.
|
||||
|
||||
## Modelo
|
||||
|
||||
- `Tenant`: entidad principal, resuelta en rutas por `codigo`; relaciona catálogo, fechas, redes, menús y configuración visual.
|
||||
- `WebsiteType`: plantilla o tipo de sitio disponible.
|
||||
- `WebsiteTypeExtra`: definición de un extra y su configuración admitida.
|
||||
- `WebsiteExtra`: valor resuelto y estado del extra para un tenant.
|
||||
- `SocialMedia`: catálogo de redes sociales asociables.
|
||||
|
||||
## Servicios
|
||||
|
||||
- `TenantService`: crea y actualiza tenants, incluyendo sus recursos asociados.
|
||||
- `TenantInformationService`: carga un tenant y las relaciones requeridas por cada contexto.
|
||||
- `WebsiteTypeService`: crea o actualiza tipos de sitio.
|
||||
- `WebsiteExtraService`: construye reglas dinámicas, crea, actualiza y habilita/deshabilita extras.
|
||||
- `TenantDomainNormalizer`: normaliza dominios antes de resolver el tenant.
|
||||
|
||||
## Endpoints
|
||||
|
||||
- Recurso REST público/administrativo `/tenants`.
|
||||
- Bajo `/v1/adminapp/tenant/website-extras`, con autenticación y contexto de tenant: consulta general, detalle, actualización y activación/desactivación.
|
||||
|
||||
## Dependencias y reglas
|
||||
|
||||
Usa `Attachable` para logos y archivos. Es referenciado por casi todos los dominios para aislamiento. El `codigo` es clave de ruta y clave foránea heredada; no debe sustituirse por `id` sin una migración integral.
|
||||
@@ -5,6 +5,7 @@ namespace App\Domains\Ticket\Services;
|
||||
use App\Domains\Auth\Models\User;
|
||||
use App\Domains\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Catalog\Models\Variant;
|
||||
use App\Domains\Ticket\Enums\ValidityTimeType;
|
||||
use App\Domains\Ticket\Exceptions\TicketGenerationException;
|
||||
use App\Domains\Ticket\Models\Ticket;
|
||||
use App\Domains\Ticket\Models\ValidityTime;
|
||||
@@ -34,14 +35,21 @@ class TicketGeneratorService
|
||||
$quantity,
|
||||
$sourceVariantId,
|
||||
);
|
||||
$fixedValidityTimes = [];
|
||||
|
||||
return $targets->map(function (array $target) use (
|
||||
&$fixedValidityTimes,
|
||||
$sourcePurchaseId,
|
||||
$user,
|
||||
): Ticket {
|
||||
$item = $target['catalog_item'];
|
||||
$variant = $target['variant'];
|
||||
$validityTime = $this->resolveValidityTime($item, $variant);
|
||||
$validityTime = $this->materializeEventDateValidityTime(
|
||||
$variant,
|
||||
$validityTime,
|
||||
$fixedValidityTimes,
|
||||
);
|
||||
|
||||
return Ticket::query()->create([
|
||||
'tenant_code' => $item->tenant_code,
|
||||
@@ -168,4 +176,56 @@ class TicketGeneratorService
|
||||
|
||||
return $validityTimes->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a recurring time window into the fixed date and time purchased by
|
||||
* the customer. Equal tickets generated together share the same snapshot.
|
||||
*
|
||||
* @param array<string, ValidityTime> $fixedValidityTimes
|
||||
*/
|
||||
private function materializeEventDateValidityTime(
|
||||
?Variant $variant,
|
||||
?ValidityTime $validityTime,
|
||||
array &$fixedValidityTimes,
|
||||
): ?ValidityTime {
|
||||
if (
|
||||
$variant === null
|
||||
|| $validityTime === null
|
||||
|| $validityTime->type !== ValidityTimeType::TimeWindow
|
||||
) {
|
||||
return $validityTime;
|
||||
}
|
||||
|
||||
$variant->loadMissing('eventDate');
|
||||
$eventDate = $variant->eventDate;
|
||||
|
||||
if ($eventDate === null) {
|
||||
return $validityTime;
|
||||
}
|
||||
|
||||
$cacheKey = $eventDate->getKey().':'.$validityTime->getKey();
|
||||
|
||||
if (isset($fixedValidityTimes[$cacheKey])) {
|
||||
return $fixedValidityTimes[$cacheKey];
|
||||
}
|
||||
|
||||
$startsAt = $validityTime->startsAt($eventDate->date);
|
||||
$expiresAt = $validityTime->expiresAt($eventDate->date);
|
||||
|
||||
if (
|
||||
$startsAt !== null
|
||||
&& $expiresAt !== null
|
||||
&& $expiresAt->lessThanOrEqualTo($startsAt)
|
||||
) {
|
||||
$expiresAt = $expiresAt->addDay();
|
||||
}
|
||||
|
||||
return $fixedValidityTimes[$cacheKey] = ValidityTime::query()->create([
|
||||
'type' => ValidityTimeType::FixedWindow,
|
||||
'start_time' => null,
|
||||
'end_time' => null,
|
||||
'fixed_starts_at' => $startsAt,
|
||||
'fixed_expires_at' => $expiresAt,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
33
app/Domains/Ticket/documentacion/README.md
Normal file
33
app/Domains/Ticket/documentacion/README.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# 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, conserva referencias a compra, producto, variante, validez y usuario escáner.
|
||||
- `ValidityTime`: define ventanas absolutas o relativas de vigencia para productos, opciones y tickets.
|
||||
- `ValidityTimeType`: enum de estrategias de vigencia.
|
||||
|
||||
El modelo calcula si un ticket está vigente, vencido o usado, y resuelve sus fechas efectivas de inicio y fin.
|
||||
|
||||
## 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. El flujo puede emitir disponibilidad para que `Notification` informe al comprador.
|
||||
|
||||
## Endpoints
|
||||
|
||||
Bajo `/tenants/{tenant:codigo}`, protegidos por `auth:sanctum`:
|
||||
|
||||
- `GET /tickets`.
|
||||
- `POST /tickets/pdf`.
|
||||
|
||||
`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.
|
||||
@@ -6,12 +6,17 @@ use App\Domains\Attachable\Enums\AttachmentType;
|
||||
use App\Domains\Attachable\Models\Attachment;
|
||||
use App\Domains\Auth\Models\User;
|
||||
use App\Domains\Catalog\Enums\CatalogItemType;
|
||||
use App\Domains\Catalog\Models\Attribute;
|
||||
use App\Domains\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Catalog\Models\Inventory;
|
||||
use App\Domains\Event\Models\EventDate;
|
||||
use App\Domains\Notification\Events\TicketsAvailable;
|
||||
use App\Domains\Purchase\Models\Purchase;
|
||||
use App\Domains\Shared\Enums\FieldType;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use App\Domains\Ticket\Enums\ValidityTimeType;
|
||||
use App\Domains\Ticket\Exceptions\TicketGenerationException;
|
||||
use App\Domains\Ticket\Models\ValidityTime;
|
||||
use App\Domains\Ticket\Services\TicketGeneratorService;
|
||||
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
@@ -180,6 +185,56 @@ class TicketGeneratorServiceTest extends TestCase
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_event_date_and_time_window_are_snapshotted_as_a_fixed_window(): void
|
||||
{
|
||||
$item = $this->createTicketableItem('scheduled-meal');
|
||||
$eventDate = EventDate::query()->create([
|
||||
'tenant_code' => $this->tenant->codigo,
|
||||
'date' => '2026-08-20',
|
||||
'time_start' => '09:00:00',
|
||||
'time_end' => '23:59:59',
|
||||
]);
|
||||
$timeWindow = ValidityTime::query()->create([
|
||||
'type' => ValidityTimeType::TimeWindow,
|
||||
'start_time' => '22:00:00',
|
||||
'end_time' => '02:00:00',
|
||||
]);
|
||||
$schedule = Attribute::query()->create([
|
||||
'tenant_codigo' => $this->tenant->codigo,
|
||||
'codigo' => 'schedule',
|
||||
'nombre' => 'Schedule',
|
||||
'type' => FieldType::Select,
|
||||
]);
|
||||
$schedule->options()->create([
|
||||
'value' => 'night',
|
||||
'label' => '22:00 - 02:00',
|
||||
'validity_time_id' => $timeWindow->id,
|
||||
]);
|
||||
$itemSchedule = $item->itemAttributes()->create([
|
||||
'attribute_id' => $schedule->id,
|
||||
]);
|
||||
$inventory = Inventory::query()->create();
|
||||
$variant = $item->variants()->create([
|
||||
'event_date_id' => $eventDate->id,
|
||||
'inventory_id' => $inventory->id,
|
||||
]);
|
||||
$variant->definitions()->create([
|
||||
'item_attribute_id' => $itemSchedule->id,
|
||||
'value' => 'night',
|
||||
]);
|
||||
|
||||
$tickets = $this->service->generate($item, $this->user, 2, $variant->id);
|
||||
|
||||
$this->assertCount(2, $tickets);
|
||||
$this->assertSame(1, $tickets->pluck('validity_time_id')->unique()->count());
|
||||
$this->assertNotSame($timeWindow->id, $tickets->first()->validity_time_id);
|
||||
|
||||
$fixedWindow = $tickets->first()->validityTime;
|
||||
$this->assertSame(ValidityTimeType::FixedWindow, $fixedWindow->type);
|
||||
$this->assertSame('2026-08-20 22:00:00', $fixedWindow->fixed_starts_at->format('Y-m-d H:i:s'));
|
||||
$this->assertSame('2026-08-21 02:00:00', $fixedWindow->fixed_expires_at->format('Y-m-d H:i:s'));
|
||||
}
|
||||
|
||||
public function test_marking_a_purchase_as_paid_ignores_items_without_tickets(): void
|
||||
{
|
||||
Event::fake([TicketsAvailable::class]);
|
||||
|
||||
Reference in New Issue
Block a user