fix(clientes): crear tenant como despacho desde admin global
Antes, createTenant() solo seteaba nombre, rfc, plan y databaseName. Ahora registra tenants completos como despachos: - dbMode: 'MANAGED' - verticalProfile: CONTABLE | JURIDICO | ARQUITECTURA - trialEndsAt: +30 días para plan trial - codigoPostal: opcional (se llena automáticamente de la CSF al subir FIEL) Frontend: - Selector de Tipo de Despacho en /clientes - C.P. omitido del formulario (viene de CSF -> sincronizarDatosFiscales) - Tipos Tenant y CreateTenantData actualizados Backend: - getAllTenants y getTenantById retornan verticalProfile y codigoPostal Refs: docs/sessions/2026-05-04-fix-clientes-crea-despacho.md
This commit is contained in:
130
docs/sessions/2026-05-04-fix-clientes-crea-despacho.md
Normal file
130
docs/sessions/2026-05-04-fix-clientes-crea-despacho.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# Sesión: Fix — Crear cliente desde `/clientes` registra como despacho
|
||||
|
||||
**Fecha:** 2026-05-04
|
||||
**Bug:** El admin global creaba tenants "legacy" (sin `dbMode`, `verticalProfile`, `trialEndsAt`) desde `/clientes`. Ahora se registran como despachos completos.
|
||||
|
||||
---
|
||||
|
||||
## 1. Problema
|
||||
|
||||
### Síntoma
|
||||
Cuando el administrador global creaba un nuevo cliente desde `/clientes`, el tenant se creaba sin los campos de despacho:
|
||||
- `dbMode` = `null` (debería ser `MANAGED`)
|
||||
- `verticalProfile` = `null` (debería ser `CONTABLE`/`JURIDICO`/`ARQUITECTURA`)
|
||||
- `trialEndsAt` = `null` (debería ser +30 días para plan trial)
|
||||
- `codigoPostal` no se seteaba
|
||||
|
||||
Esto causaba que el nuevo cliente no funcionara correctamente como despacho (no aparecía en el selector de despachos, no tenía trial, etc.).
|
||||
|
||||
### Causa raíz
|
||||
El flujo de **registro público** (`/register-despacho` → `signupDespacho()`) sí seteaba estos campos correctamente.
|
||||
Pero el flujo de **creación por admin** (`/clientes` → `createTenant()`) no lo hacía — solo creaba el tenant con `nombre`, `rfc`, `plan`, `databaseName`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Cambios realizados
|
||||
|
||||
### 2.1 Backend — `tenants.service.ts`
|
||||
|
||||
**Firma ampliada:**
|
||||
```ts
|
||||
export async function createTenant(data: {
|
||||
// ... campos existentes ...
|
||||
verticalProfile?: 'CONTABLE' | 'JURIDICO' | 'ARQUITECTURA';
|
||||
codigoPostal?: string;
|
||||
})
|
||||
```
|
||||
|
||||
**Creación del tenant ahora setea despacho fields:**
|
||||
```ts
|
||||
const tenant = await prisma.tenant.create({
|
||||
data: {
|
||||
nombre: data.nombre,
|
||||
rfc: data.rfc.toUpperCase(),
|
||||
plan,
|
||||
databaseName,
|
||||
dbMode: 'MANAGED',
|
||||
verticalProfile: data.verticalProfile || 'CONTABLE',
|
||||
codigoPostal: data.codigoPostal || undefined,
|
||||
trialEndsAt: isTrial ? new Date(Date.now() + 30 * 24 * 60 * 60 * 1000) : undefined,
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**Queries `getAllTenants` y `getTenantById` ahora retornan:**
|
||||
- `verticalProfile`
|
||||
- `codigoPostal`
|
||||
|
||||
### 2.2 Backend — `tenants.controller.ts`
|
||||
|
||||
El endpoint `POST /tenants` ahora acepta y pasa:
|
||||
- `verticalProfile` (default: `'CONTABLE'`)
|
||||
- `codigoPostal` (opcional)
|
||||
|
||||
### 2.3 Frontend — `clientes/page.tsx`
|
||||
|
||||
**Nuevo campo en el formulario:** `Tipo de Despacho`
|
||||
- Select con opciones: Contable, Jurídico, Arquitectura
|
||||
- Default: Contable
|
||||
- Se persiste al crear y se carga al editar
|
||||
|
||||
**C.P. deliberadamente omitido:**
|
||||
El campo `codigoPostal` **no** se incluyó en el formulario. Se obtiene automáticamente de la CSF cuando el dueño del despacho sube su FIEL por primera vez (`fiel.service.ts` → `consultarConstancia()` → `sincronizarDatosFiscales()`).
|
||||
|
||||
### 2.4 Frontend — `lib/api/tenants.ts`
|
||||
|
||||
Tipos actualizados:
|
||||
```ts
|
||||
export interface Tenant {
|
||||
// ... campos existentes ...
|
||||
verticalProfile?: 'CONTABLE' | 'JURIDICO' | 'ARQUITECTURA' | null;
|
||||
codigoPostal?: string | null;
|
||||
}
|
||||
|
||||
export interface CreateTenantData {
|
||||
// ... campos existentes ...
|
||||
verticalProfile?: 'CONTABLE' | 'JURIDICO' | 'ARQUITECTURA';
|
||||
codigoPostal?: string;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Flujo resultante
|
||||
|
||||
```
|
||||
Admin global crea cliente en /clientes
|
||||
↓
|
||||
Tenant creado con:
|
||||
dbMode = 'MANAGED'
|
||||
verticalProfile = (seleccionado por admin, default CONTABLE)
|
||||
trialEndsAt = +30 días (si plan = trial)
|
||||
codigoPostal = undefined (se llenará después)
|
||||
↓
|
||||
Dueño del despacho entra a la plataforma
|
||||
↓
|
||||
Sube FIEL del despacho (primer contribuyente)
|
||||
↓
|
||||
Sistema extrae CSF automáticamente
|
||||
↓
|
||||
Tenant actualizado con codigoPostal, calle, estado, regímenes...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Archivos modificados
|
||||
|
||||
| Archivo | Cambio |
|
||||
|---------|--------|
|
||||
| `apps/api/src/services/tenants.service.ts` | `createTenant` setea `dbMode`, `verticalProfile`, `trialEndsAt`, `codigoPostal`; queries retornan nuevos campos |
|
||||
| `apps/api/src/controllers/tenants.controller.ts` | Acepta `verticalProfile` y `codigoPostal` del body |
|
||||
| `apps/web/app/(dashboard)/clientes/page.tsx` | Formulario con selector de `verticalProfile`; C.P. omitido (viene de CSF) |
|
||||
| `apps/web/lib/api/tenants.ts` | Tipos `Tenant` y `CreateTenantData` ampliados |
|
||||
|
||||
---
|
||||
|
||||
## 5. Notas
|
||||
|
||||
- El campo `codigoPostal` del backend es **opcional** y se dejó en la firma por si en el futuro se quiere pasar manualmente (ej. onboarding asistido).
|
||||
- No se requirió migración de base de datos — `codigoPostal` ya existía como columna `String?` en `Tenant`.
|
||||
- `trialEndsAt` se calcula como `Date.now() + 30 días` para plan `trial`, igual que en `signupDespacho()`.
|
||||
Reference in New Issue
Block a user