# Fase 5 — Spec Completo del Sistema Comercial

> **Source of truth técnico** de la implementación del programa comercial completo.
> CD como reseller modelo + niveles Setter/Closer/Bronze/Silver/Gold + paneles + tracking.

> **Última actualización**: 2026-05-16
> **Estado**: 🟡 SPEC en construcción · IMPLEMENTACIÓN POR FASES

---

## 1. Los 5 niveles del programa

```
SETTER          → Solo captura leads via link afiliado
  ↓ (upgrade con capacitación + 3+ conversiones)
CLOSER          → Captura + acompaña entrega básica (sin provisionar)
  ↓ (upgrade con bootcamp + 5+ closes)
RESELLER BRONZE → Capta + cierra + provisiona (capacidad limitada)
  ↓ (10+ closes acumulados)
RESELLER SILVER → Bronze + capacidades extras + bonus + co-branding
  ↓ (30+ closes acumulados)
RESELLER GOLD   → Silver + exclusividad opcional + MRR% opcional + prioridad
```

### Matriz de capacidades por nivel

| Capacidad | Setter | Closer | Bronze | Silver | Gold |
|---|---|---|---|---|---|
| Link de afiliado propio | ✅ | ✅ | ✅ | ✅ | ✅ |
| Cobra SET commission | ✅ | ✅ | ✅ | ✅ | ✅ |
| Acceso panel propio | `/setter/` | `/closer/` | `/reseller/` | `/reseller/` | `/reseller/` |
| Provisionar tenants directamente | ❌ | ❌ | ✅ (max 5/mes) | ✅ (max 15/mes) | ✅ ilimitado |
| Bulk provisioning (CSV upload) | ❌ | ❌ | ❌ | ✅ (max 10) | ✅ ilimitado |
| Cobra desarrollo del cliente | ❌ | ✅ (USD 100 onboarding básico) | ✅ (100% USD 200-600) | ✅ | ✅ |
| Cobra extras al cliente | ❌ | parcial | ✅ | ✅ | ✅ |
| Importador de data (JSON upload) | ❌ | ❌ | ✅ básico | ✅ avanzado | ✅ + templates custom |
| Plantillas personalizadas pre-armadas | template estándar | template estándar | estándar | + decks co-branded | + diseño custom |
| Soporte | email | WhatsApp grupal | WhatsApp directo lead | WhatsApp + revisión propuestas | WhatsApp 24/7 + reuniones estratégicas |
| Bonus volumen | — | — | — | USD 200 (10 closes) | USD 500 (30 closes) |
| Comisión MRR % | ❌ | ❌ | ❌ | ❌ | ✅ (caso a caso) |
| Exclusividad zona/industria | ❌ | ❌ | ❌ | ❌ | ✅ (negociable) |
| Acceso a roadmap productos nuevos | ❌ | ❌ | ❌ | preview 1 sem | preview 1 mes + voto |
| Material marketing co-branded | ❌ | ❌ | ❌ | ✅ | ✅ + custom |
| Capacitación incluida | online básica | online completa | + 1 sesión live | + 4 sesiones live | + mentoring 1-a-1 mensual |

### Reglas de upgrade entre niveles

| De → A | Trigger automático | Requiere acción |
|---|---|---|
| Setter → Closer | 3+ conversiones SET en 60 días | Tomar Setter Starter Capacitación (USD 150) |
| Closer → Bronze | 5+ closes acompañados | Aprobación CD + Bootcamp opcional |
| Bronze → Silver | 10+ closes acumulados + retención >80% | Automático (notif por email) |
| Silver → Gold | 30+ closes acumulados + retención >80% + 6 meses activo | Automático + reunión estratégica |

### Reglas de downgrade

- 90 días sin actividad → "Pausado" (mantiene tier, no cobra bonus nuevos)
- Cliente cancelaciones masivas (>30% en 30 días) → revisar caso por caso
- Comportamiento dañino (spam, false claims) → suspender + downgrade

---

## 2. Modelo de datos completo

### 2.1 Tabla `users` — campos nuevos a agregar

```sql
ALTER TABLE users
  ADD COLUMN reseller_tier ENUM('setter','closer','bronze','silver','gold') NULL,
  ADD COLUMN affiliate_code VARCHAR(20) UNIQUE NULL,
  ADD COLUMN closes_count INT DEFAULT 0,
  ADD COLUMN set_conversions_count INT DEFAULT 0,
  ADD COLUMN tier_since DATE NULL,
  ADD COLUMN last_commission_paid_at DATE NULL,
  ADD INDEX (reseller_tier),
  ADD INDEX (affiliate_code);
```

### 2.2 Tabla `commissions` (nueva)

Trackea cada comisión generada para reseller/setter/closer.

```sql
CREATE TABLE commissions (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  user_id BIGINT UNSIGNED NOT NULL,                  -- reseller/setter/closer beneficiado
  tenant_project_id BIGINT UNSIGNED NULL,            -- proyecto que generó la comisión
  type ENUM('set','close','bonus_volume','bonus_retention','mrr') NOT NULL,
  amount_usd DECIMAL(10,2) NOT NULL,
  status ENUM('pending','paid','clawback') DEFAULT 'pending',
  reason TEXT NULL,                                  -- explicación humana
  paid_at DATE NULL,                                 -- cuando se transfirió
  payment_method VARCHAR(50) NULL,                   -- transferencia, mp, etc.
  payment_reference VARCHAR(100) NULL,               -- id de comprobante
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

  FOREIGN KEY (user_id) REFERENCES users(id),
  FOREIGN KEY (tenant_project_id) REFERENCES tenant_projects(id),
  INDEX (user_id, status),
  INDEX (type),
  INDEX (paid_at)
);
```

### 2.3 Tabla `affiliate_clicks` (nueva)

Trackea cada click en un link de afiliado para atribución.

```sql
CREATE TABLE affiliate_clicks (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  affiliate_code VARCHAR(20) NOT NULL,
  ip_address VARCHAR(45) NULL,
  user_agent TEXT NULL,
  referrer VARCHAR(500) NULL,
  landing_url VARCHAR(500) NOT NULL,
  session_id VARCHAR(100) NULL,
  converted_at TIMESTAMP NULL,                       -- si terminó comprando
  converted_tenant_project_id BIGINT UNSIGNED NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

  INDEX (affiliate_code),
  INDEX (session_id),
  INDEX (converted_at),
  FOREIGN KEY (converted_tenant_project_id) REFERENCES tenant_projects(id)
);
```

### 2.4 Tabla `reseller_applications` (existente, agregar campos)

```sql
ALTER TABLE reseller_applications
  ADD COLUMN target_tier ENUM('setter','closer','bronze') DEFAULT 'bronze',
  ADD COLUMN capacitacion_cd_interested BOOLEAN DEFAULT FALSE,
  ADD COLUMN capacitacion_cd_chosen ENUM('starter','bootcamp','full_setup') NULL;
```

### 2.5 Tabla `tenant_projects` (existente, agregar campos)

```sql
ALTER TABLE tenant_projects
  ADD COLUMN tier ENUM('T1','T2','T3') NULL,         -- tier del producto vendido
  ADD COLUMN affiliate_code_used VARCHAR(20) NULL,   -- código que originó la conversión
  ADD COLUMN closer_user_id BIGINT UNSIGNED NULL,    -- si fue acompañado por Closer
  ADD COLUMN prospect_email VARCHAR(255) NULL,       -- email del lead al que el reseller le mostró el proyecto
  ADD COLUMN prospect_name VARCHAR(255) NULL,        -- nombre del lead (opcional)
  ADD INDEX (affiliate_code_used),
  ADD INDEX (closer_user_id);
```

**Nota (2026-05-17 — unificación demo/real)**: la migración intermedia
2026_05_17_100000 había agregado columnas `is_demo`, `demo_expires_at`,
`demo_converted_at`. Tras decidir que **todo proyecto del reseller debe pagarse
o muere en 15 días** sin distinción técnica demo/real, esas columnas fueron
removidas en 2026_05_17_120000. El campo existente `grace_period_end` cumple
toda esa función (set en provisión a `now+15d`, evaluado por cron `check-grace`).

### 2.6 Roles Spatie nuevos

```php
// En seeder de roles
Role::firstOrCreate(['name' => 'Setter', 'guard_name' => 'web']);
Role::firstOrCreate(['name' => 'Closer', 'guard_name' => 'web']);
// Reseller ya existe
```

### 2.7 Permissions Spatie nuevos

```php
// Setter
'setter.dashboard'              // ver su panel
'setter.affiliate.use'          // generar/ver su link

// Closer
'closer.dashboard'
'closer.affiliate.use'
'closer.client.onboard'         // hacer onboarding básico al cliente

// Reseller (todos los tiers)
'reseller.dashboard'            // ya existe
'reseller.provision'            // provisionar
'reseller.bulk_provision'       // solo Silver+
'reseller.commissions.view'     // ver dashboard comisiones
'reseller.exclusivity.negotiate' // solo Gold
```

---

## 3. Rutas y paneles

### 3.1 `/setter/` (nuevo)

```
GET  /setter/                        → Dashboard setter
GET  /setter/affiliate               → Mi link + clicks tracking
GET  /setter/conversions             → Conversiones que generé
GET  /setter/commissions             → Comisiones acumuladas + pagos
GET  /setter/help                    → Centro de recursos básico
```

### 3.2 `/closer/` (nuevo)

```
GET  /closer/                        → Dashboard closer
GET  /closer/leads                   → Leads asignados/captados
GET  /closer/onboardings             → Onboardings hechos
GET  /closer/affiliate               → Mi link + tracking
GET  /closer/commissions             → Comisiones SET + onboarding
GET  /closer/help                    → Centro de recursos
```

### 3.3 `/reseller/` (extender el existente)

Agregar a las rutas existentes:

```
GET  /reseller/commissions           → Dashboard comisiones detallado (NUEVO)
GET  /reseller/affiliate             → Mi link de afiliado + tracking (NUEVO)
GET  /reseller/tier                  → Mi categoría + progreso a próxima (NUEVO)
GET  /reseller/resources             → Centro de Recursos embed (NUEVO)
GET  /reseller/bulk-provision        → Bulk provisioning UI (NUEVO, solo Silver+)
GET  /reseller/exclusivity           → Negociar exclusividad (NUEVO, solo Gold)
```

### 3.4 Middleware nuevos

```php
// app/Http/Middleware/RestrictByResellerTier.php
class RestrictByResellerTier {
    public function handle($request, $next, $minTier = 'bronze') {
        $user = $request->user();
        $hierarchy = ['setter' => 1, 'closer' => 2, 'bronze' => 3, 'silver' => 4, 'gold' => 5];
        $userTier = $user->reseller_tier ?? 'setter';
        $minRequired = $hierarchy[$minTier] ?? 99;
        $userLevel = $hierarchy[$userTier] ?? 0;
        if ($userLevel < $minRequired) {
            abort(403, "Tu categoría ({$userTier}) no permite esta acción. Requiere: {$minTier}+");
        }
        return $next($request);
    }
}

// Uso en rutas:
Route::middleware(['auth', 'reseller_tier:silver'])->get('/reseller/bulk-provision', ...);
Route::middleware(['auth', 'reseller_tier:gold'])->get('/reseller/exclusivity', ...);
```

### 3.5 Tracking de link de afiliado (middleware)

```php
// app/Http/Middleware/TrackAffiliateClick.php
class TrackAffiliateClick {
    public function handle($request, $next) {
        $code = $request->query('r') ?? $request->query('s');
        if ($code) {
            // Validar que el code existe
            $exists = User::where('affiliate_code', $code)->exists();
            if ($exists) {
                // Setear cookie 30 días
                Cookie::queue('affiliate_code', $code, 60 * 24 * 30);
                // Trackear en DB
                AffiliateClick::create([
                    'affiliate_code' => $code,
                    'ip_address' => $request->ip(),
                    'user_agent' => $request->userAgent(),
                    'referrer' => $request->header('referer'),
                    'landing_url' => $request->fullUrl(),
                    'session_id' => $request->session()->getId(),
                ]);
            }
        }
        return $next($request);
    }
}

// Registrar global en bewpro.com
```

### 3.6 Cron mensual de cálculo de comisiones

```php
// app/Console/Commands/CalculateMonthlyCommissions.php
class CalculateMonthlyCommissions extends Command {
    protected $signature = 'bewpro:calculate-commissions {--month=last}';

    public function handle() {
        $month = $this->option('month') === 'last' ? now()->subMonth() : now();
        $startOfMonth = $month->copy()->startOfMonth();
        $endOfMonth = $month->copy()->endOfMonth();

        // 1. SET commissions: por cada AffiliateClick que terminó en venta este mes
        $sets = AffiliateClick::whereNotNull('converted_at')
            ->whereBetween('converted_at', [$startOfMonth, $endOfMonth])
            ->with('convertedTenantProject')
            ->get();
        foreach ($sets as $set) {
            $user = User::where('affiliate_code', $set->affiliate_code)->first();
            $tier = $set->convertedTenantProject->tier ?? 'T1';
            $amount = match($tier) {
                'T1' => 20.00,
                'T2' => 30.00,
                'T3' => 40.00,
            };
            Commission::firstOrCreate([
                'user_id' => $user->id,
                'tenant_project_id' => $set->converted_tenant_project_id,
                'type' => 'set',
            ], [
                'amount_usd' => $amount,
                'reason' => "Lead afiliado convertido — Tier {$tier}",
            ]);
        }

        // 2. Bonus volumen: si el reseller alcanzó 10 o 30 closes este mes
        foreach (User::whereIn('reseller_tier', ['bronze','silver','gold'])->get() as $reseller) {
            $closesThisMonth = TenantProject::where('parent_reseller_id', $reseller->id)
                ->where('pipeline_status', 'active')
                ->whereBetween('created_at', [$startOfMonth, $endOfMonth])
                ->count();
            $totalCloses = TenantProject::where('parent_reseller_id', $reseller->id)
                ->where('pipeline_status', 'active')->count();

            // Bonus Silver (10 acumulado)
            if ($totalCloses >= 10 && $reseller->reseller_tier === 'bronze') {
                Commission::firstOrCreate([
                    'user_id' => $reseller->id,
                    'type' => 'bonus_volume',
                    'reason' => 'Bonus Silver — 10 closes alcanzados',
                ], ['amount_usd' => 200.00]);
                $reseller->update(['reseller_tier' => 'silver', 'tier_since' => now()]);
            }
            // Bonus Gold (30 acumulado)
            if ($totalCloses >= 30 && $reseller->reseller_tier === 'silver') {
                Commission::firstOrCreate([
                    'user_id' => $reseller->id,
                    'type' => 'bonus_volume',
                    'reason' => 'Bonus Gold — 30 closes alcanzados',
                ], ['amount_usd' => 500.00]);
                $reseller->update(['reseller_tier' => 'gold', 'tier_since' => now()]);
            }
        }

        // 3. Enviar reporte por email a cada reseller con comisiones pending
        // ...
    }
}

// Scheduled en kernel:
$schedule->command('bewpro:calculate-commissions')->monthly();
```

### 3.7 Lifecycle unificado de proyectos del reseller (2026-05-17)

**Decisión de producto (founder)**: no hay distinción técnica entre "demo" y
"proyecto real". Todo tenant que un Reseller provisiona arranca con
`grace_period_end = now + 15 días`. El cron `bewpro:check-grace` ya existente
(diario, 09:00 UTC) evalúa proyectos vencidos y los pasa a `paused` → `archived`.

```
[Reseller provisiona]
   ↓ /reseller/projects/new
TenantProject {
  parent_reseller_id = reseller.id,
  pipeline_status = required,
  grace_period_end = now() + 15d,
  prospect_email = <email del lead>,
  prospect_name = <nombre del lead>,
  stripe_customer_id = 'reseller_{id}_{slug}'  (mock — hasMockStripeCustomer()=true)
}
   ↓ cron provisiona en VPS (~25 min)
pipeline_status = on_development → active
   ↓ tenant URL viva, reseller comparte con prospect
   ↓
   ├─ Prospect activa pago Stripe en 15 días
   │    ↓ webhook checkout.session.completed
   │    ↓ pipeline_status mantiene active, stripe_customer_id real reemplaza al mock
   │    ↓ grace_period_end queda como referencia histórica
   │    ↓ TenantProjectObserver: closes_count++ del beneficiary
   │    └─ Proyecto permanente, cobros mensuales recurrentes
   │
   └─ No paga en 15 días
        ↓ check-grace cron (Airtable formula)
        ↓ pipeline_status → paused → archived
        └─ Tenant queda en /reseller/activity filtro "Expirados"
```

**Vistas del reseller**:
- `/reseller/projects/new` — único provisionador. Form con dropdown de cores
  (9 disponibles), JSON content opcional, alert visible "15 días de vida"
- `/reseller/activity` — "En Vivo": todos los proyectos del reseller con timer
  de grace, ordenados por urgencia (más cercanos a vencer primero), filtros
  live/paid/expired
- `/reseller/commissions` — "Mi Portafolio": KPIs sin USD (totales / mes /
  activos / no concretados), histórico filtrable
- `/reseller/customers` — clientes con su lista de proyectos
- `/reseller/projects` — listado con producto + tier + alta

**Helpers del modelo TenantProject**:
- `getGraceDaysLeftAttribute()` → días restantes (negativo si expiró)
- `isGraceExpiringSoon(int $days = 3)` → boolean
- `hasMockStripeCustomer()` → detecta `cus_demo_*`, `cus_test_*`, `cus_pending_*`,
  `cus_local_*`, `cus_mock_*`, `reseller_*` (no abre Stripe Portal con esos)

---

## 3.8 Gaps abiertos (post-audit 2026-05-17)

Los siguientes flujos están **identificados pero no implementados todavía** —
necesarios para que el ciclo cliente-reseller-pago cierre end-to-end:

### Gap A — Activación de pago del cliente final
- **Hoy**: el reseller provisiona con `stripe_customer_id` mock. El cliente
  final no tiene un link claro para activar su suscripción.
- **Necesario**: endpoint `/projects/{slug}/activate?token={uuid}` que abra
  Stripe Checkout con `customer_email` pre-filled. El token vive en la sesión
  del reseller cuando crea el proyecto.
- **Impacto**: sin esto, ningún proyecto reseller pasa a "pagando" en producción.

### Gap B — Sync TenantProject reseller ↔ Stripe webhook
- **Hoy**: `StripeWebhookController::handleCheckoutSessionCompleted()` crea
  un Project Airtable nuevo, sin linkear al TenantProject reseller existente.
  Resultado: dos proyectos desincronizados.
- **Necesario**: el webhook debe buscar primero TenantProject por
  `airtable_record_id` o por un token de invitación, y actualizar
  (no crear duplicado).

### Gap C — Cron `check-grace` no incluye `Required`
- **Hoy**: formula Airtable solo evalúa `On Development` / `Onboarding`.
  Proyectos que quedan en `Required` (falló provisión) no se archivan nunca.
- **Necesario**: extender formula o reemplazar con query local de
  `grace_period_end < now()` sobre `tenant_projects`.

### Gap D — Notificación in-app al reseller de vencimientos
- **Hoy**: `bewpro:check-renewals` solo manda email.
- **Necesario**: banner en `/reseller/activity` listando proyectos cuya
  grace_period_end < now()+3 days.

### Gap E — Email al cliente del reseller
- **Hoy**: cliente solo recibe credenciales de su admin tras provisión, sin
  contexto de los 15 días ni link de checkout.
- **Necesario**: email "Tu sitio está listo — Activá tu suscripción para
  mantenerlo" con CTA directo al checkout.

Estos gaps están listados también en `panel-reseller.md` como TODO operativo.

---

## 4. Setup inicial de CD como reseller Gold (referencia)

CD es el reseller modelo. Setup:

```php
// app/Console/Commands/SetupCdAsResellerGold.php
class SetupCdAsResellerGold extends Command {
    protected $signature = 'bewpro:setup-cd-reseller-gold';

    public function handle() {
        $cd = User::where('email', 'lacompaniad@gmail.com')->firstOrFail();

        // Asignar rol Reseller si no lo tiene
        if (!$cd->hasRole('Reseller')) {
            $cd->assignRole('Reseller');
        }

        // Setear tier Gold
        $cd->update([
            'reseller_tier' => 'gold',
            'tier_since' => now()->subYear(),  // Gold desde hace 1 año
            'affiliate_code' => 'CD',          // código corto/memorable
            'closes_count' => TenantProject::where('parent_reseller_id', $cd->id)
                                ->where('pipeline_status', 'active')->count(),
        ]);

        $this->info("✓ CD setup como Reseller Gold");
        $this->info("  Affiliate code: CD");
        $this->info("  Link: https://bewpro.com/?r=CD");
        $this->info("  Closes acumulados: {$cd->closes_count}");
    }
}
```

---

## 5. Centro de Recursos (embed en sidebar)

Agregar en `config/demo1/menu.php` para roles Setter/Closer/Reseller:

```php
[
    'title' => 'Centro de Recursos',
    'role' => ['Reseller', 'Setter', 'Closer'],
    'path' => 'reseller/resources',
    'icon' => [
        'svg' => theme()->getSvgIcon('demo1/media/icons/duotune/files/fil021.svg', 'svg-icon-2'),
        'font' => '<i class="bi bi-book fs-2"></i>',
    ],
],
```

Vista `/reseller/resources` muestra:

```
[Centro de Recursos]
├── 📚 Documentación
│   ├── Catálogo de productos (link)
│   ├── Speech de ventas (link)
│   ├── Pricing y comisiones (link)
│   ├── Plantillas de propuesta (link)
│   ├── Playbooks (4 docs)
│   └── FAQ del reseller (link)
├── 🎥 Videos
│   ├── Tour del panel (embed)
│   ├── Cómo provisionar (embed)
│   ├── 9 demos de productos comentados
│   └── Manejo de objeciones
├── 🎨 Material marketing
│   ├── Logos BewPro (download)
│   ├── Decks pre-armados (download)
│   └── Screenshots demos (download)
└── 📞 Soporte
    ├── WhatsApp lead asignado (link)
    ├── Email soporte L2
    └── Estratégico (Coke)
```

---

## 6. Dashboard de comisiones (vista nueva)

URL: `/reseller/commissions`

```
[Dashboard Comisiones]
┌─────────────────────────────────────────────────────────────┐
│  MI CATEGORÍA: Silver                                        │
│  Closes acumulados: 23 / 30 para Gold                       │
│  ████████████████████░░░░░░ 77%                              │
└─────────────────────────────────────────────────────────────┘

ESTE MES (Mayo 2026)                                Total: USD 280
┌──────────────────────────────────────────────────────────────┐
│ Comisiones SET (afiliados):              USD 80              │
│   - Lead Pérez (Tier 2) → USD 30                             │
│   - Lead García (Tier 3) → USD 40                            │
│   - Lead Romero (Tier 1) → USD 20 (pending)                  │
│                                                               │
│ Bonus alcanzados:                        USD 200             │
│   - Silver tier reached (10 closes)                          │
│                                                               │
│ Próximo pago: 31/05/2026                                     │
│ Método: Transferencia CBU registrado                         │
└──────────────────────────────────────────────────────────────┘

HISTÓRICO DE PAGOS
┌──────────────────────────────────────────────────────────────┐
│ Abril 2026:  USD 130   (3 SET + 0 bonus)        ✅ Pagado    │
│ Marzo 2026:  USD 90    (3 SET)                  ✅ Pagado    │
│ Febrero 2026: USD 40   (2 SET)                  ✅ Pagado    │
└──────────────────────────────────────────────────────────────┘

[Botón: Exportar histórico CSV]
[Botón: Modificar método de pago]
```

---

## 7. Plan de implementación por fases

### Fase 5.1 — Foundations (3-5 días)

- [ ] Migration: agregar campos a `users`, `tenant_projects`, `reseller_applications`
- [ ] Migration: crear `commissions` table
- [ ] Migration: crear `affiliate_clicks` table
- [ ] Seeder: roles Setter/Closer + permissions nuevos
- [ ] Modelo: `Commission`, `AffiliateClick`
- [ ] Comando: `bewpro:setup-cd-reseller-gold`

### Fase 5.2 — Tracking afiliado (2-3 días)

- [ ] Middleware: `TrackAffiliateClick` global en bewpro.com
- [ ] Generar `affiliate_code` único por reseller al alta
- [ ] Endpoint para conversiones (hook desde Stripe webhook)
- [ ] Test E2E: visitar `?r=CD` → comprar → ver Commission generated

### Fase 5.3 — Panel `/setter/` y `/closer/` (4-5 días)

- [ ] Controllers: SetterDashboardController, CloserDashboardController
- [ ] Views: dashboard básico de setter + closer
- [ ] Sidebar menu adaptado a cada rol
- [ ] Onboarding panel para nuevos setters/closers

### Fase 5.4 — Dashboard de comisiones en `/reseller/` (3-4 días)

- [ ] Controller: `ResellerCommissionsController`
- [ ] Vista: dashboard con tier progress + comisiones del mes + histórico
- [ ] Endpoint para exportar CSV
- [ ] Endpoint para modificar método de pago

### Fase 5.5 — Niveles + restricciones (3-4 días)

- [ ] Middleware: `RestrictByResellerTier`
- [ ] Auto-upgrade Bronze → Silver → Gold
- [ ] Tier limits para provisión (max N proyectos/mes según tier)
- [ ] Notificaciones por email al alcanzar nuevo tier

### Fase 5.6 — Cron + emails (2-3 días)

- [ ] Comando: `bewpro:calculate-commissions`
- [ ] Mailable: `CommissionReportMail` (mensual)
- [ ] Scheduled task en kernel
- [ ] Dashboard admin para ver y aprobar pagos

### Fase 5.7 — Centro de Recursos (1-2 días)

- [ ] Vista: `/reseller/resources` con links a doc + videos + downloads
- [ ] Sidebar item en menu.php para los 3 roles

### Fase 5.8 — Bulk provisioning UI (Silver+) (2-3 días)

- [ ] Vista: `/reseller/bulk-provision`
- [ ] Form CSV upload + JSON folder
- [ ] Backend: wrapper sobre `bewpro:bulk-provision` (que ya existe)
- [ ] Restricción por tier (Silver max 10 / Gold ilimitado)

**Total estimado: 20-30 días de dev** para implementación completa.

---

## 8. Workarounds operacionales mientras se implementa

Mientras Fase 5 no esté 100% en código, CD opera con workarounds:

### CD como Reseller Gold modelo

- ✅ Tiene los 29 proyectos pipelined (ya hecho hoy)
- 🟡 Tier "Gold" se trackea manualmente (no en DB todavía)
- 🟡 Comisiones se calculan en Sheet de CD (manual)
- 🟡 Link de afiliado: simbólico (`bewpro.com/?r=CD`) — no trackea hasta que esté el middleware

### Para sumar nuevo reseller hoy

- ✅ Aplicación vía form (ya existe)
- ✅ Aprobación manual via panel `/admin/resellers/`
- 🟡 Asignación de tier: manual en DB
- 🟡 Pagos de comisiones: manual cada mes via transferencia

### Para sumar Setter o Closer hoy

- ❌ NO posible hasta que estén los roles + paneles
- Workaround: aceptarlos como Reseller Bronze "junior" con seguimiento extra

---

## 9. Métricas para validar Fase 5

| Métrica | Cómo se mide | Objetivo |
|---|---|---|
| Tiempo desde aplicación → primera venta | Date diff | < 30 días |
| Conversión link afiliado → venta | clicks / conversions | > 5% |
| Tasa retención reseller 90 días | activos / aprobados | > 70% |
| Tier upgrade automático sin issues | bugs reportados | 0 |
| Pagos mensuales sin errores | reportados | 0 |
| Tiempo provisión via bulk Silver | minutos | < 30 |

---

## 10. Próximos pasos inmediatos (hoy)

Voy a implementar **Fase 5.1 + 5.2 parcial + 5.4 (CD setup)** en este turno:

1. Migrations: agregar campos a users + commissions + affiliate_clicks
2. Modelos: Commission, AffiliateClick
3. Roles Spatie: Setter, Closer (seeder)
4. Comando: `bewpro:setup-cd-reseller-gold` (setup CD)
5. Doc spec completo (este archivo) committed
6. Roadmap del resto de fases documentado

Después coordinamos:
- Cuándo construir Fase 5.3 (paneles /setter/ y /closer/)
- Cuándo construir Fase 5.5+ (cron, emails, etc.)
- Si los leads de CD (los 29 que ya están) se procesan o ya están en proceso

---

## Referencias

- Doc pricing y comisiones: [`kit-comercial/pricing-y-comisiones.md`](kit-comercial/pricing-y-comisiones.md)
- Panel reseller actual: [`operaciones/panel-reseller.md`](operaciones/panel-reseller.md)
- Proceso de aplicación: [`onboarding-reseller/proceso-aplicacion.md`](onboarding-reseller/proceso-aplicacion.md)
- Hub completo: [`README.md`](README.md)
