# Prompt: Implementar multitenancy en "layoutbase" (Laravel 10 + Inertia + React)

## Contexto

`layoutbase` es un boilerplate Laravel 10 + Inertia + React con generador automático de
CRUDs (`php artisan make:crud-react`), sistema de roles/permisos por módulo con
`spatie/laravel-permission`, auditoría (`AuditoriaLog`) y módulos ya construidos:
Usuarios, Roles, Empresas, Configuración, Auditoría.

El objetivo es convertirlo en un sistema **multitenant real (una base de datos por
tenant)**, siguiendo el mismo patrón ya validado y en producción en el proyecto
`tpv-saas` (paquete `stancl/tenancy`, identificación por path/slug, BD central +
BD por tenant). No reinventar el enfoque: replicar las decisiones de tpv-saas,
adaptadas a que aquí el "tenant" convivirá con el modelo `Empresa` ya existente.

## Decisión de arquitectura (ya tomada, no reabrir la discusión)

- Paquete: `stancl/tenancy ^3.x`.
- **Multi-base de datos**: una BD física por tenant (no scoping por columna
  `tenant_id`). Prefijo de BD tipo `layoutbase_<uuid>`.
- **Identificación del tenant por segmento de path** (`/{tenant}/...`), NO por
  subdominio/dominio. Mismo motivo que en tpv-saas: más simple de desplegar sin
  gestionar DNS/wildcards por cliente.
- Separación estricta BD central vs BD tenant:
  - **Central**: tabla `tenants`, `domains` (aunque no se use para resolver,
    stancl la requiere), planes/suscripciones si aplica, y el mapa
    `tenant_user_map` (email → tenant_id) para poder resolver a qué tenant
    pertenece un usuario sin exponer la tabla `users` de cada tenant.
  - **Tenant**: todo lo de negocio — `users`, `roles`, `permissions`,
    `modulos`, `rol_modulo_permiso`, `auditoria_logs`, `configuracion`, y
    cualquier módulo de negocio nuevo generado con el CRUD generator.
- `Empresa` deja de ser "una empresa más dentro de una BD compartida" y pasa a
  ser conceptualmente el **Tenant**. Decisión concreta a implementar (ver
  sección "Punto de decisión" más abajo).

## Qué replicar exactamente de tpv-saas

1. **Modelo Tenant** (`app/Models/Tenant.php`) extendiendo
   `Stancl\Tenancy\Database\Models\Tenant`, con traits `HasDatabase`,
   `HasDomains`. Route key `slug`. Columnas mínimas: `slug`, `name`, `email`,
   `is_active`. (Añadir columnas de plan/vencimiento solo si este proyecto va a
   facturar tenants; si no, omitir esa parte de tpv-saas).

2. **Middleware de identificación por path** — copiar el patrón de
   `InitializeTenancyByPathSlug.php`:
   - Extrae el slug del primer segmento de la URL o de `route('tenant')`.
   - Excluye una lista de palabras reservadas (`login`, `dashboard`, `api`,
     `central`, `public`, etc.) para no confundir rutas centrales con rutas de
     tenant.
   - Busca `Tenant::where('slug', $slug)->first()`, valida `is_active`
     (si no, vista de error tipo `errors.tenant-disabled`).
   - Llama a `tenancy()->initialize($tenant)`.
   - **Importante (lección aprendida)**: forzar `Auth::forgetUser()` en el
     middleware antes de resolver el usuario autenticado, para evitar que el
     usuario autenticado de un tenant "se filtre" al contexto de otro tenant
     cuando el mismo proceso PHP-FPM atiende dos requests seguidos de tenants
     distintos. Este fue un bug real en tpv-saas, no una precaución teórica.

3. **Modelo User vive en la BD del tenant**, no en la central. Para poder
   hacer login sin saber de antemano a qué tenant pertenece un email, replicar:
   - Tabla central `tenant_user_map` (email, tenant_id).
   - Trait `SyncTenantUserMap` en el modelo `User` que sincroniza ese mapa en
     los eventos `created`/`updated`/`deleted`.
   - Comando `artisan` tipo `BackfillTenantUserMap` para poblar el mapa
     retroactivamente si se migran datos existentes.
   - Flujo de login: primero se resuelve el tenant por el mapa central a
     partir del email, se redirige/inicializa ese tenant, y luego se autentica
     contra la BD del tenant. (Revisar cómo lo resuelve tpv-saas exactamente en
     su `LoginController`/request de login antes de portarlo).

4. **Migraciones separadas**:
   - `database/migrations/` → esquema central (`tenants`, `domains`,
     `tenant_user_map`, y lo que sea estrictamente central).
   - `database/migrations/tenant/` → todo el esquema de negocio actual del
     boilerplate: `users`, `roles`, `permissions`, `modulos`,
     `rol_modulo_permiso`, `auditoria_logs`, `configuracion`, y las tablas de
     cualquier módulo de negocio.
   - En `config/tenancy.php`, el runner de migraciones de tenant debe apuntar
     a `database_path('migrations/tenant')` (`'--path' => [...]`).

5. **Rutas separadas**:
   - `routes/web.php` → solo rutas centrales (login/gestión de tenants si
     aplica, health checks, landing).
   - `routes/tenant.php` → todo lo que hoy vive en `web.php` de layoutbase
     (Users, Roles, Empresas/Configuración, Auditoría, y todo módulo generado
     por el CRUD generator), envuelto en `Route::prefix('/{tenant}')` con el
     middleware de tenancy, y con regex negativo para excluir palabras
     reservadas en el segmento `{tenant}`.
   - Registrar la carga de `tenant.php` desde un `TenancyServiceProvider`
     propio (copiar patrón de tpv-saas), no desde `RouteServiceProvider`
     estándar.

6. **Config `config/tenancy.php`**:
   - `central_domains` con los hosts de desarrollo/producción (aunque no se
     use resolución por dominio, stancl lo requiere para no tratar esos hosts
     como tenant).
   - Bootstrapper de BD (`DatabaseTenancyBootstrapper`), prefijo de BD,
     generador de IDs `UUIDGenerator` si se quiere UUID en vez de incremental.
   - Dejar comentado explícitamente que la identificación por dominio está
     deshabilitada a propósito (para que nadie la reactive por error después).

## Punto de decisión que SÍ hay que resolver antes de escribir código

`layoutbase` ya tiene un modelo `Empresa` con su propio CRUD (controlador,
páginas React, permisos `ver empresas`/`crear empresas`/etc.). Al meter
`stancl/tenancy`, hay solapamiento conceptual entre "Empresa" y "Tenant".
Antes de implementar, decidir una de estas dos opciones (recomendada: la primera):

- **Opción A (recomendada)**: `Tenant` (stancl) pasa a ser la entidad "empresa"
  a nivel central — una fila en `tenants` = una empresa cliente con su propia
  BD. El módulo `Empresa`/`Empresas` actual (CRUD, controlador, páginas React,
  permisos) se elimina o se re-orienta a otra cosa (p. ej. "sucursales" o
  "datos de la empresa" dentro del propio tenant, análogo a `Configuracion`).
  Ventaja: no hay dos conceptos de "empresa" compitiendo.
- **Opción B**: mantener `Empresa` como tabla dentro de cada BD de tenant
  (ej. para multi-sucursal/multi-razón social dentro de un mismo tenant), y
  `Tenant` (stancl) queda como concepto puramente técnico/interno sin
  exposición directa al usuario final. Más trabajo, solo si un tenant real
  necesita múltiples "empresas" propias.

No avanzar con la migración de datos ni con el generador de CRUD hasta que
esto esté decidido explícitamente por el usuario.

## Otros ajustes necesarios en piezas propias de layoutbase

- **`make:crud-react`**: el generador debe actualizarse para que los CRUDs
  nuevos generen su migración en `database/migrations/tenant/` (no en la raíz)
  y registren sus rutas en `routes/tenant.php` (no en `web.php`). Revisar
  `MakeCrudReact.php` y sus stubs.
- **`modulos:scan` / `DetectarModulos.php`** y **`admin:asignar` /
  `AsignarRolAdministrador.php`**: hoy asumen una única BD. Deben ejecutarse
  en contexto de tenant (`tenants:run` de stancl o iterando manualmente sobre
  cada tenant), o convertirse en comandos "tenant aware" que acepten un
  `--tenant=slug`.
- **`spatie/laravel-permission`**: las tablas de roles/permisos pasan a vivir
  en cada BD de tenant. Revisar el cacheo de permisos de spatie (usa cache
  por defecto) para que no se mezcle entre tenants — invalidar/segmentar la
  cache de permisos por tenant o desactivar el cache global de spatie y usar
  uno scoped, igual que se tuvo que cuidar el cache de usuario en tpv-saas.
- **`useAuth()` / `PermissionGuard` (frontend React)**: no deberían necesitar
  cambios de lógica, pero sí hay que verificar que las URLs que arma Inertia
  para las peticiones incluyan el slug del tenant (equivalente a
  `SetTenantUrlDefaults` en tpv-saas, para que `route()` en Blade/Inertia
  genere URLs con el prefijo `/{tenant}/...` automáticamente).
- **Sesiones**: aislar sesión por tenant (equivalente a `ScopeSessionsSafe` en
  tpv-saas) para que dos tenants abiertos en pestañas distintas del mismo
  navegador no compartan sesión/cookie.

## Errores a NO repetir (observados en tpv-saas y ya corregidos ahí, evitarlos desde el inicio aquí)

1. No dejar `Log::debug(...)` de diagnóstico en el middleware de tenancy en
   producción.
2. No dejar comentarios huérfanos tipo "// rest of file..." de ediciones a
   medias — si se edita un archivo grande de rutas, revisar el diff completo.
3. Definir **una sola** convención de nombre de columna para claves de
   scoping (en tpv-saas conviven `sucursal_id` e `id_sucursal` por una
   migración incompleta — decidir un nombre único desde el día uno si este
   proyecto añade un concepto de sucursal).
4. Probar explícitamente el escenario "dos tenants distintos en requests
   consecutivos al mismo worker PHP-FPM" para descartar fugas de sesión/auth
   cruzadas antes de dar por cerrada la migración.

## Orden de implementación sugerido

1. Resolver el "Punto de decisión" (Empresa vs Tenant) con el usuario.
2. `composer require stancl/tenancy`, publicar config y migraciones.
3. Crear modelo `Tenant`, migración `tenants`/`domains`, `config/tenancy.php`.
4. Mover migraciones de negocio existentes a `database/migrations/tenant/`.
5. Crear `TenancyServiceProvider` propio (rutas + bootstrapping).
6. Middleware `InitializeTenancyByPathSlug` + registrar en `routes/tenant.php`.
7. Separar `routes/web.php` (central) de `routes/tenant.php` (negocio actual).
8. Mover `User` a contexto tenant + tabla puente `tenant_user_map` + trait de
   sincronización + comando de backfill.
9. Ajustar flujo de login para resolver tenant a partir del email antes de
   autenticar.
10. Ajustar `make:crud-react` y comandos de módulos/roles para ser tenant-aware.
11. Probar de punta a punta: crear 2 tenants de prueba, verificar aislamiento
    total de datos, sesión y permisos entre ambos.
