# Modulo de Conciliacion — Spec

**Fecha:** 2026-04-12
**Estado:** Aprobado

---

## Objetivo

Permitir al usuario conciliar CFDIs emitidos y recibidos mes a mes, registrando fecha de pago y banco. Solo se permite conciliar del ano actual en adelante.

---

## Modelo de datos (BD tenant — raw SQL)

### Tabla `bancos` (nueva)

```sql
CREATE TABLE IF NOT EXISTS bancos (
  id SERIAL PRIMARY KEY,
  banco VARCHAR(100) NOT NULL,
  terminacion_cuenta VARCHAR(4) NOT NULL,
  creado_en TIMESTAMP DEFAULT NOW()
);
```

### Tabla `conciliaciones` (nueva)

```sql
CREATE TABLE IF NOT EXISTS conciliaciones (
  id SERIAL PRIMARY KEY,
  anio VARCHAR(4) NOT NULL,
  mes VARCHAR(2) NOT NULL,
  id_cfdi INTEGER NOT NULL UNIQUE REFERENCES cfdis(id),
  fecha_de_pago DATE NOT NULL,
  id_banco INTEGER NOT NULL REFERENCES bancos(id),
  creado_en TIMESTAMP DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_conciliaciones_anio_mes ON conciliaciones(anio, mes);
CREATE INDEX IF NOT EXISTS idx_conciliaciones_id_cfdi ON conciliaciones(id_cfdi);
```

### Columnas en `cfdis`

- `conciliado VARCHAR(50)` — ya existe. Se actualiza a `'true'` al conciliar, `NULL` al desconciliar.
- `id_conciliacion INTEGER REFERENCES conciliaciones(id)` — nueva. FK a la conciliacion asociada. NULL si no conciliado.

Al conciliar: se crean registros en `conciliaciones`, se actualiza `cfdis.conciliado = 'true'` y `cfdis.id_conciliacion = conciliaciones.id`.
Al desconciliar: se pone `cfdis.conciliado = NULL`, `cfdis.id_conciliacion = NULL`, y se elimina el registro de `conciliaciones`.

### DDL para tenants nuevos

Agregar `bancos`, `conciliaciones` en `database.ts` -> `createTables()` despues de `alertas`.
Agregar `id_conciliacion INTEGER REFERENCES conciliaciones(id)` en la tabla `cfdis`.

### Migracion para tenants existentes

`CREATE TABLE IF NOT EXISTS` para `bancos` y `conciliaciones`, luego `ALTER TABLE cfdis ADD COLUMN IF NOT EXISTS id_conciliacion INTEGER REFERENCES conciliaciones(id)`.

---

## Reglas de negocio

1. Solo se concilian CFDIs del **ano de alta del tenant en adelante** (se obtiene del `createdAt` del tenant en la BD central). Esto permite que una empresa registrada en 2025 pueda conciliar 2025, 2026, etc.
2. `anio` y `mes` de `conciliaciones` se derivan automaticamente de `fecha_de_pago`.
3. Un CFDI solo puede tener una conciliacion (`id_cfdi` es UNIQUE en conciliaciones, `id_conciliacion` en cfdis).
4. Solo CFDIs vigentes (`status NOT IN ('Cancelado', '0')`).
5. Al conciliar: INSERT en `conciliaciones` + UPDATE `cfdis` SET `conciliado = 'true'`, `id_conciliacion = <id>`.
6. Al desconciliar: UPDATE `cfdis` SET `conciliado = NULL`, `id_conciliacion = NULL` + DELETE de `conciliaciones`.
7. No se puede eliminar un banco que tenga conciliaciones asociadas.

---

## API endpoints

### Conciliacion

| Metodo | Ruta | Descripcion | Auth |
|--------|------|-------------|------|
| GET | `/conciliacion` | Lista CFDIs con estado de conciliacion | JWT + Tenant |
| POST | `/conciliacion` | Conciliar CFDIs (batch) | JWT + Tenant + admin/contador |
| DELETE | `/conciliacion/:id` | Desconciliar un CFDI | JWT + Tenant + admin/contador |

#### `GET /conciliacion`

**Query params:**
- `tipo`: `EMITIDO` | `RECIBIDO` (requerido)
- `fechaInicio`, `fechaFin`: rango de fecha de emision
- `regimen`: clave de regimen fiscal (opcional)
- `estado`: `conciliado` | `pendiente` (opcional, default: todos)

**Response:** Array de CFDIs con campo adicional `conciliacion` (null si pendiente, objeto si conciliado):
```json
{
  "id": 1,
  "uuid": "...",
  "rfcEmisor": "...",
  "nombreEmisor": "...",
  "total": 1000,
  "totalMxn": 1000,
  "fechaEmision": "...",
  "conciliado": "true",
  "idConciliacion": 5,
  "conciliacion": {
    "id": 5,
    "fechaDePago": "2026-04-10",
    "banco": "BBVA",
    "terminacionCuenta": "1234"
  }
}
```

#### `POST /conciliacion`

**Body:**
```json
{
  "cfdiIds": [1, 2, 3],
  "fechaDePago": "2026-04-10",
  "idBanco": 1
}
```

**Logica:**
1. Validar que todos los CFDIs existen, estan vigentes, y no estan ya conciliados.
2. Validar que `fechaDePago` es del ano actual en adelante.
3. Derivar `anio` y `mes` de `fechaDePago`.
4. Para cada CFDI: INSERT en `conciliaciones`, UPDATE `cfdis` SET `conciliado = 'true'`, `id_conciliacion = <new id>`.

#### `DELETE /conciliacion/:id`

1. Buscar la conciliacion por id.
2. UPDATE `cfdis` SET `conciliado = NULL`, `id_conciliacion = NULL` WHERE `id_conciliacion = :id`.
3. DELETE FROM `conciliaciones` WHERE `id = :id`.

### Bancos

| Metodo | Ruta | Descripcion | Auth |
|--------|------|-------------|------|
| GET | `/bancos` | Listar bancos del tenant | JWT + Tenant |
| POST | `/bancos` | Crear banco | JWT + Tenant + admin |
| PUT | `/bancos/:id` | Editar banco | JWT + Tenant + admin |
| DELETE | `/bancos/:id` | Eliminar banco (si no tiene conciliaciones) | JWT + Tenant + admin |

---

## Frontend

### Pagina `/conciliacion`

**Acceso:** Feature-gated por `conciliacion` (Business, Enterprise). Roles: admin y contador (lectura+escritura), visor (solo lectura).

**Layout:**
```
[Header: "Conciliacion"]
[Filtros: PeriodSelector | RegimenSelector]
[Tabs: Emitidas | Recibidas]
  [Seccion: "Por conciliar" — tabla con checkboxes]
  [Barra de accion: Banco (dropdown) + Fecha de pago (date) + Boton "Conciliar"]
  [Seccion: "Conciliadas" — tabla con info de conciliacion + boton desconciliar]
```

**Tabla "Por conciliar":**
- Checkbox (no visible para visor)
- UUID (corto), Fecha emision, RFC Emisor/Receptor, Nombre, Total MXN, Metodo Pago
- Boton "Ver factura" (CfdiViewerModal)

**Tabla "Conciliadas":**
- UUID, Fecha emision, RFC, Nombre, Total MXN
- Fecha de pago, Banco (nombre + terminacion)
- Boton "Desconciliar" (no visible para visor)
- Boton "Ver factura"

**Flujo de conciliacion:**
1. Usuario selecciona checkboxes en "Por conciliar"
2. Aparece barra de accion sticky en la parte inferior
3. Selecciona banco (dropdown de bancos del tenant) y fecha de pago
4. Click "Conciliar N facturas"
5. Confirmacion -> POST `/conciliacion` -> refresh datos

### Seccion de bancos en `/configuracion`

Solo visible para admin. Card con:
- Lista de bancos existentes: Nombre + terminacion + boton eliminar
- Formulario inline: Nombre banco + Terminacion (max 4 digitos) + boton agregar

### Navegacion

Agregar "Conciliacion" al sidebar con feature gate `conciliacion`, visible para admin, contador, visor. Ubicacion: despues de Reportes.

---

## Archivos a crear/modificar

### Backend (crear)
- `apps/api/src/services/conciliacion.service.ts`
- `apps/api/src/controllers/conciliacion.controller.ts`
- `apps/api/src/routes/conciliacion.routes.ts`
- `apps/api/src/services/bancos.service.ts`
- `apps/api/src/controllers/bancos.controller.ts`
- `apps/api/src/routes/bancos.routes.ts`

### Backend (modificar)
- `apps/api/src/app.ts` — registrar rutas de conciliacion y bancos
- `apps/api/src/config/database.ts` — agregar tablas `bancos` y `conciliaciones` en `createTables()`, agregar `id_conciliacion` en `cfdis`

### Frontend (crear)
- `apps/web/app/(dashboard)/conciliacion/page.tsx`
- `apps/web/lib/api/conciliacion.ts`
- `apps/web/lib/api/bancos.ts`
- `apps/web/lib/hooks/use-conciliacion.ts`
- `apps/web/lib/hooks/use-bancos.ts`

### Frontend (modificar)
- `apps/web/components/layouts/sidebar.tsx` (y variantes) — agregar nav item
- `apps/web/app/(dashboard)/configuracion/page.tsx` — agregar seccion de bancos

### Migracion
- Aplicar DDL a tenant existente (`horux_ede123456ab1`): crear tablas + agregar columna
