CHEFMANAGER IA 3.8.0 · VALIDACIÓN DE ENTREGA (FOOD COURT FASES 2 Y 3)
========================================================================

A diferencia de docs/VALIDACION-3.7.0.txt (que llamó a FoodCourtService::
dispatch directamente en CLI), esta validación levantó un servidor PHP real
(`php -S`) sirviendo el paquete completo y ejecutó peticiones HTTP reales con
cookies de sesión, igual que lo haría un navegador o una app cliente. Esto es
precisamente lo que la validación anterior marcó como pendiente ("falta una
prueba HTTP real") y encontró un error real que la prueba en CLI no podía ver
(ver CORRECCIONES en docs/RELEASE-3.8.0.txt).

RESULTADOS ESTRUCTURALES

- php -l sobre los archivos PHP modificados/nuevos: sin errores de sintaxis.
- Instalación SQLite nueva ejecutando Database::connection() de verdad:
  conecta sin errores, PRAGMA integrity_check=ok, 0 violaciones de llaves
  foráneas, 176 tablas (174 de la 3.7.0 + food_court_orders y
  food_court_order_operators), columna orders.food_court_order_id presente.
  Migración sqlite-v20-food-court-orders.sql aditiva y no falla sobre una
  base que ya tiene la 3.7.0 completa; segunda conexión (idempotencia) sin
  errores.

PRUEBA FUNCIONAL DE EXTREMO A EXTREMO (HTTP real contra `php -S`, con cookies
de sesión reales — no simulacros ni invocación directa de clases)

Se instaló una base nueva y se crearon por HTTP: una cuenta de plataforma
(Super Administrador), y dos negocios nuevos e independientes ("Comidas Test
B" y "Pollos Test C") usando el endpoint real POST /restaurants del Control
Center, cada uno con su propio dueño ("Local").

1. Login de la cuenta de plataforma → devuelve permisos, csrf_token y
   is_platform_user=true.
2. POST /food-courts (crear patio) → primer intento real por HTTP: rechazado
   con 403 por la barrera de recursos de plataforma (error real encontrado,
   corregido, ver RELEASE-3.8.0.txt). Reintentado tras la corrección → 201,
   patio creado.
3. POST /food-courts/{id}/operators dos veces (invitar la sucursal de
   "Comidas Test B" y la de "Pollos Test C") → 201 cada una, filas en
   food_court_operators en estado pending.
4. Login de cada dueño de negocio (sin ningún permiso de plataforma) → PUT
   /food-courts/{id}/operators/{id} {status:"active"} cada uno → "Te uniste
   al patio de comidas."
5. El dueño de "Comidas Test B" intenta ponerse a sí mismo en "suspended" →
   rechazado con 422, igual que en la validación de la Fase 1.
6. GET /api/public.php?action=food-court&slug=... (sin sesión previa,
   simulando un cliente nuevo) → devuelve el directorio agregado con ambos
   operadores abiertos (vía business_hour_overrides) y sus catálogos, y
   siembra+devuelve csrf_token de sesión pública (corrección de esta
   entrega, ver RELEASE-3.8.0.txt).
7. POST /api/public.php?action=food-court-order con un carrito de dos líneas,
   una por cada operador, usando el csrf_token del paso 6 y la cookie de
   sesión pública del mismo cliente → 201, "Pedido enviado a 2 negocio(s)".
   Total calculado correctamente (2×25 + 1×60 = 110). Devuelve un pedido real
   por operador (order_id, order_number) y el pedido maestro agregador
   (group_number, token público).
8. El dueño de "Comidas Test B" consulta GET /orders (su vista normal, sin
   ningún cambio) → el subpedido del patio aparece como un pedido más, con
   source="food_court" y el contexto del patio en sus notas ("Pedido de
   patio de comidas..."). Confirma que ningún negocio necesita pantalla
   nueva para trabajar sus pedidos de patio de comidas.
9. GET /api/public.php?action=food-court-track&token=... (sin sesión, con
   solo el token público del pedido maestro) → estado conjunto "pending"
   mientras ambos subpedidos están pendientes.
10. Se avanza un subpedido a "ready" y el otro se deja en "pending" → el
    seguimiento público recalcula el estado conjunto a "pending" (el
    operador más lento gobierna, aunque el otro ya esté listo).
11. Se avanza el segundo subpedido también a "ready" → el estado conjunto
    pasa a "ready".
12. Se cancelan ambos subpedidos → el estado conjunto pasa a "cancelled".
13. GET /food-courts/{id}/orders (vista de administración del patio, como la
    cuenta de plataforma) → lista el pedido maestro con su estado conjunto
    recalculado ("cancelled" tras el paso 12) y el conteo de operadores.
    GET /food-courts/{id}/orders/{groupId} → mismo detalle con el desglose
    completo de items por operador.
14. El dueño de "Comidas Test B" (no administrador del patio) intenta GET
    /food-courts/{id}/orders → rechazado con 403 ("No tienes permiso para
    administrar este patio de comidas"). Confirma que la vista de pedidos
    del patio, que expone datos de contacto del cliente y de varios negocios
    a la vez, queda fuera del alcance de un negocio individual.

VALIDACIONES DE AISLAMIENTO Y ENTRADA INVÁLIDA (HTTP real)

- POST food-court-order con un operator_id que no pertenece al patio → 422
  ("Uno de los operadores no pertenece a este patio de comidas.").
- POST food-court-order sin el header del token CSRF público → 419 ("Sesión
  de pedido vencida. Recarga el menú.").
- POST food-court-order con un product_id real pero de OTRO operador del
  mismo patio (cruzando restaurant_id) → 422 ("Uno de los productos ya no
  está disponible."), confirmando que fcResolveItems() resuelve cada línea
  contra el restaurant_id/branch_id del operador que el cliente declaró y
  nunca contra otro negocio, aunque el producto exista y esté publicado.

NO CUBIERTO POR ESTA VALIDACIÓN

- No hay interfaz de usuario que probar en esta fase (ver
  docs/RELEASE-3.8.0.txt): el carrito multi-operador, el seguimiento del
  cliente y el panel de pedidos del patio son solo API.
- No se probó delivery ni cupones en el pedido de patio de comidas: esta
  fase solo admite "para recoger", según lo documentado.
- No se ejecutó el flujo de cobro (payments/sales) sobre un subpedido de
  patio de comidas; el avance de estado se verificó actualizando
  orders.status directamente para aislar la prueba de la política de
  pago-antes-de-cocina, que es lógica preexistente y no forma parte de esta
  entrega (ver "ANTES DE PRODUCCIÓN" en docs/RELEASE-3.8.0.txt).
- No se probó con más de dos operadores en el mismo pedido maestro, ni con
  operadores en distintas zonas horarias o monedas.
