# Arquitectura del Backend — Automia (smartfactvoice-backend)

> Documentación técnica detallada del backend NestJS. Para editores AI.

---

## Índice

1. [Stack y dependencias](#1-stack-y-dependencias)
2. [Bootstrap y configuración](#2-bootstrap-y-configuración)
3. [Estructura de módulos](#3-estructura-de-módulos)
4. [Base de datos (Prisma + PostgreSQL)](#4-base-de-datos-prisma--postgresql)
5. [Autenticación (Auth Module)](#5-autenticación-auth-module)
6. [Sistema de permisos y módulos](#6-sistema-de-permisos-y-módulos)
7. [Facturación](#7-facturación)
8. [Sincronización Offline (Sync Module)](#8-sincronización-offline-sync-module)
9. [Configuración POS](#9-configuración-pos)
10. [Colas de trabajo (BullMQ + Redis)](#10-colas-de-trabajo-bullmq--redis)
11. [Integración SIFEN](#11-integración-sifen)
12. [Suscripciones y planes SaaS](#12-suscripciones-y-planes-saas)
13. [Vendedores, cobradores y comisiones](#13-vendedores-cobradores-y-comisiones)
14. [Listas de precios y planes de cuotas](#14-listas-de-precios-y-planes-de-cuotas)
15. [Ofertas y promociones](#15-ofertas-y-promociones)
16. [Tesorería y cajas](#16-tesorería-y-cajas)
17. [Servicios auxiliares](#17-servicios-auxiliares)
18. [Patrones y convenciones](#18-patrones-y-convenciones)

---

## 1. Stack y dependencias

| Tecnología | Versión | Uso |
|---|---|---|
| NestJS | 11.0 | Framework backend |
| Prisma | 6.18 | ORM → PostgreSQL |
| PostgreSQL | — | Base de datos principal |
| Passport + JWT | 11.0 | Autenticación |
| BullMQ | 5.63 | Colas de trabajo |
| Redis / ioredis | 5.8 | Cache + colas |
| Swagger | 11.2 | Documentación API |
| bcrypt | 6.0 | Hashing de passwords |
| AWS S3 | 3.952 | Almacenamiento de archivos |
| Twilio | 5.10 | SMS / verificación |
| Nodemailer | 7.0 | Emails transaccionales |
| sharp | 0.34 | Procesamiento de imágenes |
| class-validator | 0.14 | Validación de DTOs |
| class-transformer | 0.5 | Transformación de datos |

### Archivo: `package.json`

Scripts:
```bash
npm run start:dev     # Dev con hot-reload (--watch)
npm run start:debug   # Dev con debug
npm run build         # Compilar TypeScript
npm run start:prod    # Producción (node dist/main)
npm run lint          # ESLint
npm run test          # Jest
npx prisma generate   # Generar Prisma Client
npx prisma migrate dev # Ejecutar migraciones
```

---

## 2. Bootstrap y configuración

### `src/main.ts`

```typescript
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  
  // CORS abierto (todos los orígenes en dev)
  app.enableCors({ origin: true, credentials: true });
  
  // Bull Board UI en /queues
  // → Dashboard visual para ver estado de colas BullMQ
  
  // Prefijo global: /api
  app.setGlobalPrefix('api');
  
  // Versionado URI: /api/v1/
  app.enableVersioning({ type: VersioningType.URI, defaultVersion: '1' });
  
  // Interceptor: BigInt serializer (convierte BigInt a string en JSON)
  app.useGlobalInterceptors(new BigIntSerializerInterceptor());
  
  // Validación global de DTOs
  app.useGlobalPipes(new ValidationPipe({
    whitelist: true,          // Solo propiedades decoradas pasan
    forbidNonWhitelisted: true, // Error si envían props desconocidas
    transform: true,          // Auto-transform a tipos del DTO
  }));
  
  // Swagger en /docs
  SwaggerModule.setup('docs', app, document);
  
  await app.listen(envs.port);
}
```

### URLs de servicio

| URL | Descripción |
|---|---|
| `http://localhost:3000/api/v1/` | API REST |
| `http://localhost:3000/docs` | Swagger UI |
| `http://localhost:3000/queues` | Bull Board (dashboard colas) |

### `src/config/`

Variables de entorno cargadas con `@nestjs/config` (ConfigModule global).

### `src/app.module.ts`

Root module que importa **todos** los módulos feature. Orden notable:
1. `ConfigModule.forRoot({ isGlobal: true })` — env vars
2. `PrismaModule` — base de datos
3. `RedisModule` — cache
4. `QueuesModule` — BullMQ
5. `MailModule` — emails
6. Feature modules...

---

## 3. Estructura de módulos

```
src/
├── main.ts                         # Bootstrap
├── app.module.ts                   # Root module
├── app-token.guard.ts              # Guard para x-app-key
│
├── common/                         # Shared utilities
│   └── interceptors/
│       └── bigint-serializer.interceptor.ts
│
├── config/                         # Config de entorno
├── prisma/                         # Prisma module + service
│   ├── prisma.module.ts
│   └── prisma.service.ts
├── redis/                          # Redis module
├── queues/                         # BullMQ colas
│
│── ──── AUTENTICACIÓN ────
├── auth/                           # Login, register, JWT, refresh, me
├── codigos-verificacion/           # Códigos SMS/email para registro
│
│── ──── CORE EMPRESA ────
├── empresas/                       # CRUD empresas (multi-tenant root)
├── sucursales/                     # Sucursales por empresa
├── users/                          # CRUD usuarios
├── personas/                       # Datos de persona
├── perfiles/                       # Perfiles/roles
├── modulos/                        # Módulos del sistema
├── privilegios/                    # Privilegios CRUD por módulo
├── asignaciones-sucursal/          # Usuario ↔ Sucursal
│
│── ──── CATÁLOGOS ────
├── productos/                      # CRUD productos + búsqueda
├── categorias/                     # Categorías de productos
├── marcas/                         # Marcas de productos
├── monedas/                        # Monedas (PYG, USD, etc.)
├── medio-pago/                     # Medios de pago (efectivo, tarjeta, etc.)
├── condiciones-pago/               # Condiciones (contado, crédito 30d, etc.)
├── referenciales/                  # Datos SIFEN (IVA, tipos, etc.)
│
│── ──── VENTAS Y FACTURACIÓN ────
├── facturas/                       # Crear factura, listar, enviar SIFEN
├── nota-creditos/                  # Notas de crédito
├── clientes/                       # CRUD clientes
├── numeraciones/                   # Numeraciones de documentos
├── punto-expediciones/             # Puntos de expedición
│
│── ──── INVENTARIO ────
├── stock/                          # Control de stock por almacén
├── depositos/                      # Almacenes/depósitos
├── movimientos-inventario/         # Entradas/salidas de stock
│
│── ──── POS ────
├── pos-config/                     # Config POS por sucursal
├── sync/                           # Sincronización offline POS
├── cajas/                          # Gestión de cajas registradoras
├── tesoreria/                      # Abrir/cerrar caja, movimientos
│
│── ──── COBROS Y FINANZAS ────
├── cobros/                         # Gestión de cobros
│
│── ──── COMERCIAL ────
├── lista-precios/                  # Listas de precios múltiples
├── planes-cuotas/                  # Financiamiento en cuotas
├── ofertas/                        # Ofertas y promociones
├── vendedores-cobradores/          # Vendedores, cobradores, comisiones
│
│── ──── SAAS / SUSCRIPCIONES ────
├── suscripciones/                  # Gestión de suscripciones
├── planes/                         # Planes de suscripción
│
│── ──── INTEGRACIONES ────
├── middleware-sifen/               # Integración SIFEN (factura electrónica PY)
├── mail/                           # Nodemailer (emails)
├── twilio/                         # SMS (Twilio)
├── notificaciones/                 # Push notifications
│
└── utils/                          # Utilidades compartidas
```

### Patrón de módulo NestJS

Cada módulo feature sigue la estructura:
```
src/feature/
├── feature.module.ts        # NestJS Module (imports, providers, exports)
├── feature.controller.ts    # REST endpoints
├── feature.service.ts       # Lógica de negocio
└── dto/
    ├── create-feature.dto.ts
    ├── update-feature.dto.ts
    └── doc-swagger.ts        # Definiciones Swagger
```

---

## 4. Base de datos (Prisma + PostgreSQL)

### Prisma Module (`src/prisma/`)

```typescript
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  async onModuleInit() {
    await this.$connect();
  }
}
```

Inyectable en cualquier servicio:
```typescript
constructor(private readonly prisma: PrismaService) {}
```

### Schema

El schema de Prisma está en `prisma/schema.prisma`. Tablas principales:

**Core:**
- `empresas` — tenant principal (con `parent_id` para jerarquía holding/reseller/subsidiario)
- `usuario` — usuarios del sistema
- `personas` — datos de persona (empresa_id)
- `empresas_sucursales` — sucursales por empresa
- `perfiles` — roles/perfiles
- `modulos` — módulos del sistema (códigos)
- `privilegios` — privilegios CRUD
- `perfil_modulo_privilegio` — permisos: perfil × módulo × privilegio

**Productos:**
- `productos` — catálogo (empresa_id, soft delete con `deleted`)
- `categorias` — categorías jerárquicas (padre_id)
- `marcas`
- `unidades_de_medida`
- `stock_deposito` — stock por producto × depósito

**Ventas:**
- `factura_cab` — cabecera de factura (empresa_id)
- `factura_det` — detalle (items)
- `factura_pagos` — pagos asociados
- `factura_cuotas` — cuotas de financiamiento
- `clientes` — clientes (con persona)
- `numeraciones_documento` — numeraciones con timbrado
- `timbrado` — timbrados vigentes (SIFEN)

**POS:**
- `pos_config` — config POS por sucursal (unique: sucursal_id)
- `cajas` — cajas registradoras
- `sesion_caja` — sesiones (apertura/cierre)
- `movimientos_caja` — movimientos de efectivo

**Referenciales (SIFEN):**
- `afectacion_iva`, `tipo_operacion`, `tipo_impuesto`, `tipo_transaccion`
- `condicion_operacion`, `indicador_presencia`, `naturaleza_receptor`
- `tipo_documento_identidad`, `tipo_contribuyente`
- `forma_procesamiento_pago`
- `moneda` — monedas con código ISO

**Comercial:**
- `listas_precios`, `lista_precio_items`
- `planes_cuotas`, `producto_precios_cuotas`
- `ofertas`, `oferta_condiciones`, `oferta_aplicacion`
- `vendedores_cobradores`, `comisiones`

**SaaS:**
- `suscripciones` — suscripción de cada empresa
- `planes` — planes disponibles
- `comisiones_nuevas` — comisiones de resellers
- `precios_reseller` — precios especiales para resellers

### Multi-tenancy

Todas las queries principales filtran por `empresa_id`:
```typescript
async findAll(empresaId: string) {
  return this.prisma.productos.findMany({
    where: { empresa_id: empresaId, deleted: false },
  });
}
```

El `empresa_id` se extrae del JWT en el controller:
```typescript
@GetUser() user: { id: string; empresa_id: string }
// → user.empresa_id
```

---

## 5. Autenticación (Auth Module)

### Ubicación: `src/auth/`

```
src/auth/
├── auth.module.ts
├── auth.controller.ts
├── auth.service.ts
├── jwt.strategy.ts          # Passport JWT strategy
├── get-user.decorator.ts    # @GetUser() decorator
└── dto/
    ├── login.dto.ts
    ├── registro.dto.ts
    ├── switch-empresa.dto.ts
    └── doc-swagger.ts
```

### Endpoints

| Método | Ruta | Guards | Descripción |
|---|---|---|---|
| `POST` | `/auth/register` | AppTokenGuard | Registro empresa + usuario |
| `POST` | `/auth/login` | AppTokenGuard | Login → tokens |
| `POST` | `/auth/refresh` | AppTokenGuard | Renovar access_token |
| `POST` | `/auth/logout` | AppTokenGuard | Invalidar refresh_token |
| `GET` | `/auth/me` | JWT | Datos completos del usuario |
| `POST` | `/auth/switch-empresa` | JWT | Cambiar empresa activa |
| `GET` | `/auth/empresas-accesibles` | JWT | Empresas a las que tiene acceso |

### Flujo de login

```
POST /auth/login { usuario, password }
  → Buscar usuario por RUC/username
  → bcrypt.compare(password, hash)
  → Generar access_token (JWT, corta vida)
  → Generar refresh_token (JWT, larga vida, guardado en DB)
  → Responder: { access_token, refresh_token, user }
```

### JWT Strategy (`jwt.strategy.ts`)

```typescript
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: process.env.JWT_SECRET,
    });
  }
  
  validate(payload) {
    return { id: payload.id, empresa_id: payload.empresa_id };
  }
}
```

### AppTokenGuard (`src/app-token.guard.ts`)

Verifica el header `x-app-key` contra `APP_KEY` del .env. Se usa en endpoints públicos (login, register, refresh) como capa de seguridad adicional.

### `GET /auth/me`

Devuelve el **contexto completo** del usuario:
```json
{
  "user": { "id", "nombre", "email", "is_superadmin", ... },
  "empresa": { "id", "razon_social", "ruc", "type", ... },
  "suscripcion": { "plan", "estado", "fecha_vencimiento", ... },
  "modulos": ["DASHBOARD", "POS_ADMIN", "FACTURACION", ...],
  "modulos_detalle": [{ "codigo", "descripcion", "tipo" }],
  "permisos": { "PRODUCTOS": ["LEER", "CREAR", "EDITAR", "ELIMINAR"], ... }
}
```

Este endpoint es llamado por el frontend en cada login y al recargar la app para hidratar `AuthStore`.

---

## 6. Sistema de permisos y módulos

### Tablas

```
modulos
  id, codigo (unique), descripcion, tipo

privilegios
  id, codigo (LEER, CREAR, EDITAR, ELIMINAR), descripcion

perfiles
  id, empresa_id, nombre, descripcion

perfil_modulo_privilegio
  perfil_id, modulo_id, privilegio_id
  → Define qué privilegios tiene cada perfil sobre cada módulo

usuario_perfil (o campo perfil_id en usuario)
  → Asigna un perfil a un usuario
```

### Verificación en el backend

Los endpoints se protegen con `@UseGuards(AuthGuard('jwt'))`. La verificación de permisos granulares (CREAR, EDITAR, etc.) se puede hacer en el service o con guards custom.

### Módulos del sistema

Códigos de módulos usados en ProtectedRoute y sidebar:

```
DASHBOARD, POS_ADMIN, FACTURACION, NOTAS_CREDITO, COBROS, CUENTAS_COBRAR,
PRODUCTOS, CATEGORIAS, CONTACTOS, CLIENTES, TESORERIA, SUSCRIPCIONES,
REPORTES, CONFIG_EMPRESA, CONFIG_NUMERACION, CONFIG_TICKET,
CONFIG_SUCURSALES, CONFIG_USUARIOS, CONFIG_ALMACENES, CONFIG_IMPRESORAS,
CONFIG_MEDIOS_PAGO
```

---

## 7. Facturación

### Ubicación: `src/facturas/`

```
src/facturas/
├── facturas.module.ts
├── facturas.controller.ts
├── facturas.service.ts
└── dto/
    ├── create-factura.dto.ts
    ├── create-factura-auto.dto.ts
    └── doc-swagger.ts
```

### Endpoints

| Método | Ruta | Descripción |
|---|---|---|
| `POST` | `/facturas` | Crear factura (POS Admin / offline sync) |
| `POST` | `/facturas/automatico` | Crear factura automática (suscripciones) |
| `GET` | `/facturas` | Listar facturas (paginado, filtros) |
| `GET` | `/facturas/:id` | Detalle de factura |
| `GET` | `/facturas/siguiente-numeracion/:est/:exp` | Próximo número |
| `GET` | `/facturas/pendientes-envio/lista` | Pendientes de envío SIFEN |
| `POST` | `/facturas/enviar-lote-middleware` | Enviar lote a SIFEN |

### Flujo de creación de factura (`create`)

```
1. Validar datos del DTO (cabecera, items, pagos)
2. Obtener numeración atómica (siguiente número de factura)
3. Calcular subtotales, IVA, total
4. Crear factura_cab + factura_det[] + factura_pagos[]
5. Actualizar stock (descontar cantidades)
6. Si hay cuotas → crear factura_cuotas[]
7. Encolar en sifen-queue para envío a SIFEN
8. Retornar factura creada
```

### Filtros en `findAll`

```
fecha_desde, fecha_hasta, estado, cliente_id, search, sucursal_id, estado_sifen
```

Estados de factura: `pendiente`, `pagada`, `anulada`, `vencida`
Estados SIFEN: `Aprobado`, `Rechazado`, `Pendiente`

---

## 8. Sincronización Offline (Sync Module)

### Ubicación: `src/sync/`

```
src/sync/
├── sync.module.ts
├── sync.controller.ts
└── sync.service.ts
```

### Endpoints

| Método | Ruta | Auth | Descripción |
|---|---|---|---|
| `GET` | `/sync/ping` | No | Health check → `{ ok: true, ts: ... }` |
| `GET` | `/sync/full` | JWT | Todo: productos, clientes, categorías, medios pago, referenciales, numeraciones, condiciones pago |
| `GET` | `/sync/delta?since=` | JWT | Solo cambios desde `since` |
| `GET` | `/sync/productos?since=` | JWT | Solo productos |
| `GET` | `/sync/clientes?since=` | JWT | Solo clientes |
| `GET` | `/sync/categorias?since=` | JWT | Solo categorías |
| `GET` | `/sync/medios-pago` | JWT | Medios de pago activos |
| `GET` | `/sync/referenciales` | JWT | Datos referenciales SIFEN |
| `GET` | `/sync/numeraciones` | JWT | Numeraciones activas |
| `GET` | `/sync/condiciones-pago` | JWT | Condiciones de pago |
| `POST` | `/sync/push/ventas` | JWT | Recibir ventas offline |

### Sync Service — Métodos principales

**`getFullSync(empresaId)`:**
- Ejecuta todas las queries en paralelo con `Promise.all`
- Devuelve todos los catálogos + `synced_at`

**`getDeltaSync(empresaId, since)`:**
- Productos, clientes, categorías → filtrados por `updated_at >= since`
- Medios pago, numeraciones, condiciones pago → siempre completos (cambian poco)
- Devuelve `is_delta: true` + `since` + `synced_at`

**`getProductosDelta(empresaId, since?)`:**
- Filtro: `empresa_id`, `deleted: false`, `updated_at >= since`
- Includes: unidad medida, afectación IVA, categoría, marca, stock_deposito

**`getClientesDelta(empresaId, since?)`:**
- Busca clientes vinculados a la empresa por facturas O por persona
- Includes: persona (con RUC, documento, naturaleza receptor), tipo operación, lista precios

**`pushVentasOffline(empresaId, ventas[])`:**
- Itera cada venta
- Llama a `facturasService.create()` con los mismos datos que una venta online
- Retorna resultado por cada venta: `success` o `error` con `_offline_id`
- Las ventas offline pasan por **exactamente la misma lógica** que las online

---

## 9. Configuración POS

### Ubicación: `src/pos-config/`

```
src/pos-config/
├── pos-config.module.ts
├── pos-config.controller.ts
├── pos-config.service.ts
└── dto/
    ├── create-pos-config.dto.ts
    └── update-pos-config.dto.ts
```

### Endpoints

| Método | Ruta | Descripción |
|---|---|---|
| `GET` | `/pos-config/:sucursal_id` | Config POS de sucursal |
| `POST` | `/pos-config` | Crear config |
| `PATCH` | `/pos-config/:sucursal_id` | Actualizar (upsert) |
| `DELETE` | `/pos-config/:id` | Eliminar config |

### Config por defecto

Si no existe config para la sucursal, retorna defaults:
```typescript
{
  tipo_pos: 'admin',           // 'admin' | 'retail'
  fullscreen: true,
  layout: 'grid',
  show_product_image: true,
  show_product_price: true,
  show_product_stock: true,
  card_size: 'medium',
  categories_position: 'left',
  numpad_position: 'right',
  enable_tables: false,
  enable_delivery: false,
  enable_quick_sale: true,
  default_view: 'products',
  permitir_venta_sin_stock: true,
  retail_tile_size: 'medium',
  retail_grid_columns: 0,       // 0 = auto
}
```

### Unique constraint

`pos_config.sucursal_id` es unique → una sola config por sucursal.

---

## 10. Colas de trabajo (BullMQ + Redis)

### Ubicación: `src/queues/`

```
src/queues/
├── queues.module.ts
├── queues.service.ts
└── processors/
    ├── email.processor.ts
    ├── sifen.processor.ts
    ├── sifen-nc.processor.ts
    └── sifen-sync.processor.ts
```

### Colas registradas

| Cola | Descripción |
|---|---|
| `email-queue` | Emails transaccionales (registro, recuperación, notificaciones) |
| `sifen-queue` | Envío de facturas electrónicas a SIFEN |
| `sifen-nc-queue` | Envío de notas de crédito a SIFEN |
| `sifen-sync-queue` | Sincronización de estados SIFEN (job repetible cada 5min) |

### Procesadores

- **EmailProcessor**: consume `email-queue`, envía emails via Nodemailer
- **SifenProcessor**: consume `sifen-queue`, envía XML de factura al middleware SIFEN
- **SifenNCProcessor**: consume `sifen-nc-queue`, envía XML de nota de crédito
- **SifenSyncProcessor**: job periódico cada 5min, consulta estado de DTE pendientes en SIFEN

### QueuesService

Servicio inyectable para encolar jobs desde cualquier módulo:
```typescript
@Injectable()
export class QueuesService {
  enqueueEmail(data: EmailJobData) { ... }
  enqueueSifen(facturaId: string) { ... }
  enqueueSifenNC(notaCreditoId: string) { ... }
}
```

### Bull Board

Dashboard visual en `/queues` para monitorear estado de colas, jobs fallidos, reintentar, etc.

---

## 11. Integración SIFEN

### Ubicación: `src/middleware-sifen/`

SIFEN = Sistema Integrado de Facturación Electrónica Nacional (Paraguay).

El backend actúa como intermediario:
```
Backend → XML → Middleware SIFEN → SET (Servicio de Facturación Electrónica)
```

Flujo:
1. Factura creada → se encola en `sifen-queue`
2. SifenProcessor genera XML según formato SIFEN
3. Envía al middleware SIFEN configurado
4. Guarda estado: `Pendiente`, `Aprobado`, `Rechazado`
5. `sifen-sync-queue` (cada 5min) sincroniza estados de DTEs pendientes

---

## 12. Suscripciones y planes SaaS

### Ubicación: `src/suscripciones/`, `src/planes/`

### Jerarquía empresarial

```
Holding (parent)
  ├── Reseller 1 (parent_id = holding.id)
  │   ├── Subsidiario A (parent_id = reseller1.id)
  │   └── Subsidiario B
  └── Reseller 2
      └── Subsidiario C
```

Tabla `empresas` tiene:
- `parent_id` — empresa padre
- `type` — `'holding'` | `'reseller'` | `'subsidiario'`
- `comision_porcentaje` — comisión para resellers

### Servicios

```
src/empresas/
├── empresas.service.ts
├── jerarquia-empresas.service.ts   # Jerarquía, métricas, validaciones
└── comisiones.service.ts           # Cálculo y gestión de comisiones
```

- **JerarquiaEmpresasService**: `getJerarquiaCompleta`, `getHijos`, `validarLimitesReseller`, `getMetricasReseller`, `getMetricasHolding`
- **ComisionesService**: `calcularComisionesSuscripcion`, `generarComisiones`, `generarComisionesMensuales`, `getResumenComisionesReseller`, `marcarComisionesPagadas`

### Nota

La funcionalidad multi-tenant avanzada de planes (resellers creando sus propios planes) está **pospuesta para versión futura**. Actualmente solo el holding puede administrar planes.

---

## 13. Vendedores, cobradores y comisiones

### Ubicación: `src/vendedores-cobradores/` (15 archivos)

Sistema para gestionar vendedores, cobradores, asignaciones de facturas, rutas de cobranza y liquidación de comisiones.

> Especificaciones detalladas en `docs/MODULOS_FUTUROS.md` sección 1.

---

## 14. Listas de precios y planes de cuotas

### Listas de precios: `src/lista-precios/`

Permite definir múltiples listas de precios (general, mayorista, empleados, convenio) y asignar precios específicos por producto.

### Planes de cuotas: `src/planes-cuotas/`

Sistema de financiamiento con cuotas. Define cantidad de cuotas, interés, recargo financiero.

> Documentación detallada en `docs/sistema-precios-cuotas.md`, `docs/plan-cuotas-flujo-pos-admin.md`, `docs/configuracion-y-listas-de-precios.md`

---

## 15. Ofertas y promociones

### Ubicación: `src/ofertas/`

Motor de ofertas flexible con tipos: descuento porcentaje, descuento monto, precio especial, NxM, combo, envío gratis. Soporta condiciones combinables y límites de uso.

> Documentación detallada en `docs/ofertas-promociones.md` y `docs/MODULOS_FUTUROS.md` sección 3.

---

## 16. Tesorería y cajas

### Ubicación: `src/tesoreria/`, `src/cajas/`

### Endpoints principales

| Método | Ruta | Descripción |
|---|---|---|
| `POST` | `/tesoreria/abrir-caja` | Abrir sesión de caja con monto inicial |
| `POST` | `/tesoreria/cerrar-caja/:id` | Cerrar sesión (cuadre) |
| `GET` | `/tesoreria/sesion-activa` | Sesión de caja activa del usuario |
| `GET` | `/tesoreria/cajas-disponibles` | Cajas disponibles en sucursal |
| `POST` | `/tesoreria/movimiento` | Entrada/salida de efectivo |
| `GET` | `/tesoreria/movimientos/:sesion_id` | Movimientos de una sesión |

### Flujo

```
Abrir Caja (monto base)
  → Registrar ventas (facturas)
  → Movimientos de efectivo (entradas/salidas)
  → Cerrar Caja (cuadre: sistema vs contado)
```

---

## 17. Servicios auxiliares

### Mail (`src/mail/`)

Usa `@nestjs-modules/mailer` + Nodemailer. Encolado via BullMQ para no bloquear requests.

### Twilio (`src/twilio/`)

SMS para verificación de números de celular en el registro.

### Redis (`src/redis/`)

Usado para:
- Cache (cache-manager-redis-yet)
- Colas BullMQ
- Almacenamiento de refresh tokens

### Notificaciones (`src/notificaciones/`)

Push notifications (estructura base).

---

## 18. Patrones y convenciones

### DTOs

Usan `class-validator` y `class-transformer`:
```typescript
export class CreateProductoDto {
  @IsString()
  @IsNotEmpty()
  descripcion: string;
  
  @IsNumber()
  @IsOptional()
  precio?: number;
}
```

### Respuestas API

Patrón consistente:
```json
{
  "message": "Productos obtenidos exitosamente",
  "data": [...],
  "total": 100,
  "page": 1,
  "limit": 10,
  "lastPage": 10
}
```

### Error handling

```typescript
throw new HttpException({
  success: false,
  message: 'Error descriptivo',
  error: errorMessage,
}, HttpStatus.BAD_REQUEST);
```

Excepciones NestJS usadas: `NotFoundException`, `ConflictException`, `BadRequestException`.

### Guards

- **`AuthGuard('jwt')`** — verifica JWT, inyecta user en request
- **`AppTokenGuard`** — verifica header `x-app-key` (endpoints públicos)

### Decoradores

- **`@GetUser()`** — extrae user del request: `{ id, empresa_id }`
- **`@ApiBearerAuth()`** — documenta auth en Swagger
- **`@ApiSecurity('AppKey')`** — documenta app-key en Swagger

### Soft delete

La mayoría de entidades usan `deleted: false` en lugar de borrado físico:
```typescript
where: { id, deleted: false }
```

### Naming

- Controllers: kebab-case en rutas (`/vendedores-cobradores`)
- Services: camelCase en métodos (`getProductosDelta`)
- DTOs: PascalCase (`CreateFacturaDto`)
- Tablas Prisma: snake_case (`factura_cab`, `stock_deposito`)

---

*Documento generado: Febrero 2026*
