# Provisión de Tenants

> Cómo crear un sitio web nuevo para un cliente, paso a paso, desde el panel `/reseller/`.

---

## 🎯 TL;DR

```
Form → 1 min      Cron VPS1 detecta → 1 min
Provisión técnica → ~25 min
Aplicación de JSON (si subiste) → ~1 min
TOTAL: ~25-30 min desde submit hasta sitio live con tu data
```

---

## 1. Pre-requisitos

Antes de provisionar:

✅ Tenés definido el **subdomain** que va a usar
✅ Tenés definido el **email del cliente o prospect** (para credenciales y futuro pago Stripe)
✅ Tenés definido el **producto/core** (uno de los 9 publicados)
✅ (Opcional pero recomendado) Tenés el JSON con la data del cliente

> **No es requisito que el cliente ya haya pagado.** Podés provisionar para
> mostrar el sitio armado al prospect (es tu mejor argumento de venta).
> Tenés **15 días** desde la creación para que el cliente active su pago Stripe,
> sino el proyecto se archiva automáticamente.

---

## 2. Proceso paso a paso

### Paso 1 — Loguearte al panel

URL: `https://bewpro.com/reseller/projects/new`
Credenciales: las que recibiste al ser aprobado.

### Paso 2 — Completar el form

| Campo | Cómo llenarlo |
|---|---|
| **Email** | Cliente o prospect — donde Stripe va a cobrar cuando active pago. NO tu email. |
| **Nombre** (opcional) | Para identificarlo rápido en tu lista de proyectos. Ej: "Juan Pérez" |
| **Nombre del proyecto** | El nombre que aparece como `site.name` en el sitio. Ej: "Estudio Jurídico Pérez Hernández" |
| **Subdomain** | Solo `[a-z0-9-]`, max 48 chars. Ej: `perez-hernandez` → URL final: `perez-hernandez.bewpro.com` |
| **Core (producto)** | Dropdown con los 9 publicados. Elegí el que matchea la industria del cliente. |
| **JSON de contenido** | (Opcional) Subí JSON con la data del cliente (ver sección 4 más abajo). |

> El form muestra un **alert amarillo "Vida del proyecto: 15 días"** que recuerda
> la regla unificada al provisionar.

### Paso 3 — Submit

Click en "Provisionar". El sistema:

1. Valida que el subdomain esté disponible
2. Crea record en Airtable con `Pipeline_Status=Required`
3. Crea registro local `TenantProject` con `parent_reseller_id=tu_id`
4. Si subiste JSON: lo guarda en server
5. Te redirige a la página de detalle del proyecto

### Paso 4 — Esperar la provisión

**Tiempos esperados**:

| Etapa | Duración |
|---|---|
| Cron VPS1 detecta el Required | 0-1 min |
| Crear cuenta cPanel/HestiaCP | 1-2 min |
| Crear DB MySQL | 30 seg |
| Clone del repo cd-system | 3-5 min |
| Install dependencies + cache | 2-3 min |
| Run migrations | 1-2 min |
| Run seeders (módulos del core) | 2-3 min |
| Configure Nginx + SSL | 2-3 min |
| **TOTAL provisión** | **~15-25 min** |
| Aplicación de JSON (si lo subiste) | +1 min |
| **TOTAL desde submit hasta live** | **~25-30 min** |

### Paso 5 — Verificar que está live

Una vez que el estado pasa a `On Development` o `Active`:

```bash
curl -I https://[subdomain].bewpro.com
# Debería retornar HTTP 200
```

O simplemente abrí en el navegador y verificá:
- ✅ El sitio carga
- ✅ El logo / nombre del cliente aparece
- ✅ Las secciones (welcome / about / contact) están armadas
- ✅ Las redes sociales linkadas funcionan
- ✅ El formulario de contacto envía email

### Paso 6 — Acceder al panel admin del cliente

URL: `https://[subdomain].bewpro.com/admin`

Credenciales: las recibís en un email al provisionar (lo recibe el cliente y vos en copia si el sistema lo manda así, sino tenés que generar password manualmente).

### Paso 7 — Cargar contenido adicional (si no usaste JSON)

Si NO subiste JSON al provisionar, ahora cargás contenido manualmente desde el panel admin:
- Editar textos de welcome / about / contact en `/admin/site-data`
- Subir logo, fotos hero, etc. en `/admin/site-data/assets`
- Crear servicios / productos / blog posts según el core
- Conectar redes sociales en `/admin/site-data/social`

Tiempo: 1-3 horas según cuánto contenido tengas.

### Paso 8 — Entrega al cliente

Una vez todo cargado:
- Mandar email de entrega (template en [`../kit-comercial/propuestas-al-cliente.md`](../kit-comercial/propuestas-al-cliente.md) sección F)
- Coordinar capacitación 30 min (incluida)
- Activar suscripción Stripe del cliente
- Cobrar 50% restante

Detalle del flujo: [`../playbooks/playbook-cierre.md`](../playbooks/playbook-cierre.md) etapas 6-10.

---

## 3. Provisión con JSON pre-cargado (MÁS RÁPIDO)

Si en lugar de cargar contenido manualmente después, subís un **JSON estructurado** al provisionar, el tenant arranca con esa data ya cargada.

### Ventajas

- ✅ Sitio LIVE con data real del cliente en 25 min (no 3-5 horas adicionales)
- ✅ Menos errores manuales
- ✅ Reusable: armás el JSON 1 vez y lo subís
- ✅ Si vendés a 13 clientes del mismo rubro, batch processing es viable

### Cómo armar el JSON

#### Opción A — Descargar template del producto

1. En el form de `/reseller/projects/new`, hay un link "Descargar template JSON del core"
2. Te descarga un JSON con la estructura esperada por ese producto
3. Editás el JSON con la data del cliente
4. Subís el JSON editado en el form

#### Opción B — Usar herramienta externa (lead hunter, etc.)

Si tenés un sistema upstream que genera JSONs (ej. el **LeadHunter**), simplemente subís el JSON que te exporta.

Doc del contrato de JSON: [`../../sprint-q2/leadhunter-instructions.md`](../../sprint-q2/leadhunter-instructions.md)

### Estructura del JSON

El JSON sigue el shape canónico de cada producto. Top-level:

```json
{
  "_meta": {
    "core": "law-firm-digital",
    "demo": "demo-law-firm-2",
    "active_modules": ["services", "team", "blog", "references", "faqs"]
  },
  "site": {
    "name": "Estudio Pérez",
    "tagline": "Abogados en Buenos Aires",
    "description": "...",
    "contact": { "address": "...", "phone": "...", "email": "..." },
    "welcome": { "hero_title": "...", "hero_subtitle": "..." },
    "about": { "main_title": "...", "main_subtitle": "..." },
    "footer": { "description": "...", "copyright_text": "..." },
    "social_media": { "instagram": { "url": "...", "active": 1 } }
  },
  "services": [
    { "slug": "derecho-familia", "title": "Derecho de Familia", "description": "..." }
  ],
  "team": [
    { "slug": "dr-perez", "name": "Dr. Pérez", "role": "Socio fundador" }
  ],
  "blog": [...],
  "faqs": [...],
  "references": [...]
}
```

**Las secciones disponibles** dependen del core. Ver template específico en [`../../sprint-q2/templates-json/`](../../sprint-q2/templates-json/).

### Validación pre-upload

Antes de subir el JSON al form, validá:

✅ Es JSON válido (no roto, sintaxis OK)
✅ `_meta.core` matchea el core que vas a provisionar
✅ Las secciones son las que el core soporta (no incluir `menu` si es `law-firm-digital`)
✅ Slugs únicos dentro de cada lista (no 2 services con mismo slug)
✅ URLs absolutas (no `cd-project/img/...` que es path interno)
✅ Encoding UTF-8

Si el JSON tiene errores, el provisioning falla con mensaje específico → ajustar y reintentar.

---

## 4. Problemas comunes y troubleshooting

### "El subdomain ya está tomado"

**Razones**:
- Ya existe otro tenant con ese subdomain
- Vos mismo lo provisionaste antes y olvidaste

**Solución**: cambiar el subdomain (sufijar -2, -3, etc.).

### Provisión queda en `Required` 5+ min

**Razón**: el cron VPS1 no lo está procesando.

**Acciones**:
1. Verificar log del cron: `tail -20 /var/log/process-airtable.log` (requiere acceso ssh)
2. Si está down, contactar a BewPro inmediatamente
3. NO crear otro proyecto encima — esperar resolución

### Provisión queda en `Processing` 30+ min

**Razón**: algún paso técnico tardó más de lo normal (DB lenta, npm install, etc.).

**Acciones**:
1. Verificar log de provisión
2. Si pasaron > 45 min, escalar a `soporte@bewpro.com`

### Provisión termina en `Failed`

**Razones comunes**:
- Cliente ya tenía cuenta cPanel/HestiaCP con ese nombre
- DB MySQL no se pudo crear (espacio en disco)
- Clone del repo falló (problema de red)

**Acciones**:
1. Ver `Pipeline_Error` o `Last_Error` en el panel
2. Cambiar Pipeline_Status a `Required` para retry
3. Si persiste, contactar soporte

### Sitio carga pero contenido placeholder (no del cliente)

**Razón**: JSON no se aplicó (cron `apply-pending-imports.sh` falló).

**Acciones**:
1. Verificar log: `tail /var/log/bewpro-apply-imports.log`
2. Verificar que el stash file existe: `ls storage/app/reseller-imports/`
3. Re-trigger manualmente: contactar soporte

### Sitio carga pero /about y /contact dan 500

**Razón conocida** (resuelta 2026-05-15): bug histórico de blade routes. Si pasa con un tenant nuevo, escalar a soporte.

---

## 5. Provisión bulk (varias a la vez)

### Para resellers individuales

El panel del reseller hoy solo permite provisión 1 por 1. Si tenés 5+ provisiones para una campaña/lanzamiento:

1. **Espaciá**: 1 cada 2-3 minutos para no saturar el cron
2. **Usá JSONs**: hacé el JSON de cada uno antes, solo subís
3. **Si son 10+**: pedí asistencia de BewPro (`coke@lacompaniadigital.com`)

### Para casos especiales (campañas masivas)

BewPro tiene un comando interno `bewpro:bulk-provision` que procesa CSV + carpeta de JSONs.

**Cuándo usarlo**:
- Lanzamiento de campaña con 10-50 leads
- Migración de clientes de otra plataforma
- Demo masivo para captación de resellers

**Cómo solicitarlo**: email a `coke@lacompaniadigital.com` con:
- CSV con la lista (formato definido en `../../sprint-q2/bulk-provisioning.md`)
- Carpeta con los JSONs por tenant
- Email del reseller que se asigna como `parent_reseller_id`

Doc técnico interno: [`../../sprint-q2/bulk-provisioning.md`](../../sprint-q2/bulk-provisioning.md)

---

## 6. Post-provisión — qué hacer

### Inmediato (5 min post-Active)

1. ✅ Verificar sitio carga (curl o navegador)
2. ✅ Verificar admin panel funciona
3. ✅ Generar password admin para el cliente (si no se generó automáticamente)
4. ✅ Verificar credenciales que vas a mandar al cliente

### Antes de mandar al cliente (10-30 min)

1. ✅ Ajustes finos de contenido (si algo quedó raro)
2. ✅ Verificar mobile responsive
3. ✅ Probar formulario de contacto (que llegue al email del cliente)
4. ✅ Verificar redes sociales linkadas

### Entrega al cliente

Ver checklist en [`../playbooks/playbook-onboarding-cliente.md`](../playbooks/playbook-onboarding-cliente.md) sección 2.

---

## 7. Convención de naming (recomendaciones)

### Subdominios

| ✅ Bien | ❌ Mal |
|---|---|
| `perez-hernandez` | `Perez_Hernandez` (mayúsculas, underscore) |
| `cafe-vista-al-mar` | `cafe-vista-al-mar-recoleta-cabanas` (muy largo) |
| `estudio-juridico-perez` | `estudio juridico perez` (con espacios) |
| `marca-personal-maria` | `pruebatesta` (suena a test) |

**Reglas**:
- Solo `a-z`, `0-9`, `-`
- Max 48 chars
- Sin espacios ni mayúsculas
- Sin guiones al inicio o final
- Auto-explicativo (matchea con el nombre del cliente/proyecto)

### Nombres de proyecto

| ✅ Bien | ❌ Mal |
|---|---|
| "Estudio Jurídico Pérez Hernández" | "PEREZ" |
| "Café Vista al Mar — Mar del Plata" | "cafe" |
| "Constructora Andes S.A." | "andes_sa_constructora" |

Es lo que el cliente verá grande en su sitio. Cuidá la presentación.

---

## 8. Casos edge

### Cliente quiere su propio dominio (no `*.bewpro.com`)

**Hoy NO se hace en el form de provisión** — primero se provisiona en bewpro.com, después se hace el setup de dominio propio:

1. Provisionar normal en bewpro.com con un subdominio
2. Después: ofrecer setup de dominio propio como extra (USD 50)
3. Configurar DNS del cliente (A record + CNAME al server BewPro)
4. BewPro genera certificado SSL para el dominio propio
5. Configurar redirect del subdomain antiguo al nuevo

### Cliente quiere proyecto multi-idioma

**Hoy no soportado nativamente**. Workaround:
- Provisionar 2 tenants separados (`cliente-es` y `cliente-en`)
- Linkear entre ambos vía nav

Doc i18n en backlog de BewPro.

### Cliente quiere clonar sitio de otro cliente

**Útil** cuando un cliente referido quiere algo "igual a [otro cliente]":

1. Provisionar el nuevo con el mismo core
2. Pedir al cliente original que exporte su JSON (si BewPro lo permite)
3. Adaptar el JSON con la data del nuevo cliente
4. Subir como JSON en la provisión

Si el cliente original no autoriza compartir su JSON, no se puede clonar legalmente.

### Cliente cambió de opinión y quiere otro producto

Migrar entre productos NO es automático. Opciones:

1. Provisionar nuevo tenant del producto correcto, descartar el viejo (cobrar trabajo extra al cliente)
2. Mantener el actual + activar/desactivar módulos (si el cambio es chico)

---

## Referencias

- Panel del reseller: [`panel-reseller.md`](panel-reseller.md)
- Facturación y cobros: [`facturación-y-cobros.md`](facturación-y-cobros.md)
- Playbook cierre (donde se inserta este paso): [`../playbooks/playbook-cierre.md`](../playbooks/playbook-cierre.md)
- Templates JSON canónicos: [`../../sprint-q2/templates-json/`](../../sprint-q2/templates-json/)
- Bulk provisioning (técnico interno): [`../../sprint-q2/bulk-provisioning.md`](../../sprint-q2/bulk-provisioning.md)
- Contrato JSON LeadHunter: [`../../sprint-q2/leadhunter-instructions.md`](../../sprint-q2/leadhunter-instructions.md)
- Doc técnico del provisioning: [`../../guia-provisionado.md`](../../guia-provisionado.md)
