Refactor documentation for ERP architecture and customer invoices

- Updated general requirements document to clarify its historical context and remove outdated technical details.
- Revised template documentation to serve as a base for writing prompts, emphasizing its auxiliary nature.
- Enhanced architecture documentation with clear distinctions between canonical and complementary documents.
- Added a new document for backend prompt base to centralize common context for backend prompts.
- Created a dedicated README for customer invoices, organizing current contracts, operational support, and historical context.
- Improved clarity and structure in various customer invoice documents, consolidating operational semantics and referencing canonical sources.
This commit is contained in:
David Arranz 2026-08-05 13:18:03 +02:00
parent 9cade597d7
commit e5e73a5a13
20 changed files with 1055 additions and 1345 deletions

525
AGENTS.md
View File

@ -0,0 +1,525 @@
# AGENTS.md
## Ámbito
Estas instrucciones se aplican a todo el monorepo, salvo que exista un archivo `AGENTS.md` más específico en un subdirectorio.
Los archivos `AGENTS.md` descendientes deben añadir o restringir reglas para su ámbito, no duplicar innecesariamente este documento.
## Objetivo
Trabajar sobre el ERP mediante cambios:
- correctos;
- mínimos;
- comprobables;
- coherentes con la arquitectura existente;
- fáciles de entender y mantener por otros desarrolladores.
Antes de modificar código, revisar las implementaciones y patrones existentes en el área afectada. No introducir patrones nuevos si ya existe una solución equivalente en el proyecto.
## Stack principal
- Node.js >= 22
- TypeScript
- PNPM
- Turborepo
- ExpressJS
- React
- MariaDB
- Sequelize v6
- Zod v4
- Biome
## Principios obligatorios
Aplicar de forma pragmática:
- SOLID;
- Clean Code;
- DRY;
- KISS;
- YAGNI;
- SSOT;
- Fail Fast;
- programación defensiva;
- inmutabilidad cuando resulte adecuada;
- idempotencia;
- principio de menor privilegio;
- observabilidad en APIs y servicios.
Priorizar siempre:
```text
claridad y explicitud
>
abstracción prematura
```
No introducir capas, adaptadores, factories, genéricos, eventos, contenedores DI o abstracciones sin una necesidad real y actual.
## Idioma y convenciones de nombres
- Código e identificadores en inglés.
- Comentarios y TSDoc en castellano.
- Ficheros y directorios en `kebab-case`.
- Clases, tipos e interfaces en `PascalCase`.
- Variables, funciones y métodos en `camelCase`.
- Constantes globales en `UPPER_SNAKE_CASE` solo cuando realmente sean constantes globales.
- Mantener la convención de prefijo `I` para interfaces cuando el área existente ya la utilice.
## Comentarios y documentación
Los comentarios deben explicar:
- por qué existe una decisión;
- qué riesgo evita;
- qué contrato debe respetarse;
- qué efectos laterales o errores son relevantes.
No deben repetir lo que ya expresa el código.
Documentar mediante TSDoc las APIs públicas cuando aporten información sobre:
- propósito;
- parámetros;
- valor devuelto;
- errores;
- efectos laterales;
- invariantes o restricciones.
Después de un refactor, actualizar o eliminar los comentarios que hayan quedado obsoletos.
## Disciplina de cambios
- Realizar cambios incrementales.
- No modificar código no relacionado con la tarea.
- No renombrar, mover o reformatear archivos sin necesidad.
- No introducir dependencias nuevas sin una justificación concreta.
- No copiar código si ya existe una abstracción adecuada y estable.
- No crear una abstracción nueva para eliminar una duplicación pequeña y local.
- Mantener compatibilidad con los contratos existentes salvo que la tarea exija explícitamente romperlos.
- No afirmar que una validación ha pasado si no se ha ejecutado correctamente.
- No ocultar fallos de compilación, lint o tests.
- No añadir supresiones de TypeScript, Biome o ESLint sin justificar el motivo en el código o en la respuesta final.
## Uso de `Result`, `Maybe` y `Collection`
Usar los patrones existentes del proyecto:
- `Result` para operaciones que pueden fallar;
- `Maybe` para ausencia semántica;
- `Collection` para conjuntos de objetos del mismo tipo cuando el dominio requiera comportamiento propio.
No sustituirlos por excepciones, `null`, arrays o tipos ad hoc si el área existente ya utiliza estos patrones.
No usar excepciones para flujo de negocio esperado salvo que el diseño actual del módulo lo establezca expresamente.
## Gestión de operaciones asíncronas
El proyecto aplica la regla de Biome:
```text
lint/complexity/noVoid
```
Está prohibido usar el operador `void` para descartar el resultado de una promesa.
No generar:
```ts
void handleDuplicate();
void handleDownload();
void asyncOperation();
```
Toda `Promise` debe gestionarse explícitamente mediante una de estas opciones:
### Esperar la operación
```ts
await handleDuplicate();
```
Usar esta opción cuando la función actual pueda ser `async` y el flujo deba esperar el resultado.
### Devolver la promesa
```ts
return handleDuplicate();
```
Usar esta opción cuando el llamador deba controlar el ciclo de vida de la operación.
### Gestionar expresamente el error
```ts
handleDuplicate().catch(handleUnexpectedError);
```
Usar esta opción solo cuando la operación sea deliberadamente no bloqueante.
Reglas adicionales:
- No eliminar `void` dejando una promesa flotante.
- No silenciar errores mediante `.catch(() => undefined)` o equivalentes sin justificación explícita.
- Antes de convertir un callback en `async`, comprobar si su contrato acepta `Promise<void>` o exige `void`.
- Si el contrato exige `void`, encapsular la operación asíncrona y gestionar explícitamente su error.
- No usar `void` como mecanismo para indicar intención.
## TypeScript
### Imports de tipos
Usar siempre imports de tipos explícitos en la cabecera del fichero mediante `import type`:
```ts
import type { UniqueID } from "@repo/rdx-ddd";
import type { Result } from "@repo/rdx-utils";
```
No usar referencias de tipo inline mediante `import("...").Tipo`:
```ts
// Prohibido
companyId: import("@repo/rdx-ddd").UniqueID;
```
Aplicar estas reglas:
- Usar `import type { ... } from "..."` para toda dependencia utilizada exclusivamente como tipo.
- Mantener los imports de tipos en la cabecera del fichero, junto con el resto de imports.
- No mezclar imports de tipo y de runtime cuando puedan separarse con claridad.
- No usar `import("...").Tipo` en código fuente ordinario.
- Solo se admite un tipo inline en declaraciones generadas, archivos `.d.ts`, module augmentation o casos excepcionales técnicamente justificados y documentados.
- No introducir imports de runtime para símbolos usados únicamente como tipos.
Ejemplo correcto:
```ts
import type { UniqueID } from "@repo/rdx-ddd";
import type { Result } from "@repo/rdx-utils";
export interface IIdentityTenantCompanyFinder {
existsActiveById(params: {
companyId: UniqueID;
transaction?: unknown;
}): Promise<Result<boolean, Error>>;
}
```
- Evitar `any`. Usar `unknown` cuando el tipo todavía no sea conocido.
- No usar type assertions salvo que exista una garantía verificable que el compilador no pueda inferir.
- Evitar `!` salvo que la invariante esté demostrada localmente.
- Preferir tipos semánticos frente a primitivas genéricas.
- Modelar conjuntos cerrados mediante unions o enums existentes.
- Mantener exhaustividad en `switch` cuando el tipo represente un conjunto cerrado.
- No añadir ramas `default` que oculten casos no contemplados cuando pueda usarse comprobación exhaustiva.
- No duplicar tipos derivados de schemas Zod.
- No usar `Partial<T>` si el schema o el tipo de entrada ya representa explícitamente la opcionalidad.
- Mantener inmutabilidad en valores y estructuras cuando no exista necesidad de mutación.
## Zod y DTOs
Zod es la fuente única de verdad para los contratos HTTP.
Reglas:
- Inferir tipos con `z.infer<typeof Schema>`.
- No duplicar DTOs mediante interfaces manuales equivalentes.
- Cada endpoint debe tener sus DTOs de request y response.
- Reutilizar subschemas solo cuando sean realmente estables y compartidos.
- No mezclar validación de transporte con lógica de negocio.
- Evitar `.default()` y `.transform()` en DTOs de negocio salvo necesidad justificada.
- Diferenciar explícitamente entre campo omitido, `null`, cadena vacía y colección vacía.
- Usar schemas semánticos para UUID, fechas, timestamps, porcentajes, importes y cantidades.
- Los valores decimales exactos no deben viajar como `number`.
## Arquitectura backend
El backend sigue DDD pragmático, Clean Architecture y arquitectura hexagonal.
Separación obligatoria:
```text
Domain
no conoce Application ni Infrastructure
Application
depende de Domain, contratos y Core
no conoce Express, Sequelize ni modelos ORM
Infrastructure
depende de Application, Domain y Core
```
No se permite:
- `Domain -> Application`;
- `Domain -> Infrastructure`;
- `Application -> Infrastructure`;
- importar infraestructura interna de otro módulo;
- compartir repositorios entre módulos;
- usar Service Locator;
- introducir DI implícita o mágica.
### Flujo de entrada
Respetar:
```text
RequestDTO
-> RequestMapper
-> Command
-> PropsMapper
-> Aggregate o Entity
```
No mapear directamente:
```text
DTO -> Aggregate
```
### Casos de uso
Los casos de uso deben:
- orquestar;
- gestionar transacciones cuando corresponda;
- delegar en servicios de aplicación;
- devolver `Result`;
- construir respuestas mediante builders o mappers de salida.
No deben:
- contener lógica HTTP;
- usar Sequelize directamente;
- construir agregados desde DTOs HTTP;
- asumir responsabilidades propias de repositorios o controllers.
### Servicios de aplicación
Preferir servicios pequeños y explícitos con una responsabilidad clara.
Evitar nombres genéricos como:
- `Manager`;
- `GenericService`;
- `CommandService`;
- `BaseRepository`;
- `Helper` sin semántica concreta.
### Repositorios
- Cada repositorio gestiona un agregado o una responsabilidad concreta.
- Sequelize no debe salir de Infrastructure.
- Los repositorios deben usar mappers de persistencia.
- No crear repositorios base genéricos sin una necesidad demostrada.
- Mantener aislamiento por `companyId` y `branchId` cuando corresponda.
### Mappers de persistencia
Usar un mapper bidireccional por agregado:
```ts
toPersistence();
toDomain();
```
Infrastructure debe reconstruir entidades mediante `rehydrate()`.
### `create()` y `rehydrate()`
Mantener separadas estas operaciones:
```text
create()
creación funcional
valida invariantes
puede fallar
devuelve Result
rehydrate()
reconstrucción desde persistencia
no valida reglas de creación
no genera eventos
no devuelve Result
```
Reglas:
- Infrastructure usa `rehydrate()`.
- Application y Domain usan `create()` para nuevas instancias.
- `update()` no usa `create()` ni `rehydrate()`.
- Los Value Objects no tienen `rehydrate()`; se reconstruyen mediante `create()`.
### Comunicación entre módulos
Los módulos solo deben colaborar mediante servicios públicos explícitos.
No exponer:
- repositorios;
- modelos Sequelize;
- mappers internos;
- servicios de escritura internos;
- detalles de infraestructura.
El composition root de cada módulo debe construir sus dependencias explícitamente.
## Snapshots y builders de respuesta
Los schemas Zod definen el contrato de salida.
Los snapshot builders solo materializan ese contrato.
Reglas:
- Tipar el builder con el DTO inferido del schema exacto.
- No crear interfaces `I*Snapshot` que dupliquen un DTO.
- El builder debe ser determinista y no contener lógica de negocio.
- Puede convertir `Maybe` a `null`, Value Objects a primitivas y enums internos a valores de contrato.
- No puede acceder a repositorios ni infraestructura.
- Si cambia el schema, el builder debe fallar en compilación si deja de cumplir el contrato.
## Frontend
- Mantener React, TanStack Query, TanStack Table, React Hook Form, Zod, shadcn/ui, Tailwind e i18n según los patrones existentes.
- No duplicar estado remoto en estado local sin necesidad.
- Mantener las operaciones de servidor en TanStack Query.
- Centralizar las claves de query siguiendo la convención existente.
- Invalidar o actualizar caché de forma explícita tras mutaciones.
- No introducir efectos para derivar estado que pueda calcularse durante render.
- Evitar componentes monolíticos; extraer responsabilidades cuando exista una frontera clara.
- No extraer componentes triviales que solo oculten JSX sin aportar semántica.
- Mantener accesibilidad, estados disabled, loading, empty y error.
- No eliminar acciones no disponibles si la UX exige mostrarlas deshabilitadas.
- Mantener traducciones en `es` y `en` cuando se añadan textos visibles.
- No insertar textos de interfaz directamente si el área usa i18n.
- Mantener `tabular-nums` en columnas monetarias cuando corresponda.
## APIs y errores
- Usar errores tipados y predecibles.
- No devolver detalles internos, SQL, stack traces o información sensible.
- Mapear errores de dominio y aplicación a errores HTTP en Infrastructure.
- Mantener contratos de error estables.
- Aplicar idempotencia en operaciones que puedan repetirse.
- Validar autorización y aislamiento tenant antes de acceder o modificar datos.
- No confiar en identificadores de compañía enviados por el cliente sin verificar el contexto autenticado.
- Registrar información suficiente para diagnosticar fallos sin exponer datos sensibles.
## Seguridad
- Aplicar principio de menor privilegio.
- No introducir secretos, tokens, claves o credenciales en el repositorio.
- No registrar datos sensibles.
- Validar toda entrada externa.
- Usar consultas parametrizadas y APIs seguras de Sequelize.
- Evitar interpolación manual de SQL.
- Revisar autorización además de autenticación.
- Mantener aislamiento multiempresa en todas las consultas y mutaciones.
## Base de datos y Sequelize
- Mantener nombres de tablas y columnas coherentes con las convenciones existentes.
- No introducir acceso directo a modelos Sequelize fuera de Infrastructure.
- Ejecutar operaciones agregadas dentro de una transacción cuando deban ser atómicas.
- Diseñar migraciones idempotentes cuando el procedimiento del proyecto lo requiera.
- No eliminar columnas, tablas o datos legacy sin una auditoría previa de referencias.
- No confiar únicamente en restricciones de aplicación cuando una invariante crítica pueda reforzarse en base de datos.
- Evitar `JSON.stringify` para detectar cambios de dominio.
- Comparar Value Objects semánticamente mediante sus primitivas o métodos de igualdad adecuados.
## Comandos y entorno de ejecución
El código se encuentra físicamente en una máquina Ubuntu compartida, aunque pueda aparecer en Windows como unidad `Z:`.
Preferir comandos para Linux ejecutados mediante SSH sobre Ubuntu.
No usar rutas Windows como opción principal.
Ejemplos preferidos:
```bash
pnpm --filter <workspace> typecheck
pnpm --filter <workspace> lint
pnpm --filter <workspace> test
```
Usar comandos acotados al workspace afectado cuando sea suficiente.
Usar comandos globales cuando el cambio pueda afectar contratos compartidos o varios workspaces.
## Validación obligatoria
Antes de finalizar un cambio, ejecutar las comprobaciones relevantes disponibles para el área modificada:
```bash
pnpm typecheck
pnpm lint
pnpm test
```
Cuando corresponda, ejecutar además:
```bash
pnpm check
pnpm build
```
Criterios:
- Preferir validación dirigida al paquete o módulo afectado.
- Ejecutar validación global si se modifican paquetes compartidos, contratos o configuración transversal.
- Informar exactamente qué comandos se ejecutaron.
- Informar claramente cualquier validación no ejecutada y su motivo.
- No considerar completada una tarea con errores conocidos de lint, typecheck o tests salvo que el usuario haya limitado expresamente el alcance.
## Tests
- Añadir o actualizar tests cuando cambie comportamiento observable.
- No escribir tests que dependan de detalles internos irrelevantes.
- Cubrir casos válidos, inválidos, límites e idempotencia cuando corresponda.
- Mantener tests deterministas.
- No usar esperas arbitrarias.
- No desactivar tests para hacer pasar la suite.
## Refactors
Un refactor debe mantener el comportamiento salvo que se indique lo contrario.
Antes de eliminar código legacy:
1. buscar referencias en todo el monorepo;
2. clasificar cada referencia como activa, legacy no cableada, migración, documentación o falso positivo;
3. revisar rutas, composition roots, DI, imports, modelos, repositorios, mappers, tests y scripts;
4. eliminar únicamente lo demostrado como no utilizado;
5. ejecutar typecheck, lint y tests relevantes;
6. documentar referencias residuales y motivos.
## Respuesta final del agente
Al terminar una tarea de código, indicar de forma concisa:
- cambios realizados;
- decisiones relevantes;
- archivos principales modificados;
- comandos de validación ejecutados;
- resultado de las validaciones;
- riesgos o pendientes reales.
No afirmar que algo compila, funciona o está probado si no se ha verificado.
## Regla de precedencia
En caso de conflicto, aplicar este orden:
1. instrucciones explícitas del usuario para la tarea actual;
2. `AGENTS.md` más cercano al archivo modificado;
3. este `AGENTS.md` raíz;
4. convenciones existentes del código;
5. preferencias generales del agente.
Si una instrucción contradice la arquitectura o genera un riesgo relevante, señalarlo antes de introducir el cambio.

View File

@ -1,28 +1,60 @@
# ERP Monorepo
Monorepo con aplicaciones, servidor y paquetes modulares para el ERP.
Monorepo del ERP con aplicaciones, módulos de dominio y paquetes compartidos.
## Estado actual
El `README` raíz actúa como puerta de entrada del repositorio. La documentación funcional, operativa y arquitectónica vive en [`docs/README.md`](./docs/README.md).
- El runtime nuevo de autenticación y sesión vive en `modules/identity`.
- `modules/companies` es la fuente de verdad de la ficha operativa de empresa.
- `modules/auth` debe considerarse legacy/deprecated para backend nuevo.
- El frontend ERP ya usa `@erp/identity/client` como runtime actual.
## Estructura
## Documentación clave
```text
apps/ aplicaciones ejecutables
modules/ módulos de negocio y backend
packages/ librerías compartidas
docs/ documentación operativa y arquitectónica
scripts/ utilidades de desarrollo
tools/ herramientas auxiliares del workspace
```
- [Docs index](./docs/README.md)
- [Identity and companies](./docs/architecture/identity-and-companies.md)
- [Tenant context y `X-Company-Id`](./docs/architecture/tenant-context.md)
- [Identity auth API](./docs/architecture/identity-auth-api.md)
- [Frontend auth and company selection](./docs/frontend/auth-and-company-selection.md)
- [Seed local admin](./docs/dev/seed-local-admin.sql)
Áreas principales del workspace:
## Reglas rápidas
- `apps/web`: frontend principal del ERP.
- `apps/server`: runtime servidor del monorepo.
- `modules/*`: módulos como `identity`, `companies`, `customer-invoices` o `document-series`.
- `packages/*`: utilidades compartidas como `rdx-ddd`, `rdx-utils`, `rdx-ui` e `i18n`.
- Backend nuevo: usar `@erp/identity/api`.
- Rutas solo autenticadas: `requireIdentityAuthenticated(params)`.
- Rutas tenant-scoped: `requireIdentityTenant(params)`.
- `GET /companies/available` es el endpoint vigente para companies accesibles.
- No documentar ni introducir `GET /identity/companies`.
- No meter `companyId` ni `companySlug` dentro del access token.
## Requisitos
- Node.js `>= 24`
- PNPM `10.33.0`
## Primeros pasos
```bash
pnpm install
pnpm dev
```
Comandos frecuentes:
```bash
pnpm dev:server
pnpm dev:client
pnpm lint
pnpm check
pnpm typecheck
pnpm build
```
## Documentación
- Índice general: [`docs/README.md`](./docs/README.md)
- Frontend: [`apps/web/README.md`](./apps/web/README.md)
- Identity: [`modules/identity/README.md`](./modules/identity/README.md)
- Companies: [`modules/companies/README.md`](./modules/companies/README.md)
- Auth legacy: [`modules/auth/README.md`](./modules/auth/README.md)
## Forma de uso
- Usa este `README` para orientarte en el repositorio y arrancar el entorno.
- Usa `docs/` para contratos, decisiones de arquitectura, migraciones y runbooks.
- Usa los `README` de cada app o módulo para detalles locales de implementación.

View File

@ -2,45 +2,24 @@
Aplicación frontend principal del ERP.
## Documentación canónica
- Flujo de autenticación y selección de empresa: [`../../docs/frontend/auth-and-company-selection.md`](../../docs/frontend/auth-and-company-selection.md)
- Contexto tenant y reglas de `X-Company-Id`: [`../../docs/architecture/tenant-context.md`](../../docs/architecture/tenant-context.md)
## Runtime actual de autenticación
La app usa:
```txt
@erp/identity/client
IdentityAuthSessionProvider
DataSourceProvider
Axios compartido
```
No debe usarse `@erp/auth/client` como runtime nuevo.
La app usa `@erp/identity/client` como runtime actual.
## Flujo actual
```txt
login
-> GET /identity/auth/session
-> GET /companies/available
-> resolver empresa activa
-> auto-selección si hay una sola empresa
-> selector si hay varias
-> /no-companies si no hay empresas
-> X-Company-Id en rutas tenant-scoped
```
- La sesión y la empresa activa se resuelven según el flujo documentado en `docs/frontend/auth-and-company-selection.md`.
- Las rutas tenant-scoped deben respetar las reglas de `X-Company-Id` documentadas en `docs/architecture/tenant-context.md`.
## Headers
No enviar `X-Company-Id` a:
```txt
POST /identity/auth/login
POST /identity/auth/refresh
POST /identity/auth/logout
GET /identity/auth/session
GET /companies/available
```
Sí enviarlo a rutas tenant-scoped cuando exista `activeCompanyId`.
- No duplicar aquí la matriz de headers.
- Consultar `docs/frontend/auth-and-company-selection.md` para endpoints excluidos y `tenant-context.md` para reglas tenant-scoped.
## App company switcher
@ -62,13 +41,10 @@ Reglas:
- no llamar directamente a `GET /companies/available`
- no usar `companySlug` para auth
- no modificar el access token
- respetar el flujo canónico documentado en `docs/frontend/auth-and-company-selection.md`
## Refresh automático
La app implementa refresh automático global sobre `401` con retry único por request y `refreshPromise` compartida.
La app implementa refresh automático global sobre `401`.
Si el refresh falla:
- limpiar sesión
- limpiar empresa activa persistida
- redirigir a `/login`
La semántica completa de retry, exclusiones y limpieza de sesión se documenta en `docs/frontend/auth-and-company-selection.md`.

View File

@ -1,422 +1,19 @@
## **Diseño de API RESTful**
# API (Legacy Draft / Obsoleto)
### **1. Autenticación y Gestión de Sesiones**
Este documento conserva un borrador antiguo de API REST y ya no describe el runtime vigente del ERP.
#### **1.1. Inicio de sesión**
## No usar como fuente de verdad
**Endpoint:**
`POST /api/auth/login`
Los endpoints, payloads y rutas aquí recogidos pertenecen a una fase anterior del proyecto y entran en conflicto con la arquitectura actual basada en `modules/identity`, `modules/companies` y los contratos vigentes de cada módulo.
**Solicitud:**
## Fuentes actuales
```json
{
"email": "usuario@empresa.com",
"password": "contraseña_segura"
}
```
- Auth y sesión: [architecture/identity-auth-api.md](./architecture/identity-auth-api.md)
- Contexto tenant: [architecture/tenant-context.md](./architecture/tenant-context.md)
- Relación `identity` / `companies`: [architecture/identity-and-companies.md](./architecture/identity-and-companies.md)
- Frontend auth: [frontend/auth-and-company-selection.md](./frontend/auth-and-company-selection.md)
- Customer invoices y proformas: [customer-invoices/README.md](./customer-invoices/README.md)
**Respuesta:**
## Motivo de conservación
```json
{
"token": "eyJhbGciOi...",
"user": {
"id": 123,
"username": "usuario",
"email": "usuario@empresa.com"
},
"companies": [
{ "id": 1, "name": "Empresa A" },
{ "id": 2, "name": "Empresa B" }
]
}
```
---
#### **1.2. Cierre de sesión**
**Endpoint:**
`POST /api/auth/logout`
**Encabezados:**
```
Authorization: Bearer <token>
```
**Respuesta:**
```json
{
"message": "Sesión cerrada exitosamente"
}
```
---
#### **1.3. Selección de empresa activa**
**Endpoint:**
`POST /api/auth/select-company`
**Solicitud:**
```json
{
"company_id": 1
}
```
**Respuesta:**
```json
{
"message": "Empresa seleccionada exitosamente"
}
```
---
### **2. Gestión de Empresas**
#### **2.1. Listar empresas disponibles**
**Endpoint:**
`GET /api/companies`
**Encabezados:**
```
Authorization: Bearer <token>
```
**Respuesta:**
```json
[
{
"id": 1,
"name": "Empresa A",
"country_code": "ES",
"currency_code": "EUR"
},
{ "id": 2, "name": "Empresa B", "country_code": "US", "currency_code": "USD" }
]
```
---
#### **2.2. Crear una nueva empresa**
**Endpoint:**
`POST /api/companies`
**Solicitud:**
```json
{
"name": "Nueva Empresa",
"country_code": "ES",
"currency_code": "EUR"
}
```
**Respuesta:**
```json
{
"id": 3,
"message": "Empresa creada exitosamente"
}
```
---
#### **2.3. Actualizar datos de una empresa**
**Endpoint:**
`PUT /api/companies/{company_id}`
**Solicitud:**
```json
{
"name": "Empresa Actualizada",
"currency_code": "USD"
}
```
**Respuesta:**
```json
{
"message": "Empresa actualizada correctamente"
}
```
---
#### **2.4. Eliminar una empresa**
**Endpoint:**
`DELETE /api/companies/{company_id}`
**Respuesta:**
```json
{
"message": "Empresa eliminada exitosamente"
}
```
---
### **3. Gestión de Sucursales**
#### **3.1. Listar sucursales de una empresa**
**Endpoint:**
`GET /api/companies/{company_id}/branches`
**Respuesta:**
```json
[
{ "id": 10, "name": "Sucursal Madrid" },
{ "id": 20, "name": "Sucursal Barcelona" }
]
```
---
#### **3.2. Crear una sucursal**
**Endpoint:**
`POST /api/companies/{company_id}/branches`
**Solicitud:**
```json
{
"name": "Sucursal Sevilla",
"location": "Avenida Principal 123"
}
```
**Respuesta:**
```json
{
"id": 30,
"message": "Sucursal creada exitosamente"
}
```
---
#### **3.3. Actualizar datos de una sucursal**
**Endpoint:**
`PUT /api/branches/{branch_id}`
**Solicitud:**
```json
{
"name": "Sucursal Actualizada",
"location": "Calle Nueva 456"
}
```
**Respuesta:**
```json
{
"message": "Sucursal actualizada correctamente"
}
```
---
#### **3.4. Eliminar una sucursal**
**Endpoint:**
`DELETE /api/branches/{branch_id}`
**Respuesta:**
```json
{
"message": "Sucursal eliminada exitosamente"
}
```
---
### **4. Gestión de Usuarios**
#### **4.1. Listar usuarios de una empresa**
**Endpoint:**
`GET /api/companies/{company_id}/users`
**Respuesta:**
```json
[
{
"id": 1,
"username": "admin",
"email": "admin@empresa.com",
"roles": ["Administrador"]
},
{
"id": 2,
"username": "usuario",
"email": "usuario@empresa.com",
"roles": ["Ventas"]
}
]
```
---
#### **4.2. Crear un usuario**
**Endpoint:**
`POST /api/users`
**Solicitud:**
```json
{
"username": "nuevo_usuario",
"email": "nuevo@empresa.com",
"password": "clave_segura",
"company_id": 1,
"roles": ["Ventas"]
}
```
**Respuesta:**
```json
{
"id": 3,
"message": "Usuario creado exitosamente"
}
```
---
#### **4.3. Actualizar usuario**
**Endpoint:**
`PUT /api/users/{user_id}`
**Solicitud:**
```json
{
"email": "nuevo_correo@empresa.com",
"roles": ["Compras"]
}
```
**Respuesta:**
```json
{
"message": "Usuario actualizado correctamente"
}
```
---
#### **4.4. Eliminar usuario**
**Endpoint:**
`DELETE /api/users/{user_id}`
**Respuesta:**
```json
{
"message": "Usuario eliminado exitosamente"
}
```
---
### **5. Gestión de Permisos**
#### **5.1. Obtener permisos de un usuario en una empresa**
**Endpoint:**
`GET /api/companies/{company_id}/users/{user_id}/permissions`
**Respuesta:**
```json
{
"modules": {
"Presupuestos": ["read", "write"],
"Facturas": ["read"],
"Clientes": ["read", "write", "delete"]
}
}
```
---
#### **5.2. Actualizar permisos de un usuario**
**Endpoint:**
`PUT /api/users/{user_id}/permissions`
**Solicitud:**
```json
{
"modules": {
"Presupuestos": ["read", "write"],
"Inventario": ["read"]
}
}
```
**Respuesta:**
```json
{
"message": "Permisos actualizados correctamente"
}
```
---
### **6. Auditoría**
#### **6.1. Obtener historial de cambios en documentos**
**Endpoint:**
`GET /api/audit/documents/{document_id}`
**Respuesta:**
```json
[
{
"timestamp": "2025-01-22T14:00:00Z",
"user": "admin",
"change": "Se modificó el precio de 100 a 120"
},
{
"timestamp": "2025-01-20T10:30:00Z",
"user": "usuario",
"change": "Se creó el documento"
}
]
```
Se mantiene solo por trazabilidad de descubrimiento inicial y comparación histórica.

View File

@ -1,187 +1,96 @@
# 📝 Guía de Convenciones para Nombres de Schemas
# Convención de Nombres de Schemas DTO
Esta guía define cómo nombrar los Schemas de **Requests** y **Responses** según el tipo de operación y el contexto (recurso único o colección).
Guía específica de naming para schemas DTO. Complementa, pero no sustituye, a [DTOS - GUÍA DE DISEÑO.md](./DTOS - GUÍA DE DISEÑO.md), que sigue siendo la referencia principal de diseño.
---
## Alcance
## 1️⃣ Operaciones sobre recurso único
Usa este documento cuando la duda sea cómo nombrar un schema. Si la duda es de semántica, tipado, opcionalidad o modelado del contrato, usa antes la guía general de DTOs.
**Patrón**
<Verbo><Entidad><Condición><TipoDeSchema>
## Reglas base
1. Usar singular para el nombre base de la entidad: `Customer`, `Product`, `Proforma`.
2. En operaciones sobre recurso único, el verbo va primero.
3. En listados, el sustantivo principal va primero: `CustomerList`, nunca `ListCustomer`.
4. Mantener sufijos estables según el rol del schema.
5. No mezclar convenciones distintas para la misma familia de endpoints.
- **Verbo**: describe la acción (`Get`, `Delete`, `Update`, `Patch`, `Create`).
- **Entidad**: en singular (`Product`, `Customer`, `Invoice`).
- **Condición**: criterio de selección (`ById`, `BySlug`, `ByEmail`).
- **TipoDeSchema**: origen o propósito (`ParamsSchema`, `CommandSchema`, `QuerySchema`).
## Patrones recomendados
**Ejemplos ✅**
- `GetProductByIdParamsSchema`
- `DeleteCustomerByIdParamsSchema`
- `UpdateInvoiceByIdCommandSchema`
### Recurso único
**Ejemplos ❌**
- `ProductGetByIdParamsSchema` (orden invertido, menos natural).
- `GetProductsByIdParamsSchema` (plural innecesario en recurso único).
```text
<Verb><Entity><Condition><SchemaRole>
```
**Lectura natural**: *"Esquema de parámetros para obtener un producto por id"*.
Ejemplos:
---
## 2️⃣ Operaciones sobre colecciones
**Patrón**
<Entidad>List<Criterio/Filtro><TipoDeSchema>
- **Entidad**: en singular (`Product`, `Customer`).
- **List**: indica conjunto/listado.
- **Criterio/Filtro**: tipo de filtrado (`Criteria`, `Query`).
- **TipoDeSchema**: `Schema` o `ResponseSchema` según corresponda.
**Ejemplos ✅**
- `ProductListCriteriaSchema`
- `CustomerListResponseSchema`
- `InvoiceListQuerySchema`
**Ejemplos ❌**
- `ListProductCriteriaSchema` (el orden “ListProduct” suena a verbo y no a colección).
- `ProductsListCriteriaSchema` (plural innecesario en el nombre base).
**Lectura natural**: *"Esquema de criterios para una lista de productos"*.
---
## 3️⃣ Reglas generales
1. **Singular siempre** para el nombre base de la entidad.
2. El **verbo solo** en operaciones sobre un recurso único o mutaciones.
3. En listados, el **sustantivo principal primero** (`ProductList`, `CustomerList`), nunca `ListProduct`.
4. Sufijos estándar:
- `CommandSchema` → body de mutaciones (create/update/patch).
- `ParamsSchema` → route params (`/:id`).
- `QuerySchema` → query string no normalizada.
- `CriteriaSchema` → query normalizada con patrón Criteria.
- `ResponseSchema` → respuesta de la API.
5. Mantener **consistencia**: si usas `ProductListCriteriaSchema`, no alternes con `ProductsCriteriaSchema`.
---
## 4️⃣ Ejemplos positivos y negativos
| ✅ Correcto | ❌ Incorrecto | Motivo del error |
|------------------------------------------|---------------------------------------------|----------------------------------------------|
| `GetCustomerByIdParamsSchema` | `CustomerGetByIdParamsSchema` | Orden invertido. |
| `ProductListCriteriaSchema` | `ListProductCriteriaSchema` | Orden “ListProduct” no natural en inglés. |
| `CustomerListResponseSchema` | `CustomersListResponseSchema` | Plural innecesario. |
| `DeleteInvoiceByIdParamsSchema` | `DeleteInvoicesByIdParamsSchema` | Plural innecesario en recurso único. |
| `UpdateProductByIdCommandSchema` | `UpdateProductCommandByIdSchema` | `ById` debe ir antes del tipo de esquema. |
---
# Convenciones de nombres para Schemas (Requests y Responses)
## Tabla de rutas ↔ Schemas
| **Método** | **Ruta** | **Caso de uso** | **Request Schema (validación)** | **Response Schema (DTO)** | **Notas** |
|-----------:|---------------------|--------------------------------------------------|--------------------------------------------|-------------------------------------------|-----------|
| GET | `/entities` | Listado con criterios (Criteria normalizado) | `EntityListCriteriaSchema` | `EntityListResponseSchema` | `data: EntitySummary[]`, `pageNumber`, `pageSize`, `totalItems`. |
| GET | `/entities` | Búsqueda/filtrado por query params “raw” | `SearchEntityQuerySchema` | `EntityListResponseSchema` | Úsalo si **no** aplicas patrón Criteria. |
| GET | `/entities/:id` | Obtener por ID | `GetEntityByIdParamsSchema` | `EntityDetailResponseSchema` | Si devuelves el recurso “puro”, puedes usar `EntityResourceSchema`. |
| POST | `/entities` | Crear recurso | `CreateEntityCommandSchema` | `EntityCreatedResponseSchema` **o** `EntityResourceSchema` | Si devuelves 201 + recurso completo, usa `EntityResourceSchema`. |
| PUT | `/entities/:id` | Reemplazar recurso (idempotente) | `UpdateEntityCommandSchema` + `UpdateEntityByIdParamsSchema` | `EntityResourceSchema` | También válido con PATCH para cambios parciales. |
| PATCH | `/entities/:id` | Actualizar parcialmente | `PatchEntityCommandSchema` + `UpdateEntityByIdParamsSchema` | `EntityResourceSchema` | Usa un comando separado si el payload difiere del PUT. |
| DELETE | `/entities/:id` | Eliminar por ID | `DeleteEntityByIdParamsSchema` | — (204 No Content) **o** `EntityDeletedResponseSchema` | 204 recomendado. Si devuelves body, usa `EntityDeletedResponseSchema`. |
---
## Esquemas reutilizables (Responses)
- **Item para listados:**
`EntitySummarySchema`
- **Recurso completo:**
`EntityResourceSchema`
- **Respuestas compuestas:**
- `EntityListResponseSchema` → `data: EntitySummarySchema[]` + paginación/meta
- `EntityDetailResponseSchema` → típicamente `EntityResourceSchema`
- `EntityCreatedResponseSchema` → `{ id, createdAt, ... }` si no devuelves el recurso completo
- `EntityDeletedResponseSchema` → `{ id, deleted: true }` si no usas 204
---
## Convenciones de sufijos
### Requests
- `...CommandSchema` → **body** (mutaciones)
- `...ParamsSchema` → **route params** (`/:id`)
- `...QuerySchema` → **query string** (`?page=...`)
- `...CriteriaSchema` → para queries normalizadas con patrón Criteria
### Responses
- `...ListResponseSchema` → listado paginado
- `...SummarySchema` → item de listado
- `...DetailResponseSchema` → detalle de recurso
- `...ResourceSchema` → representación completa del recurso
- `...CreatedResponseSchema` → recurso recién creado
- `...DeletedResponseSchema` → confirmación de borrado (si no usas 204)
# Ejemplos de nombres de Schemas por entidad
## 1. Customers
### Requests
- `CreateCustomerCommandSchema`
- `UpdateCustomerCommandSchema`
- `PatchCustomerCommandSchema`
- `DeleteCustomerByIdParamsSchema`
- `GetCustomerByIdParamsSchema`
- `CustomerListCriteriaSchema`
- `SearchCustomerQuerySchema`
- `DeleteCustomerByIdParamsSchema`
- `UpdateIssuedInvoiceByIdCommandSchema`
### Responses
### Colecciones
```text
<Entity>List<CriteriaOrFilter><SchemaRole>
```
Ejemplos:
- `CustomerListCriteriaSchema`
- `ProformaListResponseSchema`
- `IssuedInvoiceListQuerySchema`
## Sufijos estables
### Request
- `CommandSchema`: body de create, update o patch
- `ParamsSchema`: route params
- `QuerySchema`: query string raw
- `CriteriaSchema`: query normalizada con patrón Criteria
### Response
- `SummarySchema`: item de listado
- `ResourceSchema`: representación completa del recurso
- `DetailResponseSchema`: response de detalle
- `ListResponseSchema`: response de listado
- `CreatedResponseSchema`: confirmación o payload de creación
- `DeletedResponseSchema`: confirmación explícita de borrado cuando no se usa `204`
## Ejemplos
### Customers
- `CreateCustomerCommandSchema`
- `GetCustomerByIdParamsSchema`
- `CustomerListCriteriaSchema`
- `CustomerSummarySchema`
- `CustomerResourceSchema`
- `CustomerListResponseSchema`
- `CustomerDetailResponseSchema`
- `CustomerCreatedResponseSchema`
- `CustomerDeletedResponseSchema`
---
### Proformas
## 2. Products
- `CreateProformaCommandSchema`
- `UpdateProformaByIdParamsSchema`
- `ProformaListCriteriaSchema`
- `ProformaSummarySchema`
- `ProformaDetailResponseSchema`
### Requests
- `CreateProductCommandSchema`
- `UpdateProductCommandSchema`
- `PatchProductCommandSchema`
- `DeleteProductByIdParamsSchema`
- `GetProductByIdParamsSchema`
- `ProductListCriteriaSchema`
- `SearchProductQuerySchema`
## Errores a evitar
### Responses
- `ProductSummarySchema`
- `ProductResourceSchema`
- `ProductListResponseSchema`
- `ProductDetailResponseSchema`
- `ProductCreatedResponseSchema`
- `ProductDeletedResponseSchema`
- `ListCustomerCriteriaSchema`
- `CustomersListResponseSchema`
- `UpdateCustomerCommandByIdSchema`
- `ProductGetByIdParamsSchema`
---
## Relación con la guía general
## Notas
- Reutilizar esquemas básicos (`EntitySummarySchema`, `EntityResourceSchema`) en respuestas compuestas para cumplir el principio **DRY**.
- **Sufijos**: mantener siempre los sufijos `CommandSchema`, `ParamsSchema`, `QuerySchema`, `CriteriaSchema`, `ListResponseSchema`, `SummarySchema`, `DetailResponseSchema`, `ResourceSchema`, `CreatedResponseSchema`, `DeletedResponseSchema`.
- **Pluralización**: usar singular para el nombre de la entidad en los Schemas (ej. `Customer`, `Product`), incluso si la ruta es plural (`/customers`, `/products`).
- **Carpetas sugeridas**:
modules/
<entity>/
application/
dto/
requests/
responses/
domain/
infrastructure/
api/
La guía de DTOs define:
- organización por carpetas
- tipado semántico
- manejo de `null`, `optional()` y colecciones
- diseño de `create`, `update`, `list` y `detail`
Este documento solo fija el naming.

View File

@ -1,5 +1,9 @@
# Guía de diseño de DTOs para el ERP
Documento principal para el diseño de DTOs y schemas HTTP del proyecto.
Para reglas de naming más puntuales, consultar también [CONVENCION NOMBRADO DE ESQUEMAS DTO.md](./CONVENCION NOMBRADO DE ESQUEMAS DTO.md).
## 1. Principios generales
- Un DTO representa únicamente el contrato de transporte entre cliente y servidor.
@ -409,4 +413,4 @@ Antes de cerrar un DTO, comprobar:
9. ¿El mismo campo mantiene el mismo tipo en todo el módulo?
10. ¿El DTO refleja una vista concreta del endpoint?
---
---

View File

@ -1,56 +1,20 @@
# Facturas de Cliente (Notas Históricas)
Este fichero contenía notas sueltas y ejemplos tempranos sobre facturación. Ya no actúa como documento operativo.
Funcionalidades
---------------
## Estado
- Llamar a las facturas "borrador" como PROFORMA??
- histórico
- no canónico
- supersedido por la documentación de `customer-invoices`
- Baja lógica de facturas
- Los datos del cliente están en la factura
- Descuento general (2 decimales)
- Resumen de impuestos
- Referencia de cliente -> ???
- Idioma
- Divisa
- Observaciones
- Descripción de la operación -> obligatorio según Verifactu
## Documentación vigente
- [Índice de `customer-invoices`](./customer-invoices/README.md)
- [Contrato de create de proformas](./customer-invoices/proforma-create-contract.md)
- [Contrato de series de proformas](./customer-invoices/proforma-series-contract.md)
- [Reports de proformas](./customer-invoices/proformas-reports.md)
Detalles:
- Descripción
- Cantidad -> 2 decimales
- Importe unidad -> 4 decimales
- IVA en los detalles
- Descuento -> 2 decimales
## Motivo de conservación
Futuros:
- Unidad de medida -> texto
Formas de pago:
- Sin plazo -> texto + establecer una fecha manualemente en el futuro
- Con plazo -> texto + fechas pre-establecidas no editables
{
"serie": "A",
"numero": "2",
"fecha_expedicion": "15-07-2025",
"tipo_factura": "F1",
"descripcion": "Mensualidad de soporte",
"nif": "A15022510",
"nombre": "Empresa de prueba SL",
"lineas": [
{
"base_imponible": "200",
"tipo_impositivo": "21",
"impuesto": "01",
"clave_regimen": "01",
"cuota_repercutida": "42"
}
],
"importe_total": "242"
}
Se mantiene solo como rastro de brainstorming previo y no debe emplearse para extraer contratos, payloads ni reglas funcionales actuales.

View File

@ -1,87 +1,83 @@
# ERP Docs
Este directorio reúne la documentación operativa y arquitectónica del ERP.
Este directorio reúne la documentación técnica, operativa y de apoyo del ERP.
## Contexto técnico
Monorepo con:
- Node.js >= 22
- TypeScript
- PNPM
- Turborepo
- ExpressJS
- MariaDB
- Sequelize v6
- Vitest
## Cómo leer esta documentación
Arquitectura:
- DDD pragmático
- Clean Architecture
- Separación Domain / Application / Infrastructure
- SOLID, DRY, KISS, YAGNI, SSOT
- Fail Fast
- Defensive Programming
- Cambios incrementales
- Evitar sobreingeniería
- Empieza por este índice si necesitas orientarte.
- Usa los documentos en subdirectorios temáticos como fuente de verdad operativa.
- Trata los documentos marcados como históricos o legacy como contexto, no como contrato vigente.
## Documentación vigente
## Estructura documental
### Arquitectura general
### Documentación vigente
- [Arquitectura de identity y companies](./architecture/identity-and-companies.md)
#### Arquitectura y runtime
- [Arquitectura general](./architecture/README.md)
- [Contexto tenant y `X-Company-Id`](./architecture/tenant-context.md)
- [API de autenticación identity](./architecture/identity-auth-api.md)
- [Estado de migración a identity](./architecture/identity-migration-status.md)
- [Flujo frontend de auth y selección de empresa](./frontend/auth-and-company-selection.md)
- [API de autenticación de `identity`](./architecture/identity-auth-api.md)
- [Relación entre `identity` y `companies`](./architecture/identity-and-companies.md)
- [Estado de migración a `identity`](./architecture/identity-migration-status.md)
### Document series
#### Frontend
- [Flujo de auth y selección de empresa](./frontend/auth-and-company-selection.md)
- [README de `apps/web`](../apps/web/README.md)
#### Customer invoices y proformas
- [Índice de `customer-invoices`](./customer-invoices/README.md)
- [Contrato de series de proformas](./customer-invoices/proforma-series-contract.md)
- [Contrato de create de proformas](./customer-invoices/proforma-create-contract.md)
- [Configuración fiscal persistida en proformas](./customer-invoices/proforma-tax-config.md)
- [Reports de proformas](./customer-invoices/proformas-reports.md)
#### Document series
- [README de `document-series`](./document-series/README.md)
- [Diseño de `document-series`](./document-series/document-series-design.md)
- [Validación manual de `document-series`](./document-series/manual-validation.md)
- [Validación manual de concurrencia](./document-series/manual-concurrency-validation.md)
### Customer-invoices / proformas
#### Módulos y runtime local
- [Contrato de series de proformas](./customer-invoices/proforma-series-contract.md)
- [Contrato de create de proformas](./customer-invoices/proforma-create-contract.md)
- [Tax config persistido en proformas](./customer-invoices/proforma-tax-config.md)
### SQL operativos / runbooks vigentes
- [DDL de series documentales en proformas](./customer-invoices/sql/add-proforma-document-series-columns.sql)
- [DDL de `tax_config` en proformas](./customer-invoices/sql/add-proforma-tax-config-columns.sql)
- [Validación de migración a `document_series`](./document-series/sql/validate-document-series-migration.sql)
- [Validación de `tax_config` en proformas](./customer-invoices/sql/validate-proforma-tax-config.sql)
### Referencias de módulos
- [README de `modules/auth`](../modules/auth/README.md)
- [README de `modules/identity`](../modules/identity/README.md)
- [README de `modules/companies`](../modules/companies/README.md)
- [README de `apps/web`](../apps/web/README.md)
- [README de `modules/auth`](../modules/auth/README.md)
## Histórico / migraciones transicionales
#### Guías y estándares
- [Migración desde `customer_invoice_series` a `document_series`](./document-series/migration-from-customer-invoice-series.md)
- [Split histórico Proforma / IssuedInvoice](./customer-invoices/split-proformas-issued-invoices-migration.md)
- [InvoiceSeries legacy](./customer-invoices/invoice-series.md)
- [Drop manual de `customer_invoice_series`](./document-series/sql/drop-customer-invoice-series-legacy.sql)
- [SQL histórico de `customer_invoice_series`](./dev/customer-invoice-series.sql)
- [Runbooks SQL históricos de `customer-invoices`](./customer-invoices/sql/README.md)
- [Guía de diseño de DTOs](./DTOS - GUÍA DE DISEÑO.md)
- [Convención de nombres de schemas DTO](./CONVENCION NOMBRADO DE ESQUEMAS DTO.md)
#### SQL operativos y runbooks
- [SQL de `customer-invoices`](./customer-invoices/sql/README.md)
- [Validación de migración a `document-series`](./document-series/sql/validate-document-series-migration.sql)
- [Seed local de admin](./dev/seed-local-admin.sql)
## Estado actual resumido
### Material auxiliar
- El backend nuevo usa `modules/identity` para autenticación, sesión y control de acceso a companies.
- `modules/companies` es la fuente de verdad de la ficha operativa de empresa.
- El frontend ERP ya usa `@erp/identity/client` como runtime de autenticación.
- El flujo actual resuelve sesión, companies disponibles, auto-selección de empresa única y refresh automático sobre `401`.
- [Base común para prompts backend](./backend-prompt-base.md)
- [Prompt de auth](./prompt-auth.md)
- [Prompt de customer invoices](./prompt-customer-invoices.md)
- [Plantilla auxiliar](./TEMPLATE.md)
## Reglas de lectura rápida
### Histórico, transicional o de descubrimiento
- `modules/auth` debe considerarse legacy/deprecated para backend nuevo.
- `GET /companies/available` es el endpoint vigente para cargar companies accesibles.
- `GET /identity/companies` no es un endpoint válido del diseño actual.
- `GET /account/companies` tampoco es un endpoint válido del diseño actual.
- Las rutas tenant-scoped deben usar `Authorization` + `X-Company-Id`.
- `/document-series` es la API canónica de series documentales.
- `/catalogs/invoice-series` no es un endpoint activo.
Estos documentos se conservan por trazabilidad, auditoría o contexto de decisiones. No deben usarse como fuente de verdad del runtime actual sin contrastarlos con la documentación vigente.
- [Borrador legacy de API](./API.md)
- [Requisitos generales históricos](./REQUISITOS GENERALES.md)
- [Requisitos históricos del módulo de clientes](./REQUISITOS CLIENTES.md)
- [Notas históricas de facturas de cliente](./FACTURAS DE CLIENTE.md)
- [Split histórico Proforma / IssuedInvoice](./customer-invoices/split-proformas-issued-invoices-migration.md)
- [Migración desde `customer_invoice_series`](./document-series/migration-from-customer-invoice-series.md)
- [InvoiceSeries legacy](./customer-invoices/invoice-series.md)
## Regla práctica de mantenimiento
- Los documentos operativos vigentes deben vivir en subdirectorios temáticos y tener una responsabilidad clara.
- Los documentos raíz en mayúsculas o con naming heredado deben considerarse legacy o auxiliares salvo que este índice los declare explícitamente vigentes.
- Si una regla ya tiene fuente canónica, los demás documentos deben resumirla y enlazarla, no copiarla.

View File

@ -1,165 +1,32 @@
# **Especificaciones del Módulo de Clientes - ERP**
# Requisitos del Módulo de Clientes (Histórico / Descubrimiento)
El **módulo de clientes** del ERP permite gestionar la información de clientes, asegurando compatibilidad con la gestión multiempresa, multiidioma y multi-moneda. A continuación, se detallan las especificaciones, incluyendo los campos clave y su función.
Este documento recoge requisitos tempranos del módulo de clientes y ya no debe leerse como contrato técnico vigente.
---
## Estado
## **1. Funcionalidades del Módulo de Clientes**
El módulo de clientes debe permitir:
- **Crear, leer, actualizar y eliminar clientes** (CRUD).
- **Asociar clientes a una empresa** y, opcionalmente, a una o más sucursales.
- **Registrar clientes como personas físicas o empresas**.
- **Gestionar datos fiscales, comerciales y de contacto**.
- **Definir configuraciones de facturación y pago personalizadas**.
- **Aplicar descuentos en distintos niveles**.
- **Manejar aspectos financieros como retenciones y recargos**.
- **Identificar clientes con riesgo financiero**.
- **Asignar comerciales o delegados**.
- **Controlar el estado del cliente** (activo/inactivo).
- **Registrar auditoría de cambios**.
- Documento histórico de análisis funcional.
- Aporta vocabulario de negocio y posibles campos de cliente.
- No define endpoints, DTOs ni persistencia actuales del módulo.
---
## Qué puede seguir siendo útil
## **2. Estructura del Cliente y sus Campos**
Cada cliente tiene los siguientes atributos:
- tipologías de datos de cliente
- necesidades de facturación, fiscalidad y contacto
- consideraciones de auditoría y permisos
### **Datos Generales**
| Campo | Tipo de Dato | Descripción |
|-----------------------|-----------------|-------------|
| `id` | `INT` (PK) | Identificador único del cliente. |
| `company_id` | `INT` (FK) | Empresa a la que pertenece el cliente. |
| `is_company` | `BOOLEAN` | Indica si el cliente es una empresa (`true`) o una persona física (`false`). |
| `fiscal_name` | `VARCHAR(255)` | Nombre fiscal del cliente (solo si es empresa). |
| `commercial_name` | `VARCHAR(255)` | Nombre comercial del cliente (si aplica). |
| `name` | `VARCHAR(255)` | Nombre de la persona o empresa. |
| `email` | `VARCHAR(100)` | Correo electrónico único del cliente. |
| `phone` | `VARCHAR(20)` | Número de teléfono de contacto. |
| `address` | `TEXT` | Dirección del cliente. |
| `country_code` | `CHAR(2)` | Código del país del cliente (ISO 3166-1 alpha-2). |
| `origin` | `VARCHAR(100)` | Origen del cliente (ej. publicidad, redes sociales, feria, escaparate). |
## Qué no debe asumirse como vigente
### **Configuración de Moneda e Idioma**
| Campo | Tipo de Dato | Descripción |
|---------------------|-----------------|-------------|
| `currency_code` | `CHAR(3)` | Código de la divisa con la que trabaja el cliente (ISO 4217). |
| `language_code` | `CHAR(5)` | Idioma preferido del cliente para documentos y comunicación (ej. `es-ES`, `en-US`). |
- rutas `/api/...`
- tipos SQL y tablas propuestas
- naming técnico de campos y relaciones
- contratos HTTP o middleware de acceso
### **Información Fiscal y Financiera**
| Campo | Tipo de Dato | Descripción |
|------------------------|----------------|-------------|
| `vat_percentage` | `DECIMAL(5,2)` | Porcentaje de IVA aplicable al cliente. |
| `equivalence_charge` | `BOOLEAN` | Indica si el cliente aplica recargo de equivalencia. |
| `withholding_percentage` | `DECIMAL(5,2)` | Porcentaje de retención en facturas del cliente. |
| `payment_method` | `VARCHAR(100)` | Forma de pago habitual del cliente (ej. transferencia, tarjeta, efectivo). |
| `payment_day` | `INT` | Día del mes en que el cliente realiza pagos (1-31). |
| `risk` | `BOOLEAN` | Indica si el cliente tiene riesgo financiero o historial de morosidad. |
## Referencias actuales
### **Descuentos y Tarifas Especiales**
| Campo | Tipo de Dato | Descripción |
|---------------------|----------------|-------------|
| `discount_line` | `DECIMAL(5,2)` | Descuento aplicado a nivel de línea en presupuestos. |
| `discount_chapter` | `DECIMAL(5,2)` | Descuento aplicado a nivel de capítulo en presupuestos. |
| `discount_global` | `DECIMAL(5,2)` | Descuento global aplicado al presupuesto. |
| `price_point` | `DECIMAL(10,2)` | Valor de "precio punto" del cliente, usado para calcular costos personalizados en catálogo. |
- Arquitectura del repositorio: [README.md](../README.md)
- Índice de docs: [README.md](./README.md)
- Reglas de DTO y schemas: [DTOS - GUÍA DE DISEÑO.md](./DTOS - GUÍA DE DISEÑO.md) y [CONVENCION NOMBRADO DE ESQUEMAS DTO.md](./CONVENCION NOMBRADO DE ESQUEMAS DTO.md)
### **Clasificación y Estado**
| Campo | Tipo de Dato | Descripción |
|---------------|----------------|-------------|
| `client_type` | `VARCHAR(100)` | Tipo de cliente (ej. profesionales, constructoras, distribuidores, particulares). |
| `is_active` | `BOOLEAN` | Indica si el cliente está activo (`true`) o dado de baja (`false`). |
## Motivo de conservación
### **Delegados y Comerciales**
| Campo | Tipo de Dato | Descripción |
|-----------------|----------------|-------------|
| `Client_Sales_Rep` | `Tabla Relacional` | Relación con los comerciales asignados a este cliente. |
---
## **3. Auditoría y Seguridad**
Cada cambio en los datos de un cliente debe registrarse en una tabla de auditoría:
```sql
CREATE TABLE Client_Audit (
id SERIAL PRIMARY KEY,
client_id INT NOT NULL REFERENCES Clients(id) ON DELETE CASCADE,
user_id INT NOT NULL REFERENCES Users(id),
change_description TEXT,
change_timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```
Esto permitirá rastrear modificaciones y garantizar transparencia en la gestión de clientes.
---
## **4. Endpoints Principales de la API**
### **4.1. Listar Clientes**
```http
GET /api/companies/{company_id}/clients
Authorization: Bearer <token>
```
### **4.2. Crear Cliente**
```http
POST /api/companies/{company_id}/clients
Authorization: Bearer <token>
```
**Solicitud:**
```json
{
"is_company": true,
"fiscal_name": "Empresa XYZ SL",
"commercial_name": "XYZ Comercial",
"name": "Empresa XYZ",
"email": "contacto@xyz.com",
"phone": "+34912345678",
"country_code": "ES",
"origin": "Publicidad",
"currency_code": "EUR",
"language_code": "es-ES",
"vat_percentage": 21.00,
"payment_method": "Transferencia bancaria",
"discount_line": 5.00,
"discount_chapter": 2.50,
"discount_global": 10.00,
"price_point": 1.20,
"payment_day": 15,
"risk": false,
"equivalence_charge": false,
"withholding_percentage": 10.00,
"client_type": "Distribuidor",
"is_active": true
}
```
### **4.3. Obtener Cliente por ID**
```http
GET /api/clients/{client_id}
Authorization: Bearer <token>
```
### **4.4. Actualizar Cliente**
```http
PUT /api/clients/{client_id}
Authorization: Bearer <token>
```
### **4.5. Baja Lógica de Cliente**
```http
PATCH /api/clients/{client_id}/deactivate
Authorization: Bearer <token>
```
### **4.6. Asignar Comercial a Cliente**
```http
POST /api/clients/{client_id}/sales-reps
Authorization: Bearer <token>
```
---
## **5. Seguridad y Control de Acceso**
- **Middleware de autenticación** para validar que solo usuarios autorizados gestionen clientes.
- **Permisos basados en roles**, como `Clientes: leer`, `Clientes: escribir`, `Clientes: eliminar`.
- **Reglas de validación** para evitar datos erróneos o incompletos.
- **Registro de auditoría** para cada modificación.
---
Se conserva como material de descubrimiento funcional previo al diseño actual.

View File

@ -1,207 +1,28 @@
# **Requisitos Técnicos del ERP**
# Requisitos Generales (Histórico / Descubrimiento)
## **1. Administración de Usuarios**
Este documento resume requisitos funcionales tempranos del ERP. No define el runtime actual ni sustituye la documentación operativa vigente.
- **Roles y Perfiles**:
## Estado
- Los usuarios pueden tener uno o más perfiles predefinidos (por ejemplo, "Ventas", "Compras").
- Los perfiles controlan permisos generales a nivel de empresa y refinados a nivel de sucursal.
- Un usuario puede tener diferentes perfiles en distintas sucursales de la misma empresa.
- Documento histórico de descubrimiento.
- Útil para entender intención de producto y necesidades de negocio amplias.
- No usar como contrato técnico actual de autenticación, tenant context, JWT, base de datos o endpoints.
- **Permisos**:
## Temas que sí conserva como contexto
- Configurables por módulo (Presupuestos, Facturas, etc.).
- Granularidad: permitir acciones específicas como "leer", "escribir", "eliminar".
- Un usuario puede tener permisos para acceder a una o más sucursales de la empresa, pero debe trabajar en una empresa activa por sesión.
- Si el usuario solo tiene permisos para una sucursal, se selecciona automáticamente. Si tiene acceso a varias, se selecciona solo cuando sea necesario.
- multiempresa y multisucursal
- auditoría y trazabilidad
- seguridad y permisos
- soporte multiidioma
- extensibilidad modular
- **Superadministrador**:
- Puede crear, modificar y eliminar usuarios.
- Gestiona perfiles, permisos y asociaciones con empresas y sucursales.
## Documentación vigente relacionada
---
- Arquitectura y tenant context: [architecture/README.md](./architecture/README.md)
- Auth actual: [architecture/identity-auth-api.md](./architecture/identity-auth-api.md)
- Flujo frontend: [frontend/auth-and-company-selection.md](./frontend/auth-and-company-selection.md)
- Módulos documentales: [customer-invoices/README.md](./customer-invoices/README.md) y [document-series/README.md](./document-series/README.md)
## **2. Gestión Multiempresa y Multisucursal**
## Nota
- **Usuarios y Empresas**:
- Un usuario puede estar asociado a múltiples empresas.
- En cada sesión o pestaña, el usuario puede trabajar con una empresa específica.
- La empresa activa se elige al inicio de sesión.
- **Sucursales**:
- Cada empresa debe tener al menos una sucursal, pero puede tener varias.
- Los usuarios de una sucursal pueden gestionar datos de otras sucursales si tienen los permisos adecuados.
- Se permite definir permisos para operar en múltiples sucursales dentro de la misma empresa.
- **Flujo de Selección**:
- Al iniciar sesión, el usuario elige la empresa activa.
- Si tiene acceso a una única sucursal, esta se selecciona automáticamente.
- Si tiene acceso a múltiples sucursales, la selección de sucursal ocurre solo cuando sea necesario para una operación específica.
---
## **3. Soporte Multiidioma y Regionalización**
- **Idiomas**:
- La interfaz del ERP debe estar disponible en varios idiomas.
- Los documentos (presupuestos, facturas) pueden generarse en el idioma del cliente.
- El idioma de usuario es configurable en su perfil.
- **Monedas e Impuestos**:
- Configuración de moneda por empresa, con posibilidad de conversión.
- Definición de impuestos según país (IVA, retenciones, tasas locales).
- Flexibilidad para configurar tarifas por categoría de productos o servicios.
---
## **4. Contexto de Sesión y Pestaña**
- **Contexto por Pestaña**:
- Cada pestaña tiene un contexto independiente (`tab_id`), que incluye la empresa activa.
- Si se requiere una sucursal para la operación, se solicitará en ese momento.
- Se almacena el `tab_id` junto con la empresa activa en el backend para cada sesión de usuario.
- **Gestión de Sesiones**:
- Tokens JWT deben incluir información sobre:
- `user_id`
- `tab_id`
- `active_company_id`
- Lista de permisos específicos
- Se debe registrar el contexto de pestaña en la base de datos para auditoría y trazabilidad.
---
## **5. Auditoría y Registro de Actividades**
- **Registro de Cambios en Documentos**:
- Presupuestos, facturas y otros documentos deben mantener un historial detallado de modificaciones.
- Se registra la siguiente información:
- Usuario que realizó el cambio.
- Fecha y hora.
- Cambios específicos (antes y después).
- **Trazabilidad Global**:
- Auditar las actividades del usuario a nivel de empresa, sucursal y pestaña.
- Se debe asociar cada acción al `tab_id` para identificar actividades por pestaña específica.
---
## **6. Documentos Operativos**
- **Historial de Cambios**:
- Cada documento (presupuestos, facturas, etc.) debe registrar todas las modificaciones con la posibilidad de revertirlas si es necesario.
- **Exportación Multiidioma**:
- Generación de documentos en diferentes idiomas según la configuración del cliente.
---
## **7. Seguridad**
- **Autenticación**:
- Autenticación basada en tokens JWT.
- Contraseñas encriptadas con bcrypt o Argon2.
- Soporte para autenticación en dos pasos (2FA) en futuras iteraciones.
- **Control de Acceso**:
- Validación de permisos en cada solicitud al backend.
- Aplicación del principio de mínimos privilegios para usuarios.
---
## **8. Extensibilidad y Modularidad**
- **Estructura Modular**:
- El sistema debe permitir la integración de nuevos módulos sin afectar los permisos o el funcionamiento existente.
- Módulos actuales planificados:
- Presupuestos
- Facturas
- Clientes
- Pedidos
- Inventario (futuro)
- Recursos Humanos (futuro)
---
## **9. Base de Datos - Diseño Técnico**
### **Tablas Principales**
```sql
-- Usuarios
CREATE TABLE Users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
```
```sql
-- Empresas
CREATE TABLE Companies (
id SERIAL PRIMARY KEY,
name VARCHAR(100) UNIQUE NOT NULL,
country_code CHAR(2),
currency_code CHAR(3),
tax_config JSONB,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
```
```sql
-- Sucursales
CREATE TABLE Branches (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
company_id INT NOT NULL REFERENCES Companies(id) ON DELETE CASCADE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
```
```sql
-- Relación Usuario - Empresa
CREATE TABLE User_Company (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES Users(id),
company_id INT NOT NULL REFERENCES Companies(id),
is_active BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
```
```sql
-- Relación Usuario - Sucursal
CREATE TABLE User_Branch (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES Users(id),
branch_id INT NOT NULL REFERENCES Branches(id),
permissions JSONB NOT NULL
);
```
```sql
-- Contexto por pestaña
CREATE TABLE Tab_Context (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES Users(id),
tab_id UUID NOT NULL,
company_id INT NOT NULL REFERENCES Companies(id),
branch_id INT REFERENCES Branches(id)
);
```
---
Las decisiones antiguas sobre JWT con `active_company_id`, `tab_id`, tablas SQL de ejemplo y flujos de selección tempranos quedaron superadas por la arquitectura actual y se conservan solo a nivel histórico.

View File

@ -1,43 +1,15 @@
### Prompt para tu README / CLI
# TEMPLATE
**Generar un nuevo módulo ERP desde la plantilla**
Plantilla auxiliar para redactar documentación o prompts de trabajo.
Nuestro ERP permite crear módulos de forma **estandarizada** usando una **plantilla base** (Hexagonal/DDD, TS, Express, Sequelize) y un **generador Plop**.
## Estado
**Opción A — con Plop (recomendada)**
1. Instala dependencias de desarrollo si no las tienes:
* `pnpm add -D plop`
2. Asegúrate de tener en el repo:
* `plopfile.js` con el generador `module`
* Carpeta `templates/new-module/` con la plantilla (placeholders Handlebars: `{{kebabCase name}}`, `{{pascalCase name}}`, etc.)
3. Ejecuta el generador:
* `pnpm plop module`
4. Introduce el nombre del módulo en **kebab-case** (p. ej., `suppliers`).
5. Se creará `packages/suppliers/` con:
* `src/api/{application,domain,infrastructure,helpers}` + `common`
* Casos de uso CRUD, presenters (FULL/LIST), specs comunes y contratos de repositorio.
6. Integra en el monorepo:
* Añade targets en `turbo.json` si aplica.
* Ejecuta `pnpm -w build` para validar compilación.
**Opción B — manual (desde ZIP de plantilla)**
1. Descomprime `new-module-template.zip`.
2. Copia `new-module/` a `packages/<mi-modulo>/`.
3. Renombra los archivos con `entity`/`{{name}}` por el nombre real o usa búsqueda global.
4. Ajusta `package.json` (`name`, `scripts`), y registra el módulo en `turbo.json`.
5. `pnpm -w install && pnpm -w build`.
**Convenciones clave**
* Nombres de archivos/directorios en **kebab-case**; código en **Inglés**; comentarios **TSDoc en castellano**.
* Mantén SOLID, SoC, alta cohesión/bajo acoplamiento, y hexagonal (puertos/adaptadores).
* Observabilidad (logs estructurados), idempotencia en endpoints, y principio de menor privilegio.
- material auxiliar
- no describe comportamiento del ERP
- no debe enlazarse como documentación funcional o arquitectónica del proyecto
## Uso recomendado
- reutilizarla solo como apoyo editorial
- adaptar el contenido final al área concreta del proyecto
- no duplicar desde aquí reglas ya documentadas en los índices y guías vigentes

View File

@ -1,18 +1,25 @@
# ERP Architecture Docs
## Documentos vigentes
Índice de arquitectura vigente y documentos transicionales relacionados con autenticación, tenant context y migración a `identity`.
## Documentación canónica vigente
- [Identity and companies](./identity-and-companies.md)
- [Tenant context](./tenant-context.md)
- [Identity auth API](./identity-auth-api.md)
- [Identity backend architecture](./identity-backend-architecture.md)
- [Identity migration status](./identity-migration-status.md)
- [Identity roadmap](./identity-roadmap.md)
## Documentación complementaria
- [Identity backend architecture](./identity-backend-architecture.md)
`identity-backend-architecture.md` se mantiene como nota complementaria de diseño backend. Para contratos operativos concretos, prevalecen `tenant-context.md`, `identity-auth-api.md` y los README de módulo.
## Resumen actual
- `identity` se encarga de autenticación, sesión, refresh y memberships.
- `companies` se encarga de la ficha operativa de empresa.
- `GET /companies/available` es la vía actual para listar companies accesibles.
- `identity` gestiona autenticación, sesión, refresh y memberships.
- `companies` gestiona la ficha operativa de empresa.
- `GET /companies/available` es la vía vigente para companies accesibles.
- El contexto tenant se resuelve con `X-Company-Id` y `requireIdentityTenant(params)`.
- El frontend ERP ya usa el runtime nuevo de `identity` con auto-selección de empresa y refresh automático.
- El frontend ERP ya usa el runtime nuevo de `identity`.

View File

@ -1,5 +1,14 @@
# Identity Backend Architecture
Documento complementario de diseño backend.
Para contratos operativos vigentes, prevalecen:
- `tenant-context.md`
- `identity-auth-api.md`
- `identity-and-companies.md`
- `modules/identity/README.md`
## Objetivo del módulo `identity`
`identity` sustituye progresivamente al módulo legacy `auth`.

View File

@ -1,22 +1,10 @@
# Identity Migration Status
## Patrón final de middleware
## Referencias canónicas
Rutas tenant-scoped:
```ts
import { requireIdentityTenant } from "@erp/identity/api";
router.use(...requireIdentityTenant(params));
```
Rutas autenticadas sin tenant:
```ts
import { requireIdentityAuthenticated } from "@erp/identity/api";
router.use(...requireIdentityAuthenticated(params));
```
- Reglas de contexto tenant y middlewares públicos: [tenant-context.md](./tenant-context.md)
- Flujo frontend de autenticación y selección de empresa: [../frontend/auth-and-company-selection.md](../frontend/auth-and-company-selection.md)
- Runtime y responsabilidades del módulo `identity`: [../../modules/identity/README.md](../../modules/identity/README.md)
## Migrado a `identity`
@ -34,17 +22,11 @@ Estado:
- sin `@erp/auth/api`
- sin `mockUser`
- sin helpers locales de auth
- usan `requireIdentityTenant(params)`
- usan el middleware tenant canónico documentado en `tenant-context.md`
### Backend autenticado sin tenant
`modules/companies` usa:
```ts
import { requireIdentityAuthenticated } from "@erp/identity/api";
router.use(...requireIdentityAuthenticated(params));
```
`modules/companies` usa el middleware autenticado sin tenant documentado en `tenant-context.md`.
Esto aplica a:
@ -59,25 +41,9 @@ POST /companies/:company_id/disable
## Frontend actual
El runtime nuevo ya usa:
El runtime nuevo ya usa `@erp/identity/client`.
- `@erp/identity/client`
- `IdentityAuthSessionProvider`
- `DataSourceProvider`
- Axios compartido con refresh automático
Flujo implementado:
```txt
login
-> GET /identity/auth/session
-> GET /companies/available
-> resolver empresa activa
-> auto-selección si hay una sola empresa
-> selector si hay varias
-> /no-companies si no hay empresas
-> X-Company-Id en rutas tenant-scoped
```
Los detalles del flujo implementado, headers y refresh automático se mantienen en `frontend/auth-and-company-selection.md`.
## Legacy pendiente

View File

@ -0,0 +1,49 @@
# Backend Prompt Base
## Objetivo
Centralizar el contexto comun para prompts de backend del ERP y evitar copiar la misma plantilla en varios ficheros.
## Contexto comun
- API en `Node.js`, `TypeScript`, `Express.js` y `Sequelize`
- Arquitectura Hexagonal, DDD pragmatico y SOLID
- Controllers responsables del contrato HTTP
- Repositorios desacoplados de Sequelize
- DTOs y presenters para respuestas
## Principios de diseno
- agregados encapsulan su estado y exponen solo lo necesario
- cada agregado usa su mapper de dominio <-> persistencia
- repositorios trabajan con agregados, no con modelos ORM expuestos
- SRP, OCP, LSP, ISP y DIP aplicados de forma explicita
- dependencias expresadas mediante interfaces de aplicacion
## Reglas tecnicas
- no usar Sequelize directamente en servicios de aplicacion
- no acoplar servicios a implementaciones concretas si ya existe contrato
- mantener transformacion a DTO en controllers o presenters
- capturar errores de persistencia y mapearlos a errores de dominio o API con semantica clara
- tipar de forma explicita entradas, salidas y resultados
## Persistencia y transacciones
- los repositorios encapsulan acceso a persistencia
- los mappers reconstruyen y serializan agregados
- las transacciones se gestionan mediante el mecanismo del proyecto, no desde el controller
- mantener convenciones existentes de tablas, columnas y timestamps
## Testing y seguridad
- cubrir servicios con tests de unidad cuando cambie comportamiento observable
- cubrir rutas con tests HTTP cuando aplique
- no exponer datos sensibles en responses
- devolver codigos HTTP coherentes con el contrato del endpoint
## Uso
- `prompt-auth.md` extiende esta base para autenticacion
- `prompt-customer-invoices.md` extiende esta base para customer invoices
- si aparece otro prompt backend, debe reutilizar esta base en lugar de copiarla

View File

@ -0,0 +1,37 @@
# customer-invoices
Índice de documentación del área de `customer-invoices`, con separación explícita entre contratos vigentes, soporte operativo e histórico.
## Documentación vigente
### Contratos funcionales y de transporte
- [Contrato de series de proformas](./proforma-series-contract.md)
- [Contrato de create de proformas](./proforma-create-contract.md)
- [Configuración fiscal persistida en proformas](./proforma-tax-config.md)
- [Contrato de listado de proformas](./proforma-list-contract.md)
- [Contrato de archivado de proformas](./proforma-archive-contract.md)
- [Contrato de borrado de proformas](./proforma-delete-contract.md)
- [Contrato de duplicado de proformas](./proforma-duplicate-contract.md)
- [Contrato de series de issued invoices](./issued-invoice-series-contract.md)
- [Contrato de snapshot documental](./document-series-snapshot-contract.md)
### Reports y flujos integrados
- [Reports de proformas](./proformas-reports.md)
- [FactuGES -> Proformas V2](../factuges/create-proforma-from-factuges.md)
### SQL y validaciones
- [Índice SQL de `customer-invoices`](./sql/README.md)
## Histórico o transicional
- [Split Proformas / Issued Invoices](./split-proformas-issued-invoices-migration.md)
- [InvoiceSeries legacy](./invoice-series.md)
## Criterio de lectura
- Para semántica vigente de negocio, empieza por `proforma-series-contract.md`, `proforma-create-contract.md` y `proforma-tax-config.md`.
- Para SQL manual o runbooks, usa `sql/README.md`.
- Para decisiones antiguas, migraciones o trazabilidad, consulta solo la sección histórica.

View File

@ -25,11 +25,8 @@ En proformas:
- se pide cliente
- `proforma_series_code` es opcional; si falta, se usa la serie default activa de `document_type = proforma`
- `target_invoice_series_code` es opcional; si falta, se persiste `NULL`
- la creacion de proforma genera una cabecera valida aunque no existan lineas
- `items` puede omitirse o enviarse como `[]`
- si no hay lineas valoradas, la response debe devolver `items = []`, `taxes = []` y totales a `0`
- `tax_regime_code` en create representa configuracion fiscal inicial, no impuestos ya aplicados
- la UI obtiene ambas listas desde `GET /document-series`, filtrando por `document_type`
- la semantica detallada de create sin lineas, `tax_config`, `taxes` y `totals` vive en `proforma-create-contract.md`
### Editar draft
@ -63,17 +60,14 @@ En proformas:
- el backend emite `target_invoice_series_code` como nombre preferente
- `series` puede mantenerse solo como alias legacy/deprecated de salida mientras existan consumidores antiguos
- `taxes` representa impuestos calculados a partir de lineas valoradas
- `tax_config` representa la configuracion fiscal persistida de cabecera
- `totals` representa importes calculados a partir de lineas valoradas
- create no debe inventar filas fiscales sobre base `0`
- la semantica de `taxes`, `tax_config` y `totals` se documenta en `proforma-create-contract.md`
## Regla de emision
- si `target_invoice_series_code` tiene valor, la emision usa ese `seriesCode` para `document_type = issued_invoice`
- si `target_invoice_series_code` es `NULL`, `document-series` resuelve la serie default activa de `issued_invoice`
- la UI nunca debe consumir numeracion directa desde `POST /document-series/assign-next`
- los impuestos y totales reales se recalculan cuando la proforma ya tiene lineas en update
- el recalculo de impuestos y totales reales al editar lineas se documenta en `proforma-create-contract.md`
## Regla de borrado y numeracion

View File

@ -111,17 +111,15 @@ Preparar el esquema fisico para separar:
## Regla operativa actual de series en proformas
- al crear una proforma, `document-series` se invoca con `document_type = proforma` para resolver la numeracion visible de la proforma
- ese resultado solo alimenta `document_series_id`, `proforma_number` y `proforma_reference`
- `target_invoice_series_code` no se rellena automaticamente con la serie documental de proforma
- si el usuario no selecciona serie futura de factura, `target_invoice_series_code` se persiste en `NULL`
- si el usuario informa `target_invoice_series_code`, el backend lo conserva solo si corresponde a una serie activa de `issued_invoice`
- al emitir una proforma, la factura definitiva se numera con `document_type = issued_invoice`
- si la proforma trae `target_invoice_series_code`, se usa como `seriesCode`
- si `target_invoice_series_code` es `NULL`, `document-series` resuelve la serie default activa de `issued_invoice`
- la referencia final devuelta por `document-series` se persiste en `issued_invoices.invoice_reference`
- `invoice_reference` es snapshot documental y no se recalcula despues
- `proforma_series_code` es un input de create para `document_type = proforma` y no vuelve a ser editable despues
La semantica operativa vigente ya no se mantiene en este documento historico.
Fuente canonica actual:
- `docs/customer-invoices/proforma-series-contract.md`
- `docs/customer-invoices/proforma-create-contract.md`
- `docs/customer-invoices/proforma-tax-config.md`
Este documento conserva solo el contexto de migracion, fases y SQL historico.
## Estado Fase 2B
@ -142,30 +140,19 @@ Preparar el esquema fisico para separar:
## Estado Fase 2D
- el contrato publico de proformas ya soporta `target_invoice_series_code` en create y update
- `series` se mantiene como alias legacy temporal en responses
- si `series` y `target_invoice_series_code` llegan a la vez con distinto valor, el request falla por validacion
- las responses de proforma prefieren `target_invoice_series_code`
- `series` se mantiene temporalmente en responses como alias legacy con el mismo valor
- la semantica detallada del alias legacy `series` se consolido despues en `proforma-series-contract.md`
- el cliente interno del modulo ya emite `target_invoice_series_code` en requests nuevos
## Estado Fase 2E
- create separa `proforma_series_code` de `target_invoice_series_code`
- `proforma_series_code` solo se usa para numerar la proforma
- update ya no usa `series` en el flujo nuevo ni permite cambiar la serie propia de proforma
- `target_invoice_series_code` sigue siendo el unico concepto de serie futura de factura emitida
- las reglas operativas exactas se consolidaron despues en `proforma-series-contract.md`
- la UI nueva de proformas ya envia `proforma_series_code` y `target_invoice_series_code`
## Estado Fase 2F
- create de proforma se consolida como creacion de cabecera/documento inicial
- una proforma sin lineas es valida y debe persistirse con `items = []`
- `tax_regime_code` queda como configuracion fiscal inicial de la cabecera
- `tax_config` queda persistido en `proformas` como configuracion fiscal de cabecera
- `proforma_taxes` solo representa impuestos calculados, no defaults fiscales ni placeholders sobre base `0`
- si una proforma nace sin lineas, la response debe devolver `taxes = []` y totales monetarios a `0`
- update sigue siendo el punto donde se anaden lineas y se materializan impuestos/totales reales
- la UI deja de prometer que la fiscalidad queda "aplicada" en create y pasa a hablar de valores iniciales
- la semantica funcional exacta de create se consolido despues en `proforma-create-contract.md`
## Estado Fase 2G
@ -174,9 +161,7 @@ Preparar el esquema fisico para separar:
- el archivado es logico mediante `archived_at`
- el listado V2 de proformas usa `Criteria filters[]` para `archived_at` y `status`
- `GET /proformas/:id` permite abrir proformas archivadas
- el borrado de `draft` no depende de `archived_at`
- solo `draft` puede borrarse
- `rejected` sigue fuera del alcance y queda reservado para archivado futuro
- las reglas funcionales exactas de borrado y numeracion se consolidaron despues en `proforma-series-contract.md`
- la validacion de borrado comprueba tambien `linked_invoice_id` y `issued_invoices.source_proforma_id`
- la numeracion de `document-series` no se reutiliza ni se decrementa tras un borrado

View File

@ -1,83 +1,77 @@
📌 CONTEXTO DEL PROYECTO:
Estoy desarrollando una API en **Node.js** con **TypeScript**, **Express.js**, **Sequelize**, y **Arquitectura Hexagonal (Ports & Adapters)** bajo los principios de **DDD (Domain-Driven Design)** y **SOLID**.
La API maneja **autenticación con PassportJS**, soporte para **JWT y LocalStrategy**, gestión segura de contraseñas con **bcrypt**, y transformación de respuestas con **DTOs (Data Transfer Objects)**.
# Prompt Auth
📌 PRINCIPIOS Y PATRONES A APLICAR:
✅ **DDD (Domain-Driven Design):**
- Los **agregados** encapsulan su estado y exponen solo lo necesario.
- Cada **agregado** tiene su propio **mapper** para conversión entre persistencia y dominio.
- **Repositorios** manejan **agregados** y deben desacoplarse de Sequelize.
Base comun reutilizada: [`backend-prompt-base.md`](./backend-prompt-base.md)
✅ **SOLID (Principios de Diseño):**
- **SRP:** Cada clase tiene una única responsabilidad.
- **OCP:** El código debe ser fácil de extender sin modificarlo.
- **LSP:** Los objetos derivados deben poder sustituir a sus clases base.
- **ISP:** Usar interfaces específicas en lugar de dependencias directas.
- **DIP:** Usar **interfaces** (`IAuthProvider`, `ITransactionManager`) en lugar de acoplarse a implementaciones concretas.
Este prompt añade solo las reglas especificas de autenticacion sobre esa base.
📌 GESTIÓN DE DEPENDENCIAS:
✅ **Factory Pattern (`createAuthService`)**
- `AuthService` debe crearse con un **Factory** (`createAuthService()`), permitiendo cambiar su implementación sin afectar la API.
- `AuthService` depende de `IAuthenticatedUserRepository`, `ITransactionManager` y `IAuthProvider`.
## Contexto especifico
📌 AUTENTICACIÓN CON `PassportJS`:
✅ **Soporte para JWT y LocalStrategy**
- `PassportAuthProvider` maneja **JWTStrategy** para autenticación con tokens y **LocalStrategy** para validación con email y contraseña.
- Se debe usar `bcrypt` para cifrar contraseñas antes de almacenarlas.
- `validateUser()` en `PassportAuthProvider` verifica credenciales con `LocalStrategy`.
La API maneja autenticacion con `PassportJS`, soporte para `JWT` y `LocalStrategy`, gestion segura de contrasenas con `bcrypt` y transformacion de respuestas con DTOs.
📌 GESTIÓN SEGURA DE CONTRASEÑAS:
✅ **Uso de `bcrypt` para cifrado de contraseñas**
- `PasswordHash.create()` valida la fuerza de la contraseña antes de cifrarla.
- `bcrypt.compare()` se usa para validar la contraseña al hacer login.
- Las contraseñas **nunca deben almacenarse en texto plano en la base de datos**.
## Gestion de dependencias
📌 GESTIÓN DE ERRORES:
✅ **Uso de `ApiError` para respuestas de error estructuradas**
- `ExpressController` debe devolver errores con `ApiError` (`status`, `title`, `detail`, `timestamp`).
- `RegisterController` y `LoginController` deben usar `ApiError` en caso de errores (`409 Conflict`, `401 Unauthorized`, `500 Internal Server Error`).
- Se deben capturar errores de **Sequelize** en `BaseRepository` (`UniqueConstraintError`, `ConnectionError`, `TimeoutError`).
- **Errores de unicidad (`SequelizeUniqueConstraintError`) deben ser personalizados según el contexto usando `errorMapper` en los repositorios.**
- `AuthService` debe crearse con un factory como `createAuthService()`
- `AuthService` depende de `IAuthenticatedUserRepository`, `ITransactionManager` y `IAuthProvider`
📌 FLUJO DE LA API `/register`:
1️⃣ **Request llega a `RegisterController`**.
2️⃣ **Valida `email`, `username`, `password` como Value Objects**.
3️⃣ **Llama a `AuthService.registerUser()`**.
4️⃣ **`AuthService` usa `validateUser()` para evitar usuarios duplicados**.
5️⃣ **Si el usuario es nuevo, se cifra la contraseña con `bcrypt.hash()`**.
6️⃣ **Se almacena en la BD con `AuthenticatedUserRepository.create()`**.
7️⃣ **Se generan `accessToken` y `refreshToken` con `PassportAuthProvider`**.
8️⃣ **Se responde con los tokens y el `userId`**.
## Autenticacion con `PassportJS`
📌 FLUJO DE LA API `/login`:
1️⃣ **Request llega a `LoginController`**.
2️⃣ **Llama a `AuthService.loginUser()` con `email` y `password`**.
3️⃣ **`AuthService` busca el usuario en `AuthenticatedUserRepository.findUserByEmail()`**.
4️⃣ **Si el usuario existe, compara contraseñas con `bcrypt.compare()`**.
5️⃣ **Si la contraseña es correcta, genera `accessToken` y `refreshToken`**.
6️⃣ **Devuelve los tokens en la respuesta**.
- `PassportAuthProvider` maneja `JWTStrategy` y `LocalStrategy`
- `validateUser()` verifica credenciales con `LocalStrategy`
- usar `bcrypt` para cifrar contrasenas antes de almacenarlas
- `PasswordHash.create()` valida la fuerza de la contrasena antes de cifrarla
- `bcrypt.compare()` valida la contrasena al hacer login
- las contrasenas nunca deben almacenarse en texto plano
📌 TRANSFORMACIÓN DE RESPUESTAS:
✅ **Los Controladores son responsables de la transformación de datos antes de enviarlos.**
✅ **Se usan `Presenters` (`AuthResponsePresenter`) para mapear datos del dominio a DTOs (`AuthResponseDTO`).**
✅ **Se inyectan `Presenters` en los controladores para desacoplar la lógica de transformación.**
## Gestion de errores
📌 SEGURIDAD Y MEJORES PRÁCTICAS:
✅ **Usar `TransactionManager.complete()` para manejar transacciones.**
✅ **Evitar acoplamientos directos entre servicios y repositorios.**
✅ **Usar `bcrypt` con `SALT_ROUNDS=12` configurables en `.env`.**
✅ **Si ocurre un error en la BD, `rollback()` debe ejecutarse automáticamente.**
✅ **Devolver códigos HTTP adecuados (`401 Unauthorized`, `409 Conflict`, `500 Internal Server Error`).**
- `ExpressController` debe devolver errores con `ApiError`
- `RegisterController` y `LoginController` deben mapear errores a `409`, `401` o `500` segun contrato
- capturar errores de Sequelize relevantes en repositorios
- personalizar errores de unicidad mediante `errorMapper`
📌 SOLICITUDES QUE PUEDO HACERTE:
- **Generar código** cumpliendo con estas reglas.
- **Revisar código y detectar violaciones de SOLID o DDD**.
- **Optimizar `AuthService` para mejorar la seguridad y escalabilidad**.
- **Implementar `/refresh` para renovar `accessTokens`**.
- **Escribir pruebas unitarias y de integración para `registerUser()` y `loginUser()`**.
- **Optimizar `AuthResponsePresenter` para soportar más formatos (`XML`, `CSV`).**
## Flujo de `/register`
⚠️ **IMPORTANTE:**
- **NO generes código que acople dependencias directamente**.
- **NO uses Sequelize directamente en los servicios**.
- **SIEMPRE usa interfaces (`IAuthProvider`, `ITransactionManager`) en lugar de instancias concretas**.
1. Request llega a `RegisterController`
2. valida `email`, `username` y `password` como Value Objects
3. llama a `AuthService.registerUser()`
4. `AuthService` evita usuarios duplicados con `validateUser()`
5. si el usuario es nuevo, cifra la contrasena con `bcrypt.hash()`
6. persiste con `AuthenticatedUserRepository.create()`
7. genera `accessToken` y `refreshToken`
8. responde con tokens y `userId`
## Flujo de `/login`
1. Request llega a `LoginController`
2. llama a `AuthService.loginUser()` con `email` y `password`
3. busca el usuario en `AuthenticatedUserRepository.findUserByEmail()`
4. compara contrasenas con `bcrypt.compare()`
5. si la contrasena es correcta, genera tokens
6. devuelve los tokens en la response
## Transformacion de respuestas
- los controladores transforman antes de responder
- usar `AuthResponsePresenter` para mapear dominio a `AuthResponseDTO`
- inyectar presenters en controllers cuando ayude a desacoplar
## Seguridad y mejores practicas
- usar `TransactionManager.complete()` para manejar transacciones
- evitar acoplamientos directos entre servicios y repositorios
- usar `bcrypt` con `SALT_ROUNDS=12` configurable en `.env`
- si ocurre un error en BD, `rollback()` debe ejecutarse automaticamente
## Solicitudes tipicas
- generar codigo cumpliendo estas reglas
- revisar codigo y detectar violaciones de SOLID o DDD
- optimizar `AuthService` para seguridad y escalabilidad
- implementar `/refresh`
- escribir pruebas unitarias y de integracion para `registerUser()` y `loginUser()`
## Restricciones
- no acoplar dependencias directamente
- no usar Sequelize directamente en servicios
- usar interfaces como `IAuthProvider` e `ITransactionManager`

View File

@ -1,22 +1,23 @@
📌 CONTEXTO DEL PROYECTO:
Estoy desarrollando una API en **Node.js** con **TypeScript**, **Express.js**, **Sequelize**, y **Arquitectura Hexagonal (Ports & Adapters)** bajo los principios de **DDD (Domain-Driven Design)** y **SOLID**.
Este módulo es para **facturas de cliente (Customer Invoices)** y debe cumplir con los estándares definidos previamente para el contexto de autenticación, adaptados a los datos, reglas y operaciones de las facturas.
# Prompt Customer Invoices
📌 PRINCIPIOS Y PATRONES A APLICAR:
✅ **DDD (Domain-Driven Design)**
- Las **facturas** serán agregados raíz (`CustomerInvoice`) con propiedades como `id`, `customerId`, `invoiceNumber`, `date`, `lines`, `totals`, etc.
- Las **líneas de factura** (`InvoiceLine`) se modelan como entidades o value objects dentro del agregado.
- Se usará un `Mapper` para convertir entre dominio y persistencia.
- Repositorios (`ICustomerInvoiceRepository`) solo manejan agregados.
- Operaciones como `createInvoice`, `updateInvoice`, `getInvoiceById` serán gestionadas en `CustomerInvoiceApplicationService`.
Base comun reutilizada: [`backend-prompt-base.md`](./backend-prompt-base.md)
✅ **SOLID**
- Usar SRP: cada clase con una responsabilidad clara.
- Usar interfaces como `ICustomerInvoiceRepository`, `ITransactionManager`, `IInvoicePresenter`.
- Evitar acoplamientos entre capa de aplicación y capa de infraestructura.
- Controladores deben transformar los datos en DTOs antes de enviarlos.
Este prompt añade solo las reglas especificas de `customer-invoices`.
📌 ESTRUCTURA DE DATOS:
## Contexto especifico
Este modulo es para facturas de cliente y adapta la base comun a datos, reglas y operaciones de facturacion.
## DDD y SOLID aplicados
- `CustomerInvoice` es agregado raiz
- `InvoiceLine` se modela como entidad o Value Object dentro del agregado
- usar mapper para dominio <-> persistencia
- `ICustomerInvoiceRepository` maneja agregados
- operaciones como `createInvoice`, `updateInvoice` y `getInvoiceById` viven en `CustomerInvoiceApplicationService`
- usar SRP y contratos como `ICustomerInvoiceRepository`, `ITransactionManager` e `IInvoicePresenter`
## Estructura de datos
La entidad `CustomerInvoice` tendrá:
- `id`: UUID (ValueObject `UniqueID`)
@ -37,8 +38,8 @@ La entidad `CustomerInvoice` tendrá:
- `tax`: Amount (number) and scale (number) (ValueObject Percentage)
- `totalAmount`: Amount (number), scale (number) and currency (string) (ValueObject MoneyValue)
## Operaciones principales
📌 OPERACIONES PRINCIPALES:
- `createInvoice(data: CreateCustomerInvoiceDTO)`
- `getInvoiceById(id: string)`
- `updateInvoice(id: string, changes: UpdateCustomerInvoiceDTO)`
@ -46,39 +47,43 @@ La entidad `CustomerInvoice` tendrá:
- `deleteInvoice(id: string)`
- `markAsPaid(id: string)`
📌 VALIDACIONES:
✅ Usar `Zod` para validaciones en Value Objects (`InvoiceNumber`, `InvoiceDate`, `InvoiceLine`).
✅ No se deben lanzar excepciones para errores de validación. Usar `Result<T, E>` como en autenticación.
## Validaciones
📌 PRESENTACIÓN:
✅ Los controladores convierten el agregado `CustomerInvoice` a `CustomerInvoiceDTO`.
✅ Se usan `Presenters` (`CustomerInvoicePresenter`) para construir los DTOs.
✅ DTOs solo exponen campos necesarios: nada sensible, nada interno.
✅ Se puede extender con `CsvInvoicePresenter`, `PdfInvoicePresenter`, etc.
- usar `Zod` en Value Objects como `InvoiceNumber`, `InvoiceDate` e `InvoiceLine`
- no lanzar excepciones para errores de validacion esperados; usar `Result<T, E>`
📌 AUTORIZACIÓN Y ACCESO:
✅ Se protegerán rutas con middleware JWT (`authenticateJWT`).
✅ Se validará que el usuario autenticado pueda acceder/modificar la factura (p. ej. pertenece a su empresa).
## Presentacion
📌 PERSISTENCIA:
✅ `SequelizeCustomerInvoiceRepository` no accede directamente al modelo de Sequelize, lo hace a través de `Mapper`.
✅ La clase `CustomerInvoiceMapper` mapea de/agregado <-> persistencia.
✅ Soporta transacciones Sequelize (`TransactionManager.complete(...)`).
✅ Los nombres de los campos y las tablas en la base de datos seguirán la notación "snake_case".
✅ Los nombres de los campos en los modelos Sequelize seguirán la notación "snake_case".
✅ Los modelos Sequelize de los agregados tienen campos 'timestamp' con los nombres "created_at", "updated_at" y "deleted_at".
- los controladores convierten el agregado a `CustomerInvoiceDTO`
- usar `CustomerInvoicePresenter` para construir DTOs
- DTOs solo exponen campos necesarios
- se puede extender con `CsvInvoicePresenter`, `PdfInvoicePresenter`, etc.
## Autorizacion y acceso
📌 ERRORES:
✅ Usar `ApiError` para errores en la API.
✅ Los repositorios deben capturar errores de Sequelize (`UniqueConstraintError`, etc.) y convertirlos a errores de dominio con mensajes claros y específicos (mediante `errorMapper`).
- proteger rutas con middleware JWT o equivalente vigente del modulo
- validar que el usuario autenticado pueda acceder o modificar la factura de su empresa
📌 TESTING:
✅ Los servicios (`CustomerInvoiceApplicationService`) serán testeados con mocks de repositorio.
✅ Las rutas serán testeadas con `supertest`.
✅ Las validaciones de ValueObjects tendrán pruebas unitarias.
## Persistencia
- `SequelizeCustomerInvoiceRepository` accede mediante mapper
- `CustomerInvoiceMapper` mapea agregado <-> persistencia
- soportar transacciones con `TransactionManager.complete(...)`
- seguir `snake_case` en tablas, columnas y timestamps `created_at`, `updated_at`, `deleted_at`
## Errores
- usar `ApiError` para errores de API
- capturar errores de Sequelize y convertirlos con mensajes claros mediante `errorMapper`
## Testing
- testear `CustomerInvoiceApplicationService` con mocks de repositorio
- testear rutas con `supertest`
- cubrir Value Objects con pruebas unitarias
## Nomenclatura
📌 NOMENCLATURA:
- Agregado: `CustomerInvoice`
- Línea: `CustomerInvoiceLine`
- DTOs: `CustomerInvoiceDTO`, `InvoiceLineDTO`
@ -86,8 +91,9 @@ La entidad `CustomerInvoice` tendrá:
- Repositorio: `ICustomerInvoiceRepository`, `SequelizeCustomerInvoiceRepository`
- ValueObjects: `CustomerInvoiceNumber`, `CustomerInvoiceDate`, `CustomerInvoiceTotal`, `CustomerInvoiceItemQuantity`, etc.
⚠️ IMPORTANTE:
- NO incluir `passwords`, `tokens`, o datos sensibles en respuestas.
- NO usar Sequelize directamente en servicios ni controladores.
- NO devolver el agregado como respuesta bruta. Usar DTOs.
- TODO debe estar tipado, incluso los métodos de los servicios (`Promise<Result<T, Error>>`).
## Restricciones
- no incluir `passwords`, `tokens` u otros datos sensibles en responses
- no usar Sequelize directamente en servicios ni controllers
- no devolver el agregado bruto; usar DTOs
- tipar explicitamente metodos y resultados