# Plan: Compra sin registro (guest checkout)

## Objetivo

Agregar en la configuración del evento la opción **"Permitir compra sin registrarse"** (`events.configuration.guest_checkout`, 0/1;
1 = permite comprar sin registro). Los eventos existentes no tienen la clave y se tratan como 0 (comportamiento actual).
Si está activa, en la web pública el comprador puede pagar sin iniciar sesión ni crear contraseña. Internamente se
sigue creando (o reutilizando) `Person` + `User`, pero **sin contraseña conocida y sin iniciar sesión automáticamente**.
Con la opción activa son obligatorios **nombre, apellido y e-mail** (+ los campos de registro configurados en el evento).

## Situación actual (relevante)

- `events.configuration` es un JSON (cast `array` en [app/Event.php](../app/Event.php)), se arma completo en
  `store()` y `update()` de [Admin/EventController.php](../app/Http/Controllers/Admin/EventController.php) (~L196 y ~L527).
  → **No requiere migración**, solo agregar la llave.
- El checkout de invitado ya existe pero obliga a registrarse:
  - [Web/HomeController.php](../app/Http/Controllers/Web/HomeController.php) `payment()` (~L670): valida `email` `unique:users|unique:persons`
    + `password` confirmado; crea Person/User y hace `Auth::attempt()`.
  - `payment_pasarela()` (~L862): si no hay sesión, vuelve a crear Person/User y `Auth::login()`; usa `Auth::user()->id` para
    `cart->user_id` y `Auth::user()->person` para datos 3DS (~L890, ~L945).
  - `payment_ok()` (~L1269): mismo patrón de crear + `Auth::login()`; `Auth::user()->id` en el carrito (~L1349);
    actualiza datos de `Person` con lo del formulario (~L1398).
- Vista [web/confirm.blade.php](../resources/views/web/confirm.blade.php): alerta "Debes tener la sesión iniciada" (L34),
  bloque "OPCIONES DE ACCESO" (L180), campos nombre/email/password solo si `fullname`/`email` están en `registration_fields` (L219–252).
- `users.password` es `NOT NULL` → usar un password aleatorio hasheado (sin migración).
- El retorno de CardNET (`payment_success`) ya trabaja con `cart->user_id` y `cart->form_data`, no depende de la sesión.

## Diseño

### 1. Admin – nuevo campo de configuración
- [admin/events/form.blade.php](../resources/views/admin/events/form.blade.php): nuevo `<select name="guest_checkout">` junto a
  "Enumerar asientos", usando `selectHTML($bool, ...)` igual que los demás. Etiqueta: **"Permitir compra sin registrarse"**.
- `EventController@store` y `@update`: agregar `'guest_checkout' => $request->guest_checkout` al arreglo `configuration`.
- Helper en `Event`: `public function allowsGuestCheckout(): bool { return ($this->configuration['guest_checkout'] ?? 0) == 1; }`

### 2. Backend – resolver comprador sin login
Crear en `HomeController` un método protegido único (reemplaza los 3 bloques duplicados de "crear Person/User"):

```php
protected function resolveGuestBuyer(array $form): User
```
- Busca `User` por email (`type = 'user'`). Si existe → lo reutiliza **sin iniciar sesión** y **sin sobrescribir** los datos
  de su `Person` (solo completar campos vacíos). Los datos del formulario quedan en la orden (`orders.registration_fields`).
- Si existe una `Person` con ese email pero sin `User` → crear el `User` vinculado.
- Si no existe → crear `Person` + `User` con `password = bcrypt(Str::random(40))`, `active = 1`, `type = 'user'`.
- Si el email pertenece a un usuario `admin`/`company`/`owner` → rechazar con mensaje ("Este correo no puede usarse para compras web").

Cambios por método:
- **`payment()`**: si `!Auth::check() && $event->allowsGuestCheckout()` → validación de invitado:
  `name`, `lastname`, `email` (required|email|confirmed), `email_confirmation`, `fields.*` requeridos según config, `chk_terms`.
  Sin `password` ni `unique`. No crear usuario aquí ni hacer login; solo validar y continuar (la creación ocurre en
  `payment_pasarela` / `payment_ok`, que ya lo hacen). Mantener la rama actual (con password) cuando la opción esté apagada.
- **`payment_pasarela()`**: reemplazar bloque `if (!Auth::check()) {...Auth::login}` por
  `$user = Auth::check() ? Auth::user() : $this->resolveGuestBuyer($request);` y usar `$user` en lugar de `Auth::user()`
  (`cart->user_id`, `3DS_email`, `3DS_mobilePhone`).
- **`payment_ok()`**: igual; quitar `Auth::login()` en la rama de invitado y reemplazar `Auth::user()->id` (~L1349) por `$user->id`.
  Para invitados que reutilizan una cuenta existente, no actualizar `Person` (L1398–1401) con datos no autenticados.
- **Reservación**: hoy se actualiza `session_id` porque el login regenera la sesión. Sin login, la sesión no cambia → verificar
  que la reservación se siga encontrando por `session_id` y eliminar la actualización innecesaria en la rama invitado.
- **`max_tickets`**: el control ya busca `Person` por email cuando no hay sesión → funciona igual; verificar.
- Seguridad: no exponer en la respuesta si el email ya existe (evitar enumeración de cuentas).

### 3. Vista pública – checkout
`web/confirm.blade.php` (y revisar `eventConfirm` / `eventConfirmCart` que pasan `$registration_fields`):
- Pasar `$show_guest_form = !Auth::check() && $event->allowsGuestCheckout()` a la vista.
- Si `$show_guest_form`:
  - Ocultar alerta "Debes tener la sesión iniciada…" (L34).
  - Cambiar "OPCIONES DE ACCESO" por un aviso opcional: "¿Ya tienes cuenta? Inicia sesión (opcional)".
  - Mostrar **siempre** Nombre, Apellido, E-mail y Confirmar e-mail, aunque `fullname`/`email` no estén en `registration_fields`.
  - **No** mostrar Password / Confirmar password.
  - Encabezado "DATOS DEL COMPRADOR" en lugar de "REGISTRO".
- `resources/js/web/confirm.js`: revisar que no bloquee el envío sin sesión y que pinte los errores nuevos. Recompilar con `npm run prod`.
- `event.blade.php` / layout: verificar que no haya redirección o modal forzado de login antes de llegar al checkout.

### 4. Correo de confirmación (opcional, recomendado)
- En `mail.web.ticket-sales`, si la compra fue de invitado con cuenta nueva: agregar texto + enlace
  "Crea tu contraseña para ver tus boletas en línea" apuntando a `password.request` (flujo de "Olvidé mi contraseña" existente).
  Así el invitado puede reclamar su cuenta luego sin desarrollo adicional.

### 5. Fuera de alcance
- Flujo de empresas (`Company\DashboardController`, `confirm-special.blade.php`) — no cambia.
- Boletas etiquetadas (`ticket[email][]`) — se mantiene igual.
- Panel del usuario (`Web\UserController`) — sin cambios; el invitado accede tras crear su contraseña.

## Archivos a tocar

| Archivo | Cambio |
|---|---|
| `resources/views/admin/events/form.blade.php` | Nuevo select `guest_checkout` |
| `app/Http/Controllers/Admin/EventController.php` | Guardar llave en `store` y `update` |
| `app/Event.php` | Helper `allowsGuestCheckout()` |
| `app/Http/Controllers/Web/HomeController.php` | `resolveGuestBuyer()`, ajustes en `payment`, `payment_pasarela`, `payment_ok`, `eventConfirm`, `eventConfirmCart` |
| `resources/views/web/confirm.blade.php` | Formulario de invitado |
| `resources/js/web/confirm.js` (+ build) | Ajustes de validación/errores |
| `resources/views/mail/web/ticket-sales.blade.php` | Enlace "crear contraseña" (opcional) |

## Pruebas (manuales)

Con la opción **activa**:
1. Invitado nuevo, pago con tarjeta (CardNET sandbox) → se crea Person/User, no queda logueado, recibe correo con boletas.
2. Invitado con email de un usuario ya registrado → compra OK, boletas en la cuenta existente, sus datos de perfil no se alteran, no se inicia sesión.
3. Invitado repite compra con el mismo email → no da error de "e-mail ya registrado".
4. Email de admin/company/owner → rechazado.
5. Compra con monto 0 / cupón 100% (`payment_ok` directo).
6. Evento con boletas etiquetadas (correo por boleta).
7. Límite `max_tickets` por persona usando el email del invitado.
8. Pago fallido en CardNET → reservación y carrito se manejan igual que hoy.
9. Campos requeridos vacíos (nombre, email, confirmación, campos adicionales) → errores en pantalla.
10. Invitado luego usa "Olvidé mi contraseña" → crea clave y ve su historial.

Con la opción **apagada** (regresión): flujo actual intacto (registro con password, login, usuario logueado).

## Estimación

| Tarea | Horas |
|---|---|
| Campo en configuración del evento (form + store/update + helper) | 1.5 |
| Refactor backend: `resolveGuestBuyer` y reemplazo de `Auth::user()` en `payment`, `payment_pasarela`, `payment_ok` | 6 |
| Validaciones condicionales y manejo de email existente / roles | 2 |
| Vista checkout + JS + build | 3 |
| Correo con enlace "crear contraseña" | 1.5 |
| Pruebas completas (CardNET sandbox, monto 0, etiquetadas, regresión con opción apagada) | 4 |
| Ajustes post-prueba, despliegue y verificación en producción | 2 |
| **Total** | **20 h** (rango 18–24 h) |
