# Reseller End-to-End Test — Playbook

> Workflow canónico para probar el flow completo del reseller en producción.
> Reproducible cada vez que se necesita validar cambios o entrenar un reseller nuevo.
>
> Última actualización: 2026-05-12

---

## Pre-condiciones

- [ ] Último commit del repo `cd-system` deployado a VPS1 (CI auto-deploy en push a `cd-system` branch — verificar con `gh run list --workflow=deploy.yml --limit=1`).
- [ ] Cron `apply-pending-imports.sh` activo en VPS1 (verificar con `ssh vps1-claude 'crontab -l | grep apply-pending'`).
- [ ] El reseller tiene rol `Reseller` + login funcional en `bewpro.com`.
- [ ] Subdomain a usar está libre (sin TenantProject ni cuenta cPanel previa con ese nombre).

---

## Test E2E — Pasos

### 1. Login como reseller en producción

```
https://bewpro.com/login
```

Click derecho redirige a `/reseller` (dashboard reseller con 5 items en sidebar).

### 2. Provisionar proyecto nuevo

```
https://bewpro.com/reseller/projects/new
```

Form fields:
- **Customer email**: email que recibe las credenciales del panel admin del tenant. **Recomendado en testing**: usar tu propio email para no enviar credenciales reales a personas externas.
- **Project name**: nombre comercial del cliente (ej. "Jürgen Klaric", "Estudio Pérez & Asociados"). Solo display.
- **Subdomain**: kebab-case (ej. `klaric`, `estudio-perez`). Va a ser la URL: `{subdomain}.bewpro.com`. **Debe ser único** — si está tomado, el form devuelve error.
- **Core**: dropdown con los 9 cores del marketplace. Elegir el que mejor matchea la industria del cliente.
- **JSON de contenido (opcional)**: subir un JSON con la data del cliente. Sin esto, el tenant sale con seed canónico (placeholder genérico).

#### Sub-flow: descargar template JSON

Tras elegir un core en el dropdown, aparece el botón **📥 Descargar JSON modelo**. Descarga:
- Estructura completa para ese core: `site` (welcome/about/contact/tagline/description/header/footer/seo) + módulos activos
- Defaults realistas por industria
- Comentario `_meta` con instrucciones

**El template descargado en producción cubre el 100% de los campos editables del demo** (después del fix del 2026-05-12, antes solo cubría welcome/about/contact básicos).

Workflow recomendado:
1. Descargar template
2. Editar campo por campo con info real del cliente
3. Verificar uniqueness de slugs (el form auto-dedupe pero mejor evitar)
4. Subir el JSON editado en el form

### 3. Submit

Tras submit:
- Toast/swal: "¡Proyecto en proceso! El cron va a provisionar el sitio en los próximos ~25 minutos..."
- Redirige a `/reseller/projects` con el nuevo proyecto listado (status: `required`)

### 4. Lo que pasa automáticamente

```
[T+0]    Form crea Airtable Project (Pipeline_Status=Required) + TenantProject local
         JSON stasheado en bewpro22:/storage/app/reseller-imports/{record_id}.json
         Slack notif: "💰 Nueva compra en BewPro" + "🚀 Provisioning solicitado"

[T+1min] Cron orchestrator (process-airtable2.sh):
         · Detecta Pipeline_Status=Required
         · Crea cuenta cPanel + DB + MySQL user + DNS
         · Clona repo + composer install + .env
         · Corre bewpro:new (migrations + seed canónico + brand defaults)
         · Activa AutoSSL Let's Encrypt
         · Pipeline_Status=On Development

[T+~25min] Provisioning completo:
           · Slack notif: "✅ Provisioning completado"
           · Email a customer_email con credenciales del panel admin

[T+~26min] Cron poller (apply-pending-imports.sh):
           · Detecta stash JSON
           · Copia al tenant + DELETE secciones afectadas
           · Corre bewpro:seed → aplica el JSON
           · Borra stash (signal de "completed")
```

### 5. Verificación

#### En vivo

```bash
# Status del provisioning
ssh vps1-claude 'tail -30 /var/log/process-airtable.log'

# Status del JSON import
ssh vps1-claude 'tail -30 /var/log/bewpro-apply-imports.log'

# Verificar tenant DB tiene el contenido
ssh vps1-claude 'mysql {cpanel_user}_bp -e "SELECT COUNT(*) FROM services; SELECT COUNT(*) FROM team_members; SELECT COUNT(*) FROM posts;"'

# Verificar sitio público
curl -s https://{subdomain}.bewpro.com/ | grep -oE "{texto-del-json}"
```

#### Acceptance criteria

- [ ] Sitio responde HTTP 200 en `https://{subdomain}.bewpro.com/`
- [ ] Hero / about / contact muestran texto del JSON (no placeholders genéricos)
- [ ] Módulos activos (blog, gallery, services, team, etc.) renderean los records del JSON
- [ ] Email customer recibió credenciales
- [ ] Cliente puede login en `https://{subdomain}.bewpro.com/login` con credenciales
- [ ] Cliente puede editar TODOS los campos del site desde `/admin/site-data` (welcome + about + contact tabs)
- [ ] Cliente puede editar items de módulos (`/admin/blog`, `/admin/services`, etc.)

---

## Troubleshooting

### Sitio muestra contenido genérico en lugar del JSON

| Síntoma | Diagnóstico | Fix |
|---------|-------------|-----|
| Stash en `/home/bewpro22/.../reseller-imports/` está vacío | Form se submitió en local (APP_ENV!=production) → JSON quedó en local | `php artisan bewpro:push-reseller-stash` desde local |
| `bewpro-apply-imports.log` muestra "FAILED" | bewpro:seed colisionó por slugs duplicados o tabla missing | Revisar log detallado, dedupe manual del JSON o crear seed faltante |
| Site rendera defaults del core preset (`tagline`, `description`) | Template viejo no incluía top-level fields (pre-2026-05-12 fix) | Re-descargar template + completar fields top-level |

### Provisioning falla

Ver `/var/log/process-airtable.log`. Errores comunes documentados en:
- `docs/bewpro2.0/operaciones/reseller-provisioning.md`

### Cleanup tenant fallido

Para re-intentar con el mismo subdomain tras un fail:

```bash
# 1. Drop cuenta cPanel + DB + DNS
ssh vps1-claude 'whmapi1 removeacct user={cpanel_user} keepdns=0'

# 2. Delete record Airtable
ssh vps1-claude 'source /root/scripts/.airtable.env && curl -X DELETE \
  -H "Authorization: Bearer $AIRTABLE_TOKEN" \
  "https://api.airtable.com/v0/$AIRTABLE_BASE_ID/tblzCgJZCbbt5j13Q/{record_id}"'

# 3. Delete TenantProject local
ssh vps1-claude 'su - bewpro22 -c "cd public_html/bewpro && php artisan tinker --execute=\"\\App\\Models\\TenantProject::where(\\\"domain\\\", \\\"{subdomain}.bewpro.com\\\")->delete();\""'

# 4. Delete stash si quedó
ssh vps1-claude 'rm -f /home/bewpro22/public_html/bewpro/storage/app/reseller-imports/{record_id}.json'
```

---

## Resultados validados

| Fecha | Tenant | Core | Records | Resultado |
|-------|--------|------|---------|-----------|
| 2026-05-11 | sushifeel.bewpro.com | restaurant-bar | 79 | ✅ |
| 2026-05-11 | nunesyasociados.bewpro.com | law-firm-digital | 110 | ✅ |
| 2026-05-11 | klaric.bewpro.com (v1) | personal-brand | 80 (parcial — template viejo) | ⚠️ con gap detectado |
| 2026-05-12 | klaric.bewpro.com (v2) | personal-brand | _pendiente test post-fix_ | _pendiente_ |

---

## Métricas del sistema (post fixes 2026-05-12)

| Métrica | Valor |
|---------|-------|
| Productos certificados market-ready | 9/9 (100%) |
| Total fields editables desde admin | 582 fields |
| Paridad Vista ↔ Schema ↔ Admin ↔ JSON | 100% |
| Tiempo provisión + JSON apply | ~26 min E2E |

---

## Referencias

- Comandos: `bewpro:certify-product`, `bewpro:audit-editability`, `bewpro:push-reseller-stash`, `bewpro:validate-demo-parity`
- Doc reseller-provisioning: `docs/bewpro2.0/operaciones/reseller-provisioning.md`
- Doc product-packager: `docs/bewpro2.0/operaciones/product-packager.md`
- Schemas canónicos: `database/schemas/demos/*.json`
- Seeds canónicos: `database/seeders/products/core/seeds/*.json`
