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 # 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`. ## Estructura
- `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.
## 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) Áreas principales del workspace:
- [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)
## 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`. ## Requisitos
- Rutas solo autenticadas: `requireIdentityAuthenticated(params)`.
- Rutas tenant-scoped: `requireIdentityTenant(params)`. - Node.js `>= 24`
- `GET /companies/available` es el endpoint vigente para companies accesibles. - PNPM `10.33.0`
- No documentar ni introducir `GET /identity/companies`.
- No meter `companyId` ni `companySlug` dentro del access token. ## 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. 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 ## Runtime actual de autenticación
La app usa: La app usa `@erp/identity/client` como runtime actual.
```txt
@erp/identity/client
IdentityAuthSessionProvider
DataSourceProvider
Axios compartido
```
No debe usarse `@erp/auth/client` como runtime nuevo.
## Flujo actual ## Flujo actual
```txt - La sesión y la empresa activa se resuelven según el flujo documentado en `docs/frontend/auth-and-company-selection.md`.
login - Las rutas tenant-scoped deben respetar las reglas de `X-Company-Id` documentadas en `docs/architecture/tenant-context.md`.
-> 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
```
## Headers ## Headers
No enviar `X-Company-Id` a: - 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.
```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`.
## App company switcher ## App company switcher
@ -62,13 +41,10 @@ Reglas:
- no llamar directamente a `GET /companies/available` - no llamar directamente a `GET /companies/available`
- no usar `companySlug` para auth - no usar `companySlug` para auth
- no modificar el access token - no modificar el access token
- respetar el flujo canónico documentado en `docs/frontend/auth-and-company-selection.md`
## Refresh automático ## 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: La semántica completa de retry, exclusiones y limpieza de sesión se documenta en `docs/frontend/auth-and-company-selection.md`.
- limpiar sesión
- limpiar empresa activa persistida
- redirigir a `/login`

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:** 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.
`POST /api/auth/login`
**Solicitud:** ## Fuentes actuales
```json - Auth y sesión: [architecture/identity-auth-api.md](./architecture/identity-auth-api.md)
{ - Contexto tenant: [architecture/tenant-context.md](./architecture/tenant-context.md)
"email": "usuario@empresa.com", - Relación `identity` / `companies`: [architecture/identity-and-companies.md](./architecture/identity-and-companies.md)
"password": "contraseña_segura" - 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 Se mantiene solo por trazabilidad de descubrimiento inicial y comparación histórica.
{
"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"
}
]
```

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** ## Reglas base
<Verbo><Entidad><Condición><TipoDeSchema>
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`). ## Patrones recomendados
- **Entidad**: en singular (`Product`, `Customer`, `Invoice`).
- **Condición**: criterio de selección (`ById`, `BySlug`, `ByEmail`).
- **TipoDeSchema**: origen o propósito (`ParamsSchema`, `CommandSchema`, `QuerySchema`).
**Ejemplos ✅** ### Recurso único
- `GetProductByIdParamsSchema`
- `DeleteCustomerByIdParamsSchema`
- `UpdateInvoiceByIdCommandSchema`
**Ejemplos ❌** ```text
- `ProductGetByIdParamsSchema` (orden invertido, menos natural). <Verb><Entity><Condition><SchemaRole>
- `GetProductsByIdParamsSchema` (plural innecesario en recurso único). ```
**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` - `GetCustomerByIdParamsSchema`
- `CustomerListCriteriaSchema` - `DeleteCustomerByIdParamsSchema`
- `SearchCustomerQuerySchema` - `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` - `CustomerSummarySchema`
- `CustomerResourceSchema` - `CustomerResourceSchema`
- `CustomerListResponseSchema` - `CustomerListResponseSchema`
- `CustomerDetailResponseSchema`
- `CustomerCreatedResponseSchema`
- `CustomerDeletedResponseSchema`
--- ### Proformas
## 2. Products - `CreateProformaCommandSchema`
- `UpdateProformaByIdParamsSchema`
- `ProformaListCriteriaSchema`
- `ProformaSummarySchema`
- `ProformaDetailResponseSchema`
### Requests ## Errores a evitar
- `CreateProductCommandSchema`
- `UpdateProductCommandSchema`
- `PatchProductCommandSchema`
- `DeleteProductByIdParamsSchema`
- `GetProductByIdParamsSchema`
- `ProductListCriteriaSchema`
- `SearchProductQuerySchema`
### Responses - `ListCustomerCriteriaSchema`
- `ProductSummarySchema` - `CustomersListResponseSchema`
- `ProductResourceSchema` - `UpdateCustomerCommandByIdSchema`
- `ProductListResponseSchema` - `ProductGetByIdParamsSchema`
- `ProductDetailResponseSchema`
- `ProductCreatedResponseSchema`
- `ProductDeletedResponseSchema`
--- ## Relación con la guía general
## Notas La guía de DTOs define:
- 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`. - organización por carpetas
- **Pluralización**: usar singular para el nombre de la entidad en los Schemas (ej. `Customer`, `Product`), incluso si la ruta es plural (`/customers`, `/products`). - tipado semántico
- **Carpetas sugeridas**: - manejo de `null`, `optional()` y colecciones
modules/ - diseño de `create`, `update`, `list` y `detail`
<entity>/
application/ Este documento solo fija el naming.
dto/
requests/
responses/
domain/
infrastructure/
api/

View File

@ -1,5 +1,9 @@
# Guía de diseño de DTOs para el ERP # 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 ## 1. Principios generales
- Un DTO representa únicamente el contrato de transporte entre cliente y servidor. - 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? 9. ¿El mismo campo mantiene el mismo tipo en todo el módulo?
10. ¿El DTO refleja una vista concreta del endpoint? 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 ## Documentación vigente
- 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
- [Í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: ## Motivo de conservación
- Descripción
- Cantidad -> 2 decimales
- Importe unidad -> 4 decimales
- IVA en los detalles
- Descuento -> 2 decimales
Futuros: Se mantiene solo como rastro de brainstorming previo y no debe emplearse para extraer contratos, payloads ni reglas funcionales actuales.
- 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"
}

View File

@ -1,87 +1,83 @@
# ERP Docs # 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 ## Cómo leer esta documentación
Monorepo con:
- Node.js >= 22
- TypeScript
- PNPM
- Turborepo
- ExpressJS
- MariaDB
- Sequelize v6
- Vitest
Arquitectura: - Empieza por este índice si necesitas orientarte.
- DDD pragmático - Usa los documentos en subdirectorios temáticos como fuente de verdad operativa.
- Clean Architecture - Trata los documentos marcados como históricos o legacy como contexto, no como contrato vigente.
- Separación Domain / Application / Infrastructure
- SOLID, DRY, KISS, YAGNI, SSOT
- Fail Fast
- Defensive Programming
- Cambios incrementales
- Evitar sobreingeniería
## 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) - [Contexto tenant y `X-Company-Id`](./architecture/tenant-context.md)
- [API de autenticación identity](./architecture/identity-auth-api.md) - [API de autenticación de `identity`](./architecture/identity-auth-api.md)
- [Estado de migración a identity](./architecture/identity-migration-status.md) - [Relación entre `identity` y `companies`](./architecture/identity-and-companies.md)
- [Flujo frontend de auth y selección de empresa](./frontend/auth-and-company-selection.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) - [README de `document-series`](./document-series/README.md)
- [Diseño de `document-series`](./document-series/document-series-design.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 `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/identity`](../modules/identity/README.md)
- [README de `modules/companies`](../modules/companies/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) - [Guía de diseño de DTOs](./DTOS - GUÍA DE DISEÑO.md)
- [Split histórico Proforma / IssuedInvoice](./customer-invoices/split-proformas-issued-invoices-migration.md) - [Convención de nombres de schemas DTO](./CONVENCION NOMBRADO DE ESQUEMAS DTO.md)
- [InvoiceSeries legacy](./customer-invoices/invoice-series.md)
- [Drop manual de `customer_invoice_series`](./document-series/sql/drop-customer-invoice-series-legacy.sql) #### SQL operativos y runbooks
- [SQL histórico de `customer_invoice_series`](./dev/customer-invoice-series.sql)
- [Runbooks SQL históricos de `customer-invoices`](./customer-invoices/sql/README.md) - [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) - [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. - [Base común para prompts backend](./backend-prompt-base.md)
- `modules/companies` es la fuente de verdad de la ficha operativa de empresa. - [Prompt de auth](./prompt-auth.md)
- El frontend ERP ya usa `@erp/identity/client` como runtime de autenticación. - [Prompt de customer invoices](./prompt-customer-invoices.md)
- El flujo actual resuelve sesión, companies disponibles, auto-selección de empresa única y refresh automático sobre `401`. - [Plantilla auxiliar](./TEMPLATE.md)
## Reglas de lectura rápida ### Histórico, transicional o de descubrimiento
- `modules/auth` debe considerarse legacy/deprecated para backend nuevo. 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.
- `GET /companies/available` es el endpoint vigente para cargar companies accesibles.
- `GET /identity/companies` no es un endpoint válido del diseño actual. - [Borrador legacy de API](./API.md)
- `GET /account/companies` tampoco es un endpoint válido del diseño actual. - [Requisitos generales históricos](./REQUISITOS GENERALES.md)
- Las rutas tenant-scoped deben usar `Authorization` + `X-Company-Id`. - [Requisitos históricos del módulo de clientes](./REQUISITOS CLIENTES.md)
- `/document-series` es la API canónica de series documentales. - [Notas históricas de facturas de cliente](./FACTURAS DE CLIENTE.md)
- `/catalogs/invoice-series` no es un endpoint activo. - [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** - Documento histórico de análisis funcional.
El módulo de clientes debe permitir: - Aporta vocabulario de negocio y posibles campos de cliente.
- **Crear, leer, actualizar y eliminar clientes** (CRUD). - No define endpoints, DTOs ni persistencia actuales del módulo.
- **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**.
--- ## Qué puede seguir siendo útil
## **2. Estructura del Cliente y sus Campos** - tipologías de datos de cliente
Cada cliente tiene los siguientes atributos: - necesidades de facturación, fiscalidad y contacto
- consideraciones de auditoría y permisos
### **Datos Generales** ## Qué no debe asumirse como vigente
| 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). |
### **Configuración de Moneda e Idioma** - rutas `/api/...`
| Campo | Tipo de Dato | Descripción | - tipos SQL y tablas propuestas
|---------------------|-----------------|-------------| - naming técnico de campos y relaciones
| `currency_code` | `CHAR(3)` | Código de la divisa con la que trabaja el cliente (ISO 4217). | - contratos HTTP o middleware de acceso
| `language_code` | `CHAR(5)` | Idioma preferido del cliente para documentos y comunicación (ej. `es-ES`, `en-US`). |
### **Información Fiscal y Financiera** ## Referencias actuales
| 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. |
### **Descuentos y Tarifas Especiales** - Arquitectura del repositorio: [README.md](../README.md)
| Campo | Tipo de Dato | Descripción | - Í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)
| `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. |
### **Clasificación y Estado** ## Motivo de conservación
| 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`). |
### **Delegados y Comerciales** Se conserva como material de descubrimiento funcional previo al diseño actual.
| 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.
---

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"). - Documento histórico de descubrimiento.
- Los perfiles controlan permisos generales a nivel de empresa y refinados a nivel de sucursal. - Útil para entender intención de producto y necesidades de negocio amplias.
- Un usuario puede tener diferentes perfiles en distintas sucursales de la misma empresa. - 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.). - multiempresa y multisucursal
- Granularidad: permitir acciones específicas como "leer", "escribir", "eliminar". - auditoría y trazabilidad
- 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. - seguridad y permisos
- Si el usuario solo tiene permisos para una sucursal, se selecciona automáticamente. Si tiene acceso a varias, se selecciona solo cuando sea necesario. - soporte multiidioma
- extensibilidad modular
- **Superadministrador**: ## Documentación vigente relacionada
- Puede crear, modificar y eliminar usuarios.
- Gestiona perfiles, permisos y asociaciones con empresas y sucursales.
--- - 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**: 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.
- 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)
);
```
---

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)** - material auxiliar
- no describe comportamiento del ERP
1. Instala dependencias de desarrollo si no las tienes: - no debe enlazarse como documentación funcional o arquitectónica del proyecto
* `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.
## 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 # 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) - [Identity and companies](./identity-and-companies.md)
- [Tenant context](./tenant-context.md) - [Tenant context](./tenant-context.md)
- [Identity auth API](./identity-auth-api.md) - [Identity auth API](./identity-auth-api.md)
- [Identity backend architecture](./identity-backend-architecture.md)
- [Identity migration status](./identity-migration-status.md) - [Identity migration status](./identity-migration-status.md)
- [Identity roadmap](./identity-roadmap.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 ## Resumen actual
- `identity` se encarga de autenticación, sesión, refresh y memberships. - `identity` gestiona autenticación, sesión, refresh y memberships.
- `companies` se encarga de la ficha operativa de empresa. - `companies` gestiona la ficha operativa de empresa.
- `GET /companies/available` es la vía actual para listar companies accesibles. - `GET /companies/available` es la vía vigente para companies accesibles.
- El contexto tenant se resuelve con `X-Company-Id` y `requireIdentityTenant(params)`. - 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 # 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` ## Objetivo del módulo `identity`
`identity` sustituye progresivamente al módulo legacy `auth`. `identity` sustituye progresivamente al módulo legacy `auth`.

View File

@ -1,22 +1,10 @@
# Identity Migration Status # Identity Migration Status
## Patrón final de middleware ## Referencias canónicas
Rutas tenant-scoped: - 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)
```ts - Runtime y responsabilidades del módulo `identity`: [../../modules/identity/README.md](../../modules/identity/README.md)
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));
```
## Migrado a `identity` ## Migrado a `identity`
@ -34,17 +22,11 @@ Estado:
- sin `@erp/auth/api` - sin `@erp/auth/api`
- sin `mockUser` - sin `mockUser`
- sin helpers locales de auth - sin helpers locales de auth
- usan `requireIdentityTenant(params)` - usan el middleware tenant canónico documentado en `tenant-context.md`
### Backend autenticado sin tenant ### Backend autenticado sin tenant
`modules/companies` usa: `modules/companies` usa el middleware autenticado sin tenant documentado en `tenant-context.md`.
```ts
import { requireIdentityAuthenticated } from "@erp/identity/api";
router.use(...requireIdentityAuthenticated(params));
```
Esto aplica a: Esto aplica a:
@ -59,25 +41,9 @@ POST /companies/:company_id/disable
## Frontend actual ## Frontend actual
El runtime nuevo ya usa: El runtime nuevo ya usa `@erp/identity/client`.
- `@erp/identity/client` Los detalles del flujo implementado, headers y refresh automático se mantienen en `frontend/auth-and-company-selection.md`.
- `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
```
## Legacy pendiente ## 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 - se pide cliente
- `proforma_series_code` es opcional; si falta, se usa la serie default activa de `document_type = proforma` - `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` - `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 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 ### Editar draft
@ -63,17 +60,14 @@ En proformas:
- el backend emite `target_invoice_series_code` como nombre preferente - el backend emite `target_invoice_series_code` como nombre preferente
- `series` puede mantenerse solo como alias legacy/deprecated de salida mientras existan consumidores antiguos - `series` puede mantenerse solo como alias legacy/deprecated de salida mientras existan consumidores antiguos
- `taxes` representa impuestos calculados a partir de lineas valoradas - la semantica de `taxes`, `tax_config` y `totals` se documenta en `proforma-create-contract.md`
- `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`
## Regla de emision ## 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` 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` - 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` - 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 ## Regla de borrado y numeracion

View File

@ -111,17 +111,15 @@ Preparar el esquema fisico para separar:
## Regla operativa actual de series en proformas ## 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 La semantica operativa vigente ya no se mantiene en este documento historico.
- 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 Fuente canonica actual:
- 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` - `docs/customer-invoices/proforma-series-contract.md`
- al emitir una proforma, la factura definitiva se numera con `document_type = issued_invoice` - `docs/customer-invoices/proforma-create-contract.md`
- si la proforma trae `target_invoice_series_code`, se usa como `seriesCode` - `docs/customer-invoices/proforma-tax-config.md`
- 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` Este documento conserva solo el contexto de migracion, fases y SQL historico.
- `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
## Estado Fase 2B ## Estado Fase 2B
@ -142,30 +140,19 @@ Preparar el esquema fisico para separar:
## Estado Fase 2D ## Estado Fase 2D
- el contrato publico de proformas ya soporta `target_invoice_series_code` en create y update - el contrato publico de proformas ya soporta `target_invoice_series_code` en create y update
- `series` se mantiene como alias legacy temporal en responses - la semantica detallada del alias legacy `series` se consolido despues en `proforma-series-contract.md`
- 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
- el cliente interno del modulo ya emite `target_invoice_series_code` en requests nuevos - el cliente interno del modulo ya emite `target_invoice_series_code` en requests nuevos
## Estado Fase 2E ## Estado Fase 2E
- create separa `proforma_series_code` de `target_invoice_series_code` - create separa `proforma_series_code` de `target_invoice_series_code`
- `proforma_series_code` solo se usa para numerar la proforma - las reglas operativas exactas se consolidaron despues en `proforma-series-contract.md`
- 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
- la UI nueva de proformas ya envia `proforma_series_code` y `target_invoice_series_code` - la UI nueva de proformas ya envia `proforma_series_code` y `target_invoice_series_code`
## Estado Fase 2F ## Estado Fase 2F
- create de proforma se consolida como creacion de cabecera/documento inicial - create de proforma se consolida como creacion de cabecera/documento inicial
- una proforma sin lineas es valida y debe persistirse con `items = []` - la semantica funcional exacta de create se consolido despues en `proforma-create-contract.md`
- `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
## Estado Fase 2G ## Estado Fase 2G
@ -174,9 +161,7 @@ Preparar el esquema fisico para separar:
- el archivado es logico mediante `archived_at` - el archivado es logico mediante `archived_at`
- el listado V2 de proformas usa `Criteria filters[]` para `archived_at` y `status` - el listado V2 de proformas usa `Criteria filters[]` para `archived_at` y `status`
- `GET /proformas/:id` permite abrir proformas archivadas - `GET /proformas/:id` permite abrir proformas archivadas
- el borrado de `draft` no depende de `archived_at` - las reglas funcionales exactas de borrado y numeracion se consolidaron despues en `proforma-series-contract.md`
- solo `draft` puede borrarse
- `rejected` sigue fuera del alcance y queda reservado para archivado futuro
- la validacion de borrado comprueba tambien `linked_invoice_id` y `issued_invoices.source_proforma_id` - 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 - la numeracion de `document-series` no se reutiliza ni se decrementa tras un borrado

View File

@ -1,83 +1,77 @@
📌 CONTEXTO DEL PROYECTO: # Prompt Auth
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)**.
📌 PRINCIPIOS Y PATRONES A APLICAR: Base comun reutilizada: [`backend-prompt-base.md`](./backend-prompt-base.md)
✅ **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.
✅ **SOLID (Principios de Diseño):** Este prompt añade solo las reglas especificas de autenticacion sobre esa base.
- **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.
📌 GESTIÓN DE DEPENDENCIAS: ## Contexto especifico
✅ **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`.
📌 AUTENTICACIÓN CON `PassportJS`: La API maneja autenticacion con `PassportJS`, soporte para `JWT` y `LocalStrategy`, gestion segura de contrasenas con `bcrypt` y transformacion de respuestas con DTOs.
✅ **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`.
📌 GESTIÓN SEGURA DE CONTRASEÑAS: ## Gestion de dependencias
✅ **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**.
📌 GESTIÓN DE ERRORES: - `AuthService` debe crearse con un factory como `createAuthService()`
✅ **Uso de `ApiError` para respuestas de error estructuradas** - `AuthService` depende de `IAuthenticatedUserRepository`, `ITransactionManager` y `IAuthProvider`
- `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.**
📌 FLUJO DE LA API `/register`: ## Autenticacion con `PassportJS`
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`**.
📌 FLUJO DE LA API `/login`: - `PassportAuthProvider` maneja `JWTStrategy` y `LocalStrategy`
1️⃣ **Request llega a `LoginController`**. - `validateUser()` verifica credenciales con `LocalStrategy`
2️⃣ **Llama a `AuthService.loginUser()` con `email` y `password`**. - usar `bcrypt` para cifrar contrasenas antes de almacenarlas
3️⃣ **`AuthService` busca el usuario en `AuthenticatedUserRepository.findUserByEmail()`**. - `PasswordHash.create()` valida la fuerza de la contrasena antes de cifrarla
4️⃣ **Si el usuario existe, compara contraseñas con `bcrypt.compare()`**. - `bcrypt.compare()` valida la contrasena al hacer login
5️⃣ **Si la contraseña es correcta, genera `accessToken` y `refreshToken`**. - las contrasenas nunca deben almacenarse en texto plano
6️⃣ **Devuelve los tokens en la respuesta**.
📌 TRANSFORMACIÓN DE RESPUESTAS: ## Gestion de errores
✅ **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.**
📌 SEGURIDAD Y MEJORES PRÁCTICAS: - `ExpressController` debe devolver errores con `ApiError`
✅ **Usar `TransactionManager.complete()` para manejar transacciones.** - `RegisterController` y `LoginController` deben mapear errores a `409`, `401` o `500` segun contrato
✅ **Evitar acoplamientos directos entre servicios y repositorios.** - capturar errores de Sequelize relevantes en repositorios
✅ **Usar `bcrypt` con `SALT_ROUNDS=12` configurables en `.env`.** - personalizar errores de unicidad mediante `errorMapper`
✅ **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`).**
📌 SOLICITUDES QUE PUEDO HACERTE: ## Flujo de `/register`
- **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`).**
⚠️ **IMPORTANTE:** 1. Request llega a `RegisterController`
- **NO generes código que acople dependencias directamente**. 2. valida `email`, `username` y `password` como Value Objects
- **NO uses Sequelize directamente en los servicios**. 3. llama a `AuthService.registerUser()`
- **SIEMPRE usa interfaces (`IAuthProvider`, `ITransactionManager`) en lugar de instancias concretas**. 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: # Prompt Customer Invoices
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.
📌 PRINCIPIOS Y PATRONES A APLICAR: Base comun reutilizada: [`backend-prompt-base.md`](./backend-prompt-base.md)
✅ **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`.
✅ **SOLID** Este prompt añade solo las reglas especificas de `customer-invoices`.
- 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.
📌 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á: La entidad `CustomerInvoice` tendrá:
- `id`: UUID (ValueObject `UniqueID`) - `id`: UUID (ValueObject `UniqueID`)
@ -37,8 +38,8 @@ La entidad `CustomerInvoice` tendrá:
- `tax`: Amount (number) and scale (number) (ValueObject Percentage) - `tax`: Amount (number) and scale (number) (ValueObject Percentage)
- `totalAmount`: Amount (number), scale (number) and currency (string) (ValueObject MoneyValue) - `totalAmount`: Amount (number), scale (number) and currency (string) (ValueObject MoneyValue)
## Operaciones principales
📌 OPERACIONES PRINCIPALES:
- `createInvoice(data: CreateCustomerInvoiceDTO)` - `createInvoice(data: CreateCustomerInvoiceDTO)`
- `getInvoiceById(id: string)` - `getInvoiceById(id: string)`
- `updateInvoice(id: string, changes: UpdateCustomerInvoiceDTO)` - `updateInvoice(id: string, changes: UpdateCustomerInvoiceDTO)`
@ -46,39 +47,43 @@ La entidad `CustomerInvoice` tendrá:
- `deleteInvoice(id: string)` - `deleteInvoice(id: string)`
- `markAsPaid(id: string)` - `markAsPaid(id: string)`
📌 VALIDACIONES: ## 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.
📌 PRESENTACIÓN: - usar `Zod` en Value Objects como `InvoiceNumber`, `InvoiceDate` e `InvoiceLine`
✅ Los controladores convierten el agregado `CustomerInvoice` a `CustomerInvoiceDTO`. - no lanzar excepciones para errores de validacion esperados; usar `Result<T, E>`
✅ Se usan `Presenters` (`CustomerInvoicePresenter`) para construir los DTOs.
✅ DTOs solo exponen campos necesarios: nada sensible, nada interno.
✅ Se puede extender con `CsvInvoicePresenter`, `PdfInvoicePresenter`, etc.
📌 AUTORIZACIÓN Y ACCESO: ## Presentacion
✅ 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).
📌 PERSISTENCIA: - los controladores convierten el agregado a `CustomerInvoiceDTO`
✅ `SequelizeCustomerInvoiceRepository` no accede directamente al modelo de Sequelize, lo hace a través de `Mapper`. - usar `CustomerInvoicePresenter` para construir DTOs
✅ La clase `CustomerInvoiceMapper` mapea de/agregado <-> persistencia. - DTOs solo exponen campos necesarios
✅ Soporta transacciones Sequelize (`TransactionManager.complete(...)`). - se puede extender con `CsvInvoicePresenter`, `PdfInvoicePresenter`, etc.
✅ 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".
## Autorizacion y acceso
📌 ERRORES: - proteger rutas con middleware JWT o equivalente vigente del modulo
✅ Usar `ApiError` para errores en la API. - validar que el usuario autenticado pueda acceder o modificar la factura de su empresa
✅ Los repositorios deben capturar errores de Sequelize (`UniqueConstraintError`, etc.) y convertirlos a errores de dominio con mensajes claros y específicos (mediante `errorMapper`).
📌 TESTING: ## Persistencia
✅ Los servicios (`CustomerInvoiceApplicationService`) serán testeados con mocks de repositorio.
✅ Las rutas serán testeadas con `supertest`. - `SequelizeCustomerInvoiceRepository` accede mediante mapper
✅ Las validaciones de ValueObjects tendrán pruebas unitarias. - `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` - Agregado: `CustomerInvoice`
- Línea: `CustomerInvoiceLine` - Línea: `CustomerInvoiceLine`
- DTOs: `CustomerInvoiceDTO`, `InvoiceLineDTO` - DTOs: `CustomerInvoiceDTO`, `InvoiceLineDTO`
@ -86,8 +91,9 @@ La entidad `CustomerInvoice` tendrá:
- Repositorio: `ICustomerInvoiceRepository`, `SequelizeCustomerInvoiceRepository` - Repositorio: `ICustomerInvoiceRepository`, `SequelizeCustomerInvoiceRepository`
- ValueObjects: `CustomerInvoiceNumber`, `CustomerInvoiceDate`, `CustomerInvoiceTotal`, `CustomerInvoiceItemQuantity`, etc. - ValueObjects: `CustomerInvoiceNumber`, `CustomerInvoiceDate`, `CustomerInvoiceTotal`, `CustomerInvoiceItemQuantity`, etc.
⚠️ IMPORTANTE: ## Restricciones
- NO incluir `passwords`, `tokens`, o datos sensibles en respuestas.
- NO usar Sequelize directamente en servicios ni controladores. - no incluir `passwords`, `tokens` u otros datos sensibles en responses
- NO devolver el agregado como respuesta bruta. Usar DTOs. - no usar Sequelize directamente en servicios ni controllers
- TODO debe estar tipado, incluso los métodos de los servicios (`Promise<Result<T, Error>>`). - no devolver el agregado bruto; usar DTOs
- tipar explicitamente metodos y resultados