diff --git a/AGENTS.md b/AGENTS.md index e69de29b..9ae4af2d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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` 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>; +} +``` + +- 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` 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`. +- 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 typecheck +pnpm --filter lint +pnpm --filter 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. diff --git a/README.md b/README.md index f4f7a335..ee6ee76d 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,60 @@ # ERP Monorepo -Monorepo con aplicaciones, servidor y paquetes modulares para el ERP. +Monorepo del ERP con aplicaciones, módulos de dominio y paquetes compartidos. -## Estado actual +El `README` raíz actúa como puerta de entrada del repositorio. La documentación funcional, operativa y arquitectónica vive en [`docs/README.md`](./docs/README.md). -- El runtime nuevo de autenticación y sesión vive en `modules/identity`. -- `modules/companies` es la fuente de verdad de la ficha operativa de empresa. -- `modules/auth` debe considerarse legacy/deprecated para backend nuevo. -- El frontend ERP ya usa `@erp/identity/client` como runtime actual. +## Estructura -## Documentación clave +```text +apps/ aplicaciones ejecutables +modules/ módulos de negocio y backend +packages/ librerías compartidas +docs/ documentación operativa y arquitectónica +scripts/ utilidades de desarrollo +tools/ herramientas auxiliares del workspace +``` -- [Docs index](./docs/README.md) -- [Identity and companies](./docs/architecture/identity-and-companies.md) -- [Tenant context y `X-Company-Id`](./docs/architecture/tenant-context.md) -- [Identity auth API](./docs/architecture/identity-auth-api.md) -- [Frontend auth and company selection](./docs/frontend/auth-and-company-selection.md) -- [Seed local admin](./docs/dev/seed-local-admin.sql) +Áreas principales del workspace: -## Reglas rápidas +- `apps/web`: frontend principal del ERP. +- `apps/server`: runtime servidor del monorepo. +- `modules/*`: módulos como `identity`, `companies`, `customer-invoices` o `document-series`. +- `packages/*`: utilidades compartidas como `rdx-ddd`, `rdx-utils`, `rdx-ui` e `i18n`. -- Backend nuevo: usar `@erp/identity/api`. -- Rutas solo autenticadas: `requireIdentityAuthenticated(params)`. -- Rutas tenant-scoped: `requireIdentityTenant(params)`. -- `GET /companies/available` es el endpoint vigente para companies accesibles. -- No documentar ni introducir `GET /identity/companies`. -- No meter `companyId` ni `companySlug` dentro del access token. +## Requisitos + +- Node.js `>= 24` +- PNPM `10.33.0` + +## Primeros pasos + +```bash +pnpm install +pnpm dev +``` + +Comandos frecuentes: + +```bash +pnpm dev:server +pnpm dev:client +pnpm lint +pnpm check +pnpm typecheck +pnpm build +``` + +## Documentación + +- Índice general: [`docs/README.md`](./docs/README.md) +- Frontend: [`apps/web/README.md`](./apps/web/README.md) +- Identity: [`modules/identity/README.md`](./modules/identity/README.md) +- Companies: [`modules/companies/README.md`](./modules/companies/README.md) +- Auth legacy: [`modules/auth/README.md`](./modules/auth/README.md) + +## Forma de uso + +- Usa este `README` para orientarte en el repositorio y arrancar el entorno. +- Usa `docs/` para contratos, decisiones de arquitectura, migraciones y runbooks. +- Usa los `README` de cada app o módulo para detalles locales de implementación. diff --git a/apps/web/README.md b/apps/web/README.md index 5be50921..b5ff82e5 100644 --- a/apps/web/README.md +++ b/apps/web/README.md @@ -2,45 +2,24 @@ Aplicación frontend principal del ERP. +## Documentación canónica + +- Flujo de autenticación y selección de empresa: [`../../docs/frontend/auth-and-company-selection.md`](../../docs/frontend/auth-and-company-selection.md) +- Contexto tenant y reglas de `X-Company-Id`: [`../../docs/architecture/tenant-context.md`](../../docs/architecture/tenant-context.md) + ## Runtime actual de autenticación -La app usa: - -```txt -@erp/identity/client -IdentityAuthSessionProvider -DataSourceProvider -Axios compartido -``` - -No debe usarse `@erp/auth/client` como runtime nuevo. +La app usa `@erp/identity/client` como runtime actual. ## Flujo actual -```txt -login --> GET /identity/auth/session --> GET /companies/available --> resolver empresa activa --> auto-selección si hay una sola empresa --> selector si hay varias --> /no-companies si no hay empresas --> X-Company-Id en rutas tenant-scoped -``` +- La sesión y la empresa activa se resuelven según el flujo documentado en `docs/frontend/auth-and-company-selection.md`. +- Las rutas tenant-scoped deben respetar las reglas de `X-Company-Id` documentadas en `docs/architecture/tenant-context.md`. ## Headers -No enviar `X-Company-Id` a: - -```txt -POST /identity/auth/login -POST /identity/auth/refresh -POST /identity/auth/logout -GET /identity/auth/session -GET /companies/available -``` - -Sí enviarlo a rutas tenant-scoped cuando exista `activeCompanyId`. +- No duplicar aquí la matriz de headers. +- Consultar `docs/frontend/auth-and-company-selection.md` para endpoints excluidos y `tenant-context.md` para reglas tenant-scoped. ## App company switcher @@ -62,13 +41,10 @@ Reglas: - no llamar directamente a `GET /companies/available` - no usar `companySlug` para auth - no modificar el access token +- respetar el flujo canónico documentado en `docs/frontend/auth-and-company-selection.md` ## Refresh automático -La app implementa refresh automático global sobre `401` con retry único por request y `refreshPromise` compartida. +La app implementa refresh automático global sobre `401`. -Si el refresh falla: - -- limpiar sesión -- limpiar empresa activa persistida -- redirigir a `/login` +La semántica completa de retry, exclusiones y limpieza de sesión se documenta en `docs/frontend/auth-and-company-selection.md`. diff --git a/docs/API.md b/docs/API.md index 946a1b91..9da03e1f 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1,422 +1,19 @@ -## **Diseño de API RESTful** +# API (Legacy Draft / Obsoleto) -### **1. Autenticación y Gestión de Sesiones** +Este documento conserva un borrador antiguo de API REST y ya no describe el runtime vigente del ERP. -#### **1.1. Inicio de sesión** +## No usar como fuente de verdad -**Endpoint:** -`POST /api/auth/login` +Los endpoints, payloads y rutas aquí recogidos pertenecen a una fase anterior del proyecto y entran en conflicto con la arquitectura actual basada en `modules/identity`, `modules/companies` y los contratos vigentes de cada módulo. -**Solicitud:** +## Fuentes actuales -```json -{ - "email": "usuario@empresa.com", - "password": "contraseña_segura" -} -``` +- Auth y sesión: [architecture/identity-auth-api.md](./architecture/identity-auth-api.md) +- Contexto tenant: [architecture/tenant-context.md](./architecture/tenant-context.md) +- Relación `identity` / `companies`: [architecture/identity-and-companies.md](./architecture/identity-and-companies.md) +- Frontend auth: [frontend/auth-and-company-selection.md](./frontend/auth-and-company-selection.md) +- Customer invoices y proformas: [customer-invoices/README.md](./customer-invoices/README.md) -**Respuesta:** +## Motivo de conservación -```json -{ - "token": "eyJhbGciOi...", - "user": { - "id": 123, - "username": "usuario", - "email": "usuario@empresa.com" - }, - "companies": [ - { "id": 1, "name": "Empresa A" }, - { "id": 2, "name": "Empresa B" } - ] -} -``` - ---- - -#### **1.2. Cierre de sesión** - -**Endpoint:** -`POST /api/auth/logout` - -**Encabezados:** - -``` -Authorization: Bearer -``` - -**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 -``` - -**Respuesta:** - -```json -[ - { - "id": 1, - "name": "Empresa A", - "country_code": "ES", - "currency_code": "EUR" - }, - { "id": 2, "name": "Empresa B", "country_code": "US", "currency_code": "USD" } -] -``` - ---- - -#### **2.2. Crear una nueva empresa** - -**Endpoint:** -`POST /api/companies` - -**Solicitud:** - -```json -{ - "name": "Nueva Empresa", - "country_code": "ES", - "currency_code": "EUR" -} -``` - -**Respuesta:** - -```json -{ - "id": 3, - "message": "Empresa creada exitosamente" -} -``` - ---- - -#### **2.3. Actualizar datos de una empresa** - -**Endpoint:** -`PUT /api/companies/{company_id}` - -**Solicitud:** - -```json -{ - "name": "Empresa Actualizada", - "currency_code": "USD" -} -``` - -**Respuesta:** - -```json -{ - "message": "Empresa actualizada correctamente" -} -``` - ---- - -#### **2.4. Eliminar una empresa** - -**Endpoint:** -`DELETE /api/companies/{company_id}` - -**Respuesta:** - -```json -{ - "message": "Empresa eliminada exitosamente" -} -``` - ---- - -### **3. Gestión de Sucursales** - -#### **3.1. Listar sucursales de una empresa** - -**Endpoint:** -`GET /api/companies/{company_id}/branches` - -**Respuesta:** - -```json -[ - { "id": 10, "name": "Sucursal Madrid" }, - { "id": 20, "name": "Sucursal Barcelona" } -] -``` - ---- - -#### **3.2. Crear una sucursal** - -**Endpoint:** -`POST /api/companies/{company_id}/branches` - -**Solicitud:** - -```json -{ - "name": "Sucursal Sevilla", - "location": "Avenida Principal 123" -} -``` - -**Respuesta:** - -```json -{ - "id": 30, - "message": "Sucursal creada exitosamente" -} -``` - ---- - -#### **3.3. Actualizar datos de una sucursal** - -**Endpoint:** -`PUT /api/branches/{branch_id}` - -**Solicitud:** - -```json -{ - "name": "Sucursal Actualizada", - "location": "Calle Nueva 456" -} -``` - -**Respuesta:** - -```json -{ - "message": "Sucursal actualizada correctamente" -} -``` - ---- - -#### **3.4. Eliminar una sucursal** - -**Endpoint:** -`DELETE /api/branches/{branch_id}` - -**Respuesta:** - -```json -{ - "message": "Sucursal eliminada exitosamente" -} -``` - ---- - -### **4. Gestión de Usuarios** - -#### **4.1. Listar usuarios de una empresa** - -**Endpoint:** -`GET /api/companies/{company_id}/users` - -**Respuesta:** - -```json -[ - { - "id": 1, - "username": "admin", - "email": "admin@empresa.com", - "roles": ["Administrador"] - }, - { - "id": 2, - "username": "usuario", - "email": "usuario@empresa.com", - "roles": ["Ventas"] - } -] -``` - ---- - -#### **4.2. Crear un usuario** - -**Endpoint:** -`POST /api/users` - -**Solicitud:** - -```json -{ - "username": "nuevo_usuario", - "email": "nuevo@empresa.com", - "password": "clave_segura", - "company_id": 1, - "roles": ["Ventas"] -} -``` - -**Respuesta:** - -```json -{ - "id": 3, - "message": "Usuario creado exitosamente" -} -``` - ---- - -#### **4.3. Actualizar usuario** - -**Endpoint:** -`PUT /api/users/{user_id}` - -**Solicitud:** - -```json -{ - "email": "nuevo_correo@empresa.com", - "roles": ["Compras"] -} -``` - -**Respuesta:** - -```json -{ - "message": "Usuario actualizado correctamente" -} -``` - ---- - -#### **4.4. Eliminar usuario** - -**Endpoint:** -`DELETE /api/users/{user_id}` - -**Respuesta:** - -```json -{ - "message": "Usuario eliminado exitosamente" -} -``` - ---- - -### **5. Gestión de Permisos** - -#### **5.1. Obtener permisos de un usuario en una empresa** - -**Endpoint:** -`GET /api/companies/{company_id}/users/{user_id}/permissions` - -**Respuesta:** - -```json -{ - "modules": { - "Presupuestos": ["read", "write"], - "Facturas": ["read"], - "Clientes": ["read", "write", "delete"] - } -} -``` - ---- - -#### **5.2. Actualizar permisos de un usuario** - -**Endpoint:** -`PUT /api/users/{user_id}/permissions` - -**Solicitud:** - -```json -{ - "modules": { - "Presupuestos": ["read", "write"], - "Inventario": ["read"] - } -} -``` - -**Respuesta:** - -```json -{ - "message": "Permisos actualizados correctamente" -} -``` - ---- - -### **6. Auditoría** - -#### **6.1. Obtener historial de cambios en documentos** - -**Endpoint:** -`GET /api/audit/documents/{document_id}` - -**Respuesta:** - -```json -[ - { - "timestamp": "2025-01-22T14:00:00Z", - "user": "admin", - "change": "Se modificó el precio de 100 a 120" - }, - { - "timestamp": "2025-01-20T10:30:00Z", - "user": "usuario", - "change": "Se creó el documento" - } -] -``` +Se mantiene solo por trazabilidad de descubrimiento inicial y comparación histórica. diff --git a/docs/CONVENCION NOMBRADO DE ESQUEMAS DTO.md b/docs/CONVENCION NOMBRADO DE ESQUEMAS DTO.md index 4fbdd200..0de89cb0 100644 --- a/docs/CONVENCION NOMBRADO DE ESQUEMAS DTO.md +++ b/docs/CONVENCION NOMBRADO DE ESQUEMAS DTO.md @@ -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 +1. Usar singular para el nombre base de la entidad: `Customer`, `Product`, `Proforma`. +2. En operaciones sobre recurso único, el verbo va primero. +3. En listados, el sustantivo principal va primero: `CustomerList`, nunca `ListCustomer`. +4. Mantener sufijos estables según el rol del schema. +5. No mezclar convenciones distintas para la misma familia de endpoints. -- **Verbo**: describe la acción (`Get`, `Delete`, `Update`, `Patch`, `Create`). -- **Entidad**: en singular (`Product`, `Customer`, `Invoice`). -- **Condición**: criterio de selección (`ById`, `BySlug`, `ByEmail`). -- **TipoDeSchema**: origen o propósito (`ParamsSchema`, `CommandSchema`, `QuerySchema`). +## Patrones recomendados -**Ejemplos ✅** -- `GetProductByIdParamsSchema` -- `DeleteCustomerByIdParamsSchema` -- `UpdateInvoiceByIdCommandSchema` +### Recurso único -**Ejemplos ❌** -- `ProductGetByIdParamsSchema` (orden invertido, menos natural). -- `GetProductsByIdParamsSchema` (plural innecesario en recurso único). +```text + +``` -**Lectura natural**: *"Esquema de parámetros para obtener un producto por id"*. +Ejemplos: ---- - -## 2️⃣ Operaciones sobre colecciones - -**Patrón** -List - -- **Entidad**: en singular (`Product`, `Customer`). -- **List**: indica conjunto/listado. -- **Criterio/Filtro**: tipo de filtrado (`Criteria`, `Query`). -- **TipoDeSchema**: `Schema` o `ResponseSchema` según corresponda. - -**Ejemplos ✅** -- `ProductListCriteriaSchema` -- `CustomerListResponseSchema` -- `InvoiceListQuerySchema` - -**Ejemplos ❌** -- `ListProductCriteriaSchema` (el orden “ListProduct” suena a verbo y no a colección). -- `ProductsListCriteriaSchema` (plural innecesario en el nombre base). - -**Lectura natural**: *"Esquema de criterios para una lista de productos"*. - ---- - -## 3️⃣ Reglas generales - -1. **Singular siempre** para el nombre base de la entidad. -2. El **verbo solo** en operaciones sobre un recurso único o mutaciones. -3. En listados, el **sustantivo principal primero** (`ProductList`, `CustomerList`), nunca `ListProduct`. -4. Sufijos estándar: - - `CommandSchema` → body de mutaciones (create/update/patch). - - `ParamsSchema` → route params (`/:id`). - - `QuerySchema` → query string no normalizada. - - `CriteriaSchema` → query normalizada con patrón Criteria. - - `ResponseSchema` → respuesta de la API. -5. Mantener **consistencia**: si usas `ProductListCriteriaSchema`, no alternes con `ProductsCriteriaSchema`. - ---- - -## 4️⃣ Ejemplos positivos y negativos - -| ✅ Correcto | ❌ Incorrecto | Motivo del error | -|------------------------------------------|---------------------------------------------|----------------------------------------------| -| `GetCustomerByIdParamsSchema` | `CustomerGetByIdParamsSchema` | Orden invertido. | -| `ProductListCriteriaSchema` | `ListProductCriteriaSchema` | Orden “ListProduct” no natural en inglés. | -| `CustomerListResponseSchema` | `CustomersListResponseSchema` | Plural innecesario. | -| `DeleteInvoiceByIdParamsSchema` | `DeleteInvoicesByIdParamsSchema` | Plural innecesario en recurso único. | -| `UpdateProductByIdCommandSchema` | `UpdateProductCommandByIdSchema` | `ById` debe ir antes del tipo de esquema. | - ---- - - -# Convenciones de nombres para Schemas (Requests y Responses) - -## Tabla de rutas ↔ Schemas - -| **Método** | **Ruta** | **Caso de uso** | **Request Schema (validación)** | **Response Schema (DTO)** | **Notas** | -|-----------:|---------------------|--------------------------------------------------|--------------------------------------------|-------------------------------------------|-----------| -| GET | `/entities` | Listado con criterios (Criteria normalizado) | `EntityListCriteriaSchema` | `EntityListResponseSchema` | `data: EntitySummary[]`, `pageNumber`, `pageSize`, `totalItems`. | -| GET | `/entities` | Búsqueda/filtrado por query params “raw” | `SearchEntityQuerySchema` | `EntityListResponseSchema` | Úsalo si **no** aplicas patrón Criteria. | -| GET | `/entities/:id` | Obtener por ID | `GetEntityByIdParamsSchema` | `EntityDetailResponseSchema` | Si devuelves el recurso “puro”, puedes usar `EntityResourceSchema`. | -| POST | `/entities` | Crear recurso | `CreateEntityCommandSchema` | `EntityCreatedResponseSchema` **o** `EntityResourceSchema` | Si devuelves 201 + recurso completo, usa `EntityResourceSchema`. | -| PUT | `/entities/:id` | Reemplazar recurso (idempotente) | `UpdateEntityCommandSchema` + `UpdateEntityByIdParamsSchema` | `EntityResourceSchema` | También válido con PATCH para cambios parciales. | -| PATCH | `/entities/:id` | Actualizar parcialmente | `PatchEntityCommandSchema` + `UpdateEntityByIdParamsSchema` | `EntityResourceSchema` | Usa un comando separado si el payload difiere del PUT. | -| DELETE | `/entities/:id` | Eliminar por ID | `DeleteEntityByIdParamsSchema` | — (204 No Content) **o** `EntityDeletedResponseSchema` | 204 recomendado. Si devuelves body, usa `EntityDeletedResponseSchema`. | - ---- - -## Esquemas reutilizables (Responses) - -- **Item para listados:** - `EntitySummarySchema` - -- **Recurso completo:** - `EntityResourceSchema` - -- **Respuestas compuestas:** - - `EntityListResponseSchema` → `data: EntitySummarySchema[]` + paginación/meta - - `EntityDetailResponseSchema` → típicamente `EntityResourceSchema` - - `EntityCreatedResponseSchema` → `{ id, createdAt, ... }` si no devuelves el recurso completo - - `EntityDeletedResponseSchema` → `{ id, deleted: true }` si no usas 204 - ---- - -## Convenciones de sufijos - -### Requests -- `...CommandSchema` → **body** (mutaciones) -- `...ParamsSchema` → **route params** (`/:id`) -- `...QuerySchema` → **query string** (`?page=...`) -- `...CriteriaSchema` → para queries normalizadas con patrón Criteria - -### Responses -- `...ListResponseSchema` → listado paginado -- `...SummarySchema` → item de listado -- `...DetailResponseSchema` → detalle de recurso -- `...ResourceSchema` → representación completa del recurso -- `...CreatedResponseSchema` → recurso recién creado -- `...DeletedResponseSchema` → confirmación de borrado (si no usas 204) - -# Ejemplos de nombres de Schemas por entidad - -## 1. Customers - -### Requests -- `CreateCustomerCommandSchema` -- `UpdateCustomerCommandSchema` -- `PatchCustomerCommandSchema` -- `DeleteCustomerByIdParamsSchema` - `GetCustomerByIdParamsSchema` -- `CustomerListCriteriaSchema` -- `SearchCustomerQuerySchema` +- `DeleteCustomerByIdParamsSchema` +- `UpdateIssuedInvoiceByIdCommandSchema` -### Responses +### Colecciones + +```text +List +``` + +Ejemplos: + +- `CustomerListCriteriaSchema` +- `ProformaListResponseSchema` +- `IssuedInvoiceListQuerySchema` + +## Sufijos estables + +### Request + +- `CommandSchema`: body de create, update o patch +- `ParamsSchema`: route params +- `QuerySchema`: query string raw +- `CriteriaSchema`: query normalizada con patrón Criteria + +### Response + +- `SummarySchema`: item de listado +- `ResourceSchema`: representación completa del recurso +- `DetailResponseSchema`: response de detalle +- `ListResponseSchema`: response de listado +- `CreatedResponseSchema`: confirmación o payload de creación +- `DeletedResponseSchema`: confirmación explícita de borrado cuando no se usa `204` + +## Ejemplos + +### Customers + +- `CreateCustomerCommandSchema` +- `GetCustomerByIdParamsSchema` +- `CustomerListCriteriaSchema` - `CustomerSummarySchema` - `CustomerResourceSchema` - `CustomerListResponseSchema` -- `CustomerDetailResponseSchema` -- `CustomerCreatedResponseSchema` -- `CustomerDeletedResponseSchema` ---- +### Proformas -## 2. Products +- `CreateProformaCommandSchema` +- `UpdateProformaByIdParamsSchema` +- `ProformaListCriteriaSchema` +- `ProformaSummarySchema` +- `ProformaDetailResponseSchema` -### Requests -- `CreateProductCommandSchema` -- `UpdateProductCommandSchema` -- `PatchProductCommandSchema` -- `DeleteProductByIdParamsSchema` -- `GetProductByIdParamsSchema` -- `ProductListCriteriaSchema` -- `SearchProductQuerySchema` +## Errores a evitar -### Responses -- `ProductSummarySchema` -- `ProductResourceSchema` -- `ProductListResponseSchema` -- `ProductDetailResponseSchema` -- `ProductCreatedResponseSchema` -- `ProductDeletedResponseSchema` +- `ListCustomerCriteriaSchema` +- `CustomersListResponseSchema` +- `UpdateCustomerCommandByIdSchema` +- `ProductGetByIdParamsSchema` ---- +## Relación con la guía general -## Notas -- Reutilizar esquemas básicos (`EntitySummarySchema`, `EntityResourceSchema`) en respuestas compuestas para cumplir el principio **DRY**. -- **Sufijos**: mantener siempre los sufijos `CommandSchema`, `ParamsSchema`, `QuerySchema`, `CriteriaSchema`, `ListResponseSchema`, `SummarySchema`, `DetailResponseSchema`, `ResourceSchema`, `CreatedResponseSchema`, `DeletedResponseSchema`. -- **Pluralización**: usar singular para el nombre de la entidad en los Schemas (ej. `Customer`, `Product`), incluso si la ruta es plural (`/customers`, `/products`). -- **Carpetas sugeridas**: - modules/ - / - application/ - dto/ - requests/ - responses/ - domain/ - infrastructure/ - api/ +La guía de DTOs define: + +- organización por carpetas +- tipado semántico +- manejo de `null`, `optional()` y colecciones +- diseño de `create`, `update`, `list` y `detail` + +Este documento solo fija el naming. diff --git a/docs/DTOS - GUÍA DE DISEÑO.md b/docs/DTOS - GUÍA DE DISEÑO.md index b470ca6a..bf737dcc 100644 --- a/docs/DTOS - GUÍA DE DISEÑO.md +++ b/docs/DTOS - GUÍA DE DISEÑO.md @@ -1,5 +1,9 @@ # Guía de diseño de DTOs para el ERP +Documento principal para el diseño de DTOs y schemas HTTP del proyecto. + +Para reglas de naming más puntuales, consultar también [CONVENCION NOMBRADO DE ESQUEMAS DTO.md](./CONVENCION NOMBRADO DE ESQUEMAS DTO.md). + ## 1. Principios generales - Un DTO representa únicamente el contrato de transporte entre cliente y servidor. @@ -409,4 +413,4 @@ Antes de cerrar un DTO, comprobar: 9. ¿El mismo campo mantiene el mismo tipo en todo el módulo? 10. ¿El DTO refleja una vista concreta del endpoint? ---- \ No newline at end of file +--- diff --git a/docs/FACTURAS DE CLIENTE.md b/docs/FACTURAS DE CLIENTE.md index ccc37fa3..7058677c 100644 --- a/docs/FACTURAS DE CLIENTE.md +++ b/docs/FACTURAS DE CLIENTE.md @@ -1,56 +1,20 @@ +# Facturas de Cliente (Notas Históricas) +Este fichero contenía notas sueltas y ejemplos tempranos sobre facturación. Ya no actúa como documento operativo. -Funcionalidades ---------------- +## Estado -- Llamar a las facturas "borrador" como PROFORMA?? +- histórico +- no canónico +- supersedido por la documentación de `customer-invoices` -- Baja lógica de facturas -- Los datos del cliente están en la factura -- Descuento general (2 decimales) -- Resumen de impuestos -- Referencia de cliente -> ??? -- Idioma -- Divisa -- Observaciones -- Descripción de la operación -> obligatorio según Verifactu +## Documentación vigente +- [Índice de `customer-invoices`](./customer-invoices/README.md) +- [Contrato de create de proformas](./customer-invoices/proforma-create-contract.md) +- [Contrato de series de proformas](./customer-invoices/proforma-series-contract.md) +- [Reports de proformas](./customer-invoices/proformas-reports.md) -Detalles: -- Descripción -- Cantidad -> 2 decimales -- Importe unidad -> 4 decimales -- IVA en los detalles -- Descuento -> 2 decimales +## Motivo de conservación -Futuros: -- Unidad de medida -> texto - - - -Formas de pago: -- Sin plazo -> texto + establecer una fecha manualemente en el futuro -- Con plazo -> texto + fechas pre-establecidas no editables - - - - -{ - "serie": "A", - "numero": "2", - "fecha_expedicion": "15-07-2025", - "tipo_factura": "F1", - "descripcion": "Mensualidad de soporte", - "nif": "A15022510", - "nombre": "Empresa de prueba SL", - "lineas": [ - { - "base_imponible": "200", - "tipo_impositivo": "21", - "impuesto": "01", - "clave_regimen": "01", - "cuota_repercutida": "42" - } - ], - "importe_total": "242" -} \ No newline at end of file +Se mantiene solo como rastro de brainstorming previo y no debe emplearse para extraer contratos, payloads ni reglas funcionales actuales. diff --git a/docs/README.md b/docs/README.md index b2ca108a..9b6f30b5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,87 +1,83 @@ # ERP Docs -Este directorio reúne la documentación operativa y arquitectónica del ERP. +Este directorio reúne la documentación técnica, operativa y de apoyo del ERP. -## Contexto técnico -Monorepo con: -- Node.js >= 22 -- TypeScript -- PNPM -- Turborepo -- ExpressJS -- MariaDB -- Sequelize v6 -- Vitest +## Cómo leer esta documentación -Arquitectura: -- DDD pragmático -- Clean Architecture -- Separación Domain / Application / Infrastructure -- SOLID, DRY, KISS, YAGNI, SSOT -- Fail Fast -- Defensive Programming -- Cambios incrementales -- Evitar sobreingeniería +- Empieza por este índice si necesitas orientarte. +- Usa los documentos en subdirectorios temáticos como fuente de verdad operativa. +- Trata los documentos marcados como históricos o legacy como contexto, no como contrato vigente. -## Documentación vigente +## Estructura documental -### Arquitectura general +### Documentación vigente -- [Arquitectura de identity y companies](./architecture/identity-and-companies.md) +#### Arquitectura y runtime + +- [Arquitectura general](./architecture/README.md) - [Contexto tenant y `X-Company-Id`](./architecture/tenant-context.md) -- [API de autenticación identity](./architecture/identity-auth-api.md) -- [Estado de migración a identity](./architecture/identity-migration-status.md) -- [Flujo frontend de auth y selección de empresa](./frontend/auth-and-company-selection.md) +- [API de autenticación de `identity`](./architecture/identity-auth-api.md) +- [Relación entre `identity` y `companies`](./architecture/identity-and-companies.md) +- [Estado de migración a `identity`](./architecture/identity-migration-status.md) -### Document series +#### Frontend + +- [Flujo de auth y selección de empresa](./frontend/auth-and-company-selection.md) +- [README de `apps/web`](../apps/web/README.md) + +#### Customer invoices y proformas + +- [Índice de `customer-invoices`](./customer-invoices/README.md) +- [Contrato de series de proformas](./customer-invoices/proforma-series-contract.md) +- [Contrato de create de proformas](./customer-invoices/proforma-create-contract.md) +- [Configuración fiscal persistida en proformas](./customer-invoices/proforma-tax-config.md) +- [Reports de proformas](./customer-invoices/proformas-reports.md) + +#### Document series - [README de `document-series`](./document-series/README.md) - [Diseño de `document-series`](./document-series/document-series-design.md) - [Validación manual de `document-series`](./document-series/manual-validation.md) +- [Validación manual de concurrencia](./document-series/manual-concurrency-validation.md) -### Customer-invoices / proformas +#### Módulos y runtime local -- [Contrato de series de proformas](./customer-invoices/proforma-series-contract.md) -- [Contrato de create de proformas](./customer-invoices/proforma-create-contract.md) -- [Tax config persistido en proformas](./customer-invoices/proforma-tax-config.md) - -### SQL operativos / runbooks vigentes - -- [DDL de series documentales en proformas](./customer-invoices/sql/add-proforma-document-series-columns.sql) -- [DDL de `tax_config` en proformas](./customer-invoices/sql/add-proforma-tax-config-columns.sql) -- [Validación de migración a `document_series`](./document-series/sql/validate-document-series-migration.sql) -- [Validación de `tax_config` en proformas](./customer-invoices/sql/validate-proforma-tax-config.sql) - -### Referencias de módulos - -- [README de `modules/auth`](../modules/auth/README.md) - [README de `modules/identity`](../modules/identity/README.md) - [README de `modules/companies`](../modules/companies/README.md) -- [README de `apps/web`](../apps/web/README.md) +- [README de `modules/auth`](../modules/auth/README.md) -## Histórico / migraciones transicionales +#### Guías y estándares -- [Migración desde `customer_invoice_series` a `document_series`](./document-series/migration-from-customer-invoice-series.md) -- [Split histórico Proforma / IssuedInvoice](./customer-invoices/split-proformas-issued-invoices-migration.md) -- [InvoiceSeries legacy](./customer-invoices/invoice-series.md) -- [Drop manual de `customer_invoice_series`](./document-series/sql/drop-customer-invoice-series-legacy.sql) -- [SQL histórico de `customer_invoice_series`](./dev/customer-invoice-series.sql) -- [Runbooks SQL históricos de `customer-invoices`](./customer-invoices/sql/README.md) +- [Guía de diseño de DTOs](./DTOS - GUÍA DE DISEÑO.md) +- [Convención de nombres de schemas DTO](./CONVENCION NOMBRADO DE ESQUEMAS DTO.md) + +#### SQL operativos y runbooks + +- [SQL de `customer-invoices`](./customer-invoices/sql/README.md) +- [Validación de migración a `document-series`](./document-series/sql/validate-document-series-migration.sql) - [Seed local de admin](./dev/seed-local-admin.sql) -## Estado actual resumido +### Material auxiliar -- El backend nuevo usa `modules/identity` para autenticación, sesión y control de acceso a companies. -- `modules/companies` es la fuente de verdad de la ficha operativa de empresa. -- El frontend ERP ya usa `@erp/identity/client` como runtime de autenticación. -- El flujo actual resuelve sesión, companies disponibles, auto-selección de empresa única y refresh automático sobre `401`. +- [Base común para prompts backend](./backend-prompt-base.md) +- [Prompt de auth](./prompt-auth.md) +- [Prompt de customer invoices](./prompt-customer-invoices.md) +- [Plantilla auxiliar](./TEMPLATE.md) -## Reglas de lectura rápida +### Histórico, transicional o de descubrimiento -- `modules/auth` debe considerarse legacy/deprecated para backend nuevo. -- `GET /companies/available` es el endpoint vigente para cargar companies accesibles. -- `GET /identity/companies` no es un endpoint válido del diseño actual. -- `GET /account/companies` tampoco es un endpoint válido del diseño actual. -- Las rutas tenant-scoped deben usar `Authorization` + `X-Company-Id`. -- `/document-series` es la API canónica de series documentales. -- `/catalogs/invoice-series` no es un endpoint activo. +Estos documentos se conservan por trazabilidad, auditoría o contexto de decisiones. No deben usarse como fuente de verdad del runtime actual sin contrastarlos con la documentación vigente. + +- [Borrador legacy de API](./API.md) +- [Requisitos generales históricos](./REQUISITOS GENERALES.md) +- [Requisitos históricos del módulo de clientes](./REQUISITOS CLIENTES.md) +- [Notas históricas de facturas de cliente](./FACTURAS DE CLIENTE.md) +- [Split histórico Proforma / IssuedInvoice](./customer-invoices/split-proformas-issued-invoices-migration.md) +- [Migración desde `customer_invoice_series`](./document-series/migration-from-customer-invoice-series.md) +- [InvoiceSeries legacy](./customer-invoices/invoice-series.md) + +## Regla práctica de mantenimiento + +- Los documentos operativos vigentes deben vivir en subdirectorios temáticos y tener una responsabilidad clara. +- Los documentos raíz en mayúsculas o con naming heredado deben considerarse legacy o auxiliares salvo que este índice los declare explícitamente vigentes. +- Si una regla ya tiene fuente canónica, los demás documentos deben resumirla y enlazarla, no copiarla. diff --git a/docs/REQUISITOS CLIENTES.md b/docs/REQUISITOS CLIENTES.md index 0175fda1..6d21c04e 100644 --- a/docs/REQUISITOS CLIENTES.md +++ b/docs/REQUISITOS CLIENTES.md @@ -1,165 +1,32 @@ -# **Especificaciones del Módulo de Clientes - ERP** +# Requisitos del Módulo de Clientes (Histórico / Descubrimiento) -El **módulo de clientes** del ERP permite gestionar la información de clientes, asegurando compatibilidad con la gestión multiempresa, multiidioma y multi-moneda. A continuación, se detallan las especificaciones, incluyendo los campos clave y su función. +Este documento recoge requisitos tempranos del módulo de clientes y ya no debe leerse como contrato técnico vigente. ---- +## Estado -## **1. Funcionalidades del Módulo de Clientes** -El módulo de clientes debe permitir: -- **Crear, leer, actualizar y eliminar clientes** (CRUD). -- **Asociar clientes a una empresa** y, opcionalmente, a una o más sucursales. -- **Registrar clientes como personas físicas o empresas**. -- **Gestionar datos fiscales, comerciales y de contacto**. -- **Definir configuraciones de facturación y pago personalizadas**. -- **Aplicar descuentos en distintos niveles**. -- **Manejar aspectos financieros como retenciones y recargos**. -- **Identificar clientes con riesgo financiero**. -- **Asignar comerciales o delegados**. -- **Controlar el estado del cliente** (activo/inactivo). -- **Registrar auditoría de cambios**. +- Documento histórico de análisis funcional. +- Aporta vocabulario de negocio y posibles campos de cliente. +- No define endpoints, DTOs ni persistencia actuales del módulo. ---- +## Qué puede seguir siendo útil -## **2. Estructura del Cliente y sus Campos** -Cada cliente tiene los siguientes atributos: +- tipologías de datos de cliente +- necesidades de facturación, fiscalidad y contacto +- consideraciones de auditoría y permisos -### **Datos Generales** -| Campo | Tipo de Dato | Descripción | -|-----------------------|-----------------|-------------| -| `id` | `INT` (PK) | Identificador único del cliente. | -| `company_id` | `INT` (FK) | Empresa a la que pertenece el cliente. | -| `is_company` | `BOOLEAN` | Indica si el cliente es una empresa (`true`) o una persona física (`false`). | -| `fiscal_name` | `VARCHAR(255)` | Nombre fiscal del cliente (solo si es empresa). | -| `commercial_name` | `VARCHAR(255)` | Nombre comercial del cliente (si aplica). | -| `name` | `VARCHAR(255)` | Nombre de la persona o empresa. | -| `email` | `VARCHAR(100)` | Correo electrónico único del cliente. | -| `phone` | `VARCHAR(20)` | Número de teléfono de contacto. | -| `address` | `TEXT` | Dirección del cliente. | -| `country_code` | `CHAR(2)` | Código del país del cliente (ISO 3166-1 alpha-2). | -| `origin` | `VARCHAR(100)` | Origen del cliente (ej. publicidad, redes sociales, feria, escaparate). | +## Qué no debe asumirse como vigente -### **Configuración de Moneda e Idioma** -| Campo | Tipo de Dato | Descripción | -|---------------------|-----------------|-------------| -| `currency_code` | `CHAR(3)` | Código de la divisa con la que trabaja el cliente (ISO 4217). | -| `language_code` | `CHAR(5)` | Idioma preferido del cliente para documentos y comunicación (ej. `es-ES`, `en-US`). | +- rutas `/api/...` +- tipos SQL y tablas propuestas +- naming técnico de campos y relaciones +- contratos HTTP o middleware de acceso -### **Información Fiscal y Financiera** -| Campo | Tipo de Dato | Descripción | -|------------------------|----------------|-------------| -| `vat_percentage` | `DECIMAL(5,2)` | Porcentaje de IVA aplicable al cliente. | -| `equivalence_charge` | `BOOLEAN` | Indica si el cliente aplica recargo de equivalencia. | -| `withholding_percentage` | `DECIMAL(5,2)` | Porcentaje de retención en facturas del cliente. | -| `payment_method` | `VARCHAR(100)` | Forma de pago habitual del cliente (ej. transferencia, tarjeta, efectivo). | -| `payment_day` | `INT` | Día del mes en que el cliente realiza pagos (1-31). | -| `risk` | `BOOLEAN` | Indica si el cliente tiene riesgo financiero o historial de morosidad. | +## Referencias actuales -### **Descuentos y Tarifas Especiales** -| Campo | Tipo de Dato | Descripción | -|---------------------|----------------|-------------| -| `discount_line` | `DECIMAL(5,2)` | Descuento aplicado a nivel de línea en presupuestos. | -| `discount_chapter` | `DECIMAL(5,2)` | Descuento aplicado a nivel de capítulo en presupuestos. | -| `discount_global` | `DECIMAL(5,2)` | Descuento global aplicado al presupuesto. | -| `price_point` | `DECIMAL(10,2)` | Valor de "precio punto" del cliente, usado para calcular costos personalizados en catálogo. | +- Arquitectura del repositorio: [README.md](../README.md) +- Índice de docs: [README.md](./README.md) +- Reglas de DTO y schemas: [DTOS - GUÍA DE DISEÑO.md](./DTOS - GUÍA DE DISEÑO.md) y [CONVENCION NOMBRADO DE ESQUEMAS DTO.md](./CONVENCION NOMBRADO DE ESQUEMAS DTO.md) -### **Clasificación y Estado** -| Campo | Tipo de Dato | Descripción | -|---------------|----------------|-------------| -| `client_type` | `VARCHAR(100)` | Tipo de cliente (ej. profesionales, constructoras, distribuidores, particulares). | -| `is_active` | `BOOLEAN` | Indica si el cliente está activo (`true`) o dado de baja (`false`). | +## Motivo de conservación -### **Delegados y Comerciales** -| Campo | Tipo de Dato | Descripción | -|-----------------|----------------|-------------| -| `Client_Sales_Rep` | `Tabla Relacional` | Relación con los comerciales asignados a este cliente. | - ---- - -## **3. Auditoría y Seguridad** -Cada cambio en los datos de un cliente debe registrarse en una tabla de auditoría: -```sql -CREATE TABLE Client_Audit ( - id SERIAL PRIMARY KEY, - client_id INT NOT NULL REFERENCES Clients(id) ON DELETE CASCADE, - user_id INT NOT NULL REFERENCES Users(id), - change_description TEXT, - change_timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); -``` -Esto permitirá rastrear modificaciones y garantizar transparencia en la gestión de clientes. - ---- - -## **4. Endpoints Principales de la API** -### **4.1. Listar Clientes** -```http -GET /api/companies/{company_id}/clients -Authorization: Bearer -``` - -### **4.2. Crear Cliente** -```http -POST /api/companies/{company_id}/clients -Authorization: Bearer -``` -**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 -``` - -### **4.4. Actualizar Cliente** -```http -PUT /api/clients/{client_id} -Authorization: Bearer -``` - -### **4.5. Baja Lógica de Cliente** -```http -PATCH /api/clients/{client_id}/deactivate -Authorization: Bearer -``` - -### **4.6. Asignar Comercial a Cliente** -```http -POST /api/clients/{client_id}/sales-reps -Authorization: Bearer -``` - ---- - -## **5. Seguridad y Control de Acceso** -- **Middleware de autenticación** para validar que solo usuarios autorizados gestionen clientes. -- **Permisos basados en roles**, como `Clientes: leer`, `Clientes: escribir`, `Clientes: eliminar`. -- **Reglas de validación** para evitar datos erróneos o incompletos. -- **Registro de auditoría** para cada modificación. - ---- +Se conserva como material de descubrimiento funcional previo al diseño actual. diff --git a/docs/REQUISITOS GENERALES.md b/docs/REQUISITOS GENERALES.md index 174659e3..50791ba6 100644 --- a/docs/REQUISITOS GENERALES.md +++ b/docs/REQUISITOS GENERALES.md @@ -1,207 +1,28 @@ -# **Requisitos Técnicos del ERP** +# Requisitos Generales (Histórico / Descubrimiento) -## **1. Administración de Usuarios** +Este documento resume requisitos funcionales tempranos del ERP. No define el runtime actual ni sustituye la documentación operativa vigente. -- **Roles y Perfiles**: +## Estado - - Los usuarios pueden tener uno o más perfiles predefinidos (por ejemplo, "Ventas", "Compras"). - - Los perfiles controlan permisos generales a nivel de empresa y refinados a nivel de sucursal. - - Un usuario puede tener diferentes perfiles en distintas sucursales de la misma empresa. +- Documento histórico de descubrimiento. +- Útil para entender intención de producto y necesidades de negocio amplias. +- No usar como contrato técnico actual de autenticación, tenant context, JWT, base de datos o endpoints. -- **Permisos**: +## Temas que sí conserva como contexto - - Configurables por módulo (Presupuestos, Facturas, etc.). - - Granularidad: permitir acciones específicas como "leer", "escribir", "eliminar". - - Un usuario puede tener permisos para acceder a una o más sucursales de la empresa, pero debe trabajar en una empresa activa por sesión. - - Si el usuario solo tiene permisos para una sucursal, se selecciona automáticamente. Si tiene acceso a varias, se selecciona solo cuando sea necesario. +- multiempresa y multisucursal +- auditoría y trazabilidad +- seguridad y permisos +- soporte multiidioma +- extensibilidad modular -- **Superadministrador**: - - Puede crear, modificar y eliminar usuarios. - - Gestiona perfiles, permisos y asociaciones con empresas y sucursales. +## Documentación vigente relacionada ---- +- Arquitectura y tenant context: [architecture/README.md](./architecture/README.md) +- Auth actual: [architecture/identity-auth-api.md](./architecture/identity-auth-api.md) +- Flujo frontend: [frontend/auth-and-company-selection.md](./frontend/auth-and-company-selection.md) +- Módulos documentales: [customer-invoices/README.md](./customer-invoices/README.md) y [document-series/README.md](./document-series/README.md) -## **2. Gestión Multiempresa y Multisucursal** +## Nota -- **Usuarios y Empresas**: - - - Un usuario puede estar asociado a múltiples empresas. - - En cada sesión o pestaña, el usuario puede trabajar con una empresa específica. - - La empresa activa se elige al inicio de sesión. - -- **Sucursales**: - - - Cada empresa debe tener al menos una sucursal, pero puede tener varias. - - Los usuarios de una sucursal pueden gestionar datos de otras sucursales si tienen los permisos adecuados. - - Se permite definir permisos para operar en múltiples sucursales dentro de la misma empresa. - -- **Flujo de Selección**: - - Al iniciar sesión, el usuario elige la empresa activa. - - Si tiene acceso a una única sucursal, esta se selecciona automáticamente. - - Si tiene acceso a múltiples sucursales, la selección de sucursal ocurre solo cuando sea necesario para una operación específica. - ---- - -## **3. Soporte Multiidioma y Regionalización** - -- **Idiomas**: - - - La interfaz del ERP debe estar disponible en varios idiomas. - - Los documentos (presupuestos, facturas) pueden generarse en el idioma del cliente. - - El idioma de usuario es configurable en su perfil. - -- **Monedas e Impuestos**: - - Configuración de moneda por empresa, con posibilidad de conversión. - - Definición de impuestos según país (IVA, retenciones, tasas locales). - - Flexibilidad para configurar tarifas por categoría de productos o servicios. - ---- - -## **4. Contexto de Sesión y Pestaña** - -- **Contexto por Pestaña**: - - - Cada pestaña tiene un contexto independiente (`tab_id`), que incluye la empresa activa. - - Si se requiere una sucursal para la operación, se solicitará en ese momento. - - Se almacena el `tab_id` junto con la empresa activa en el backend para cada sesión de usuario. - -- **Gestión de Sesiones**: - - Tokens JWT deben incluir información sobre: - - `user_id` - - `tab_id` - - `active_company_id` - - Lista de permisos específicos - - Se debe registrar el contexto de pestaña en la base de datos para auditoría y trazabilidad. - ---- - -## **5. Auditoría y Registro de Actividades** - -- **Registro de Cambios en Documentos**: - - - Presupuestos, facturas y otros documentos deben mantener un historial detallado de modificaciones. - - Se registra la siguiente información: - - Usuario que realizó el cambio. - - Fecha y hora. - - Cambios específicos (antes y después). - -- **Trazabilidad Global**: - - Auditar las actividades del usuario a nivel de empresa, sucursal y pestaña. - - Se debe asociar cada acción al `tab_id` para identificar actividades por pestaña específica. - ---- - -## **6. Documentos Operativos** - -- **Historial de Cambios**: - - - Cada documento (presupuestos, facturas, etc.) debe registrar todas las modificaciones con la posibilidad de revertirlas si es necesario. - -- **Exportación Multiidioma**: - - Generación de documentos en diferentes idiomas según la configuración del cliente. - ---- - -## **7. Seguridad** - -- **Autenticación**: - - - Autenticación basada en tokens JWT. - - Contraseñas encriptadas con bcrypt o Argon2. - - Soporte para autenticación en dos pasos (2FA) en futuras iteraciones. - -- **Control de Acceso**: - - Validación de permisos en cada solicitud al backend. - - Aplicación del principio de mínimos privilegios para usuarios. - ---- - -## **8. Extensibilidad y Modularidad** - -- **Estructura Modular**: - - El sistema debe permitir la integración de nuevos módulos sin afectar los permisos o el funcionamiento existente. - - Módulos actuales planificados: - - Presupuestos - - Facturas - - Clientes - - Pedidos - - Inventario (futuro) - - Recursos Humanos (futuro) - ---- - -## **9. Base de Datos - Diseño Técnico** - -### **Tablas Principales** - -```sql --- Usuarios -CREATE TABLE Users ( - id SERIAL PRIMARY KEY, - username VARCHAR(50) UNIQUE NOT NULL, - email VARCHAR(100) UNIQUE NOT NULL, - password VARCHAR(255) NOT NULL, - is_active BOOLEAN DEFAULT TRUE, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -); -``` - -```sql --- Empresas -CREATE TABLE Companies ( - id SERIAL PRIMARY KEY, - name VARCHAR(100) UNIQUE NOT NULL, - country_code CHAR(2), - currency_code CHAR(3), - tax_config JSONB, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -); -``` - -```sql --- Sucursales -CREATE TABLE Branches ( - id SERIAL PRIMARY KEY, - name VARCHAR(100) NOT NULL, - company_id INT NOT NULL REFERENCES Companies(id) ON DELETE CASCADE, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -); -``` - -```sql --- Relación Usuario - Empresa -CREATE TABLE User_Company ( - id SERIAL PRIMARY KEY, - user_id INT NOT NULL REFERENCES Users(id), - company_id INT NOT NULL REFERENCES Companies(id), - is_active BOOLEAN DEFAULT FALSE, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -); -``` - -```sql --- Relación Usuario - Sucursal -CREATE TABLE User_Branch ( - id SERIAL PRIMARY KEY, - user_id INT NOT NULL REFERENCES Users(id), - branch_id INT NOT NULL REFERENCES Branches(id), - permissions JSONB NOT NULL -); -``` - -```sql --- Contexto por pestaña -CREATE TABLE Tab_Context ( - id SERIAL PRIMARY KEY, - user_id INT NOT NULL REFERENCES Users(id), - tab_id UUID NOT NULL, - company_id INT NOT NULL REFERENCES Companies(id), - branch_id INT REFERENCES Branches(id) -); -``` - ---- +Las decisiones antiguas sobre JWT con `active_company_id`, `tab_id`, tablas SQL de ejemplo y flujos de selección tempranos quedaron superadas por la arquitectura actual y se conservan solo a nivel histórico. diff --git a/docs/TEMPLATE.md b/docs/TEMPLATE.md index a8f89b14..69110973 100644 --- a/docs/TEMPLATE.md +++ b/docs/TEMPLATE.md @@ -1,43 +1,15 @@ -### Prompt para tu README / CLI +# TEMPLATE -**Generar un nuevo módulo ERP desde la plantilla** +Plantilla auxiliar para redactar documentación o prompts de trabajo. -Nuestro ERP permite crear módulos de forma **estandarizada** usando una **plantilla base** (Hexagonal/DDD, TS, Express, Sequelize) y un **generador Plop**. +## Estado -**Opción A — con Plop (recomendada)** - -1. Instala dependencias de desarrollo si no las tienes: - - * `pnpm add -D plop` -2. Asegúrate de tener en el repo: - - * `plopfile.js` con el generador `module` - * Carpeta `templates/new-module/` con la plantilla (placeholders Handlebars: `{{kebabCase name}}`, `{{pascalCase name}}`, etc.) -3. Ejecuta el generador: - - * `pnpm plop module` -4. Introduce el nombre del módulo en **kebab-case** (p. ej., `suppliers`). -5. Se creará `packages/suppliers/` con: - - * `src/api/{application,domain,infrastructure,helpers}` + `common` - * Casos de uso CRUD, presenters (FULL/LIST), specs comunes y contratos de repositorio. -6. Integra en el monorepo: - - * Añade targets en `turbo.json` si aplica. - * Ejecuta `pnpm -w build` para validar compilación. - -**Opción B — manual (desde ZIP de plantilla)** - -1. Descomprime `new-module-template.zip`. -2. Copia `new-module/` a `packages//`. -3. Renombra los archivos con `entity`/`{{name}}` por el nombre real o usa búsqueda global. -4. Ajusta `package.json` (`name`, `scripts`), y registra el módulo en `turbo.json`. -5. `pnpm -w install && pnpm -w build`. - -**Convenciones clave** - -* Nombres de archivos/directorios en **kebab-case**; código en **Inglés**; comentarios **TSDoc en castellano**. -* Mantén SOLID, SoC, alta cohesión/bajo acoplamiento, y hexagonal (puertos/adaptadores). -* Observabilidad (logs estructurados), idempotencia en endpoints, y principio de menor privilegio. +- material auxiliar +- no describe comportamiento del ERP +- no debe enlazarse como documentación funcional o arquitectónica del proyecto +## Uso recomendado +- reutilizarla solo como apoyo editorial +- adaptar el contenido final al área concreta del proyecto +- no duplicar desde aquí reglas ya documentadas en los índices y guías vigentes diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 91add18f..26e89805 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -1,18 +1,25 @@ # ERP Architecture Docs -## Documentos vigentes +Índice de arquitectura vigente y documentos transicionales relacionados con autenticación, tenant context y migración a `identity`. + +## Documentación canónica vigente - [Identity and companies](./identity-and-companies.md) - [Tenant context](./tenant-context.md) - [Identity auth API](./identity-auth-api.md) -- [Identity backend architecture](./identity-backend-architecture.md) - [Identity migration status](./identity-migration-status.md) - [Identity roadmap](./identity-roadmap.md) +## Documentación complementaria + +- [Identity backend architecture](./identity-backend-architecture.md) + +`identity-backend-architecture.md` se mantiene como nota complementaria de diseño backend. Para contratos operativos concretos, prevalecen `tenant-context.md`, `identity-auth-api.md` y los README de módulo. + ## Resumen actual -- `identity` se encarga de autenticación, sesión, refresh y memberships. -- `companies` se encarga de la ficha operativa de empresa. -- `GET /companies/available` es la vía actual para listar companies accesibles. +- `identity` gestiona autenticación, sesión, refresh y memberships. +- `companies` gestiona la ficha operativa de empresa. +- `GET /companies/available` es la vía vigente para companies accesibles. - El contexto tenant se resuelve con `X-Company-Id` y `requireIdentityTenant(params)`. -- El frontend ERP ya usa el runtime nuevo de `identity` con auto-selección de empresa y refresh automático. +- El frontend ERP ya usa el runtime nuevo de `identity`. diff --git a/docs/architecture/identity-backend-architecture.md b/docs/architecture/identity-backend-architecture.md index ef6aba50..7ec8e90e 100644 --- a/docs/architecture/identity-backend-architecture.md +++ b/docs/architecture/identity-backend-architecture.md @@ -1,5 +1,14 @@ # Identity Backend Architecture +Documento complementario de diseño backend. + +Para contratos operativos vigentes, prevalecen: + +- `tenant-context.md` +- `identity-auth-api.md` +- `identity-and-companies.md` +- `modules/identity/README.md` + ## Objetivo del módulo `identity` `identity` sustituye progresivamente al módulo legacy `auth`. diff --git a/docs/architecture/identity-migration-status.md b/docs/architecture/identity-migration-status.md index f6280f77..aaa7ff18 100644 --- a/docs/architecture/identity-migration-status.md +++ b/docs/architecture/identity-migration-status.md @@ -1,22 +1,10 @@ # Identity Migration Status -## Patrón final de middleware +## Referencias canónicas -Rutas tenant-scoped: - -```ts -import { requireIdentityTenant } from "@erp/identity/api"; - -router.use(...requireIdentityTenant(params)); -``` - -Rutas autenticadas sin tenant: - -```ts -import { requireIdentityAuthenticated } from "@erp/identity/api"; - -router.use(...requireIdentityAuthenticated(params)); -``` +- Reglas de contexto tenant y middlewares públicos: [tenant-context.md](./tenant-context.md) +- Flujo frontend de autenticación y selección de empresa: [../frontend/auth-and-company-selection.md](../frontend/auth-and-company-selection.md) +- Runtime y responsabilidades del módulo `identity`: [../../modules/identity/README.md](../../modules/identity/README.md) ## Migrado a `identity` @@ -34,17 +22,11 @@ Estado: - sin `@erp/auth/api` - sin `mockUser` - sin helpers locales de auth -- usan `requireIdentityTenant(params)` +- usan el middleware tenant canónico documentado en `tenant-context.md` ### Backend autenticado sin tenant -`modules/companies` usa: - -```ts -import { requireIdentityAuthenticated } from "@erp/identity/api"; - -router.use(...requireIdentityAuthenticated(params)); -``` +`modules/companies` usa el middleware autenticado sin tenant documentado en `tenant-context.md`. Esto aplica a: @@ -59,25 +41,9 @@ POST /companies/:company_id/disable ## Frontend actual -El runtime nuevo ya usa: +El runtime nuevo ya usa `@erp/identity/client`. -- `@erp/identity/client` -- `IdentityAuthSessionProvider` -- `DataSourceProvider` -- Axios compartido con refresh automático - -Flujo implementado: - -```txt -login --> GET /identity/auth/session --> GET /companies/available --> resolver empresa activa --> auto-selección si hay una sola empresa --> selector si hay varias --> /no-companies si no hay empresas --> X-Company-Id en rutas tenant-scoped -``` +Los detalles del flujo implementado, headers y refresh automático se mantienen en `frontend/auth-and-company-selection.md`. ## Legacy pendiente diff --git a/docs/backend-prompt-base.md b/docs/backend-prompt-base.md new file mode 100644 index 00000000..fb5d7074 --- /dev/null +++ b/docs/backend-prompt-base.md @@ -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 diff --git a/docs/customer-invoices/README.md b/docs/customer-invoices/README.md new file mode 100644 index 00000000..0c7ddc04 --- /dev/null +++ b/docs/customer-invoices/README.md @@ -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. diff --git a/docs/customer-invoices/proforma-series-contract.md b/docs/customer-invoices/proforma-series-contract.md index 16b69255..c5252ea6 100644 --- a/docs/customer-invoices/proforma-series-contract.md +++ b/docs/customer-invoices/proforma-series-contract.md @@ -25,11 +25,8 @@ En proformas: - se pide cliente - `proforma_series_code` es opcional; si falta, se usa la serie default activa de `document_type = proforma` - `target_invoice_series_code` es opcional; si falta, se persiste `NULL` -- la creacion de proforma genera una cabecera valida aunque no existan lineas -- `items` puede omitirse o enviarse como `[]` -- si no hay lineas valoradas, la response debe devolver `items = []`, `taxes = []` y totales a `0` -- `tax_regime_code` en create representa configuracion fiscal inicial, no impuestos ya aplicados - la UI obtiene ambas listas desde `GET /document-series`, filtrando por `document_type` +- la semantica detallada de create sin lineas, `tax_config`, `taxes` y `totals` vive en `proforma-create-contract.md` ### Editar draft @@ -63,17 +60,14 @@ En proformas: - el backend emite `target_invoice_series_code` como nombre preferente - `series` puede mantenerse solo como alias legacy/deprecated de salida mientras existan consumidores antiguos -- `taxes` representa impuestos calculados a partir de lineas valoradas -- `tax_config` representa la configuracion fiscal persistida de cabecera -- `totals` representa importes calculados a partir de lineas valoradas -- create no debe inventar filas fiscales sobre base `0` +- la semantica de `taxes`, `tax_config` y `totals` se documenta en `proforma-create-contract.md` ## Regla de emision - si `target_invoice_series_code` tiene valor, la emision usa ese `seriesCode` para `document_type = issued_invoice` - si `target_invoice_series_code` es `NULL`, `document-series` resuelve la serie default activa de `issued_invoice` - la UI nunca debe consumir numeracion directa desde `POST /document-series/assign-next` -- los impuestos y totales reales se recalculan cuando la proforma ya tiene lineas en update +- el recalculo de impuestos y totales reales al editar lineas se documenta en `proforma-create-contract.md` ## Regla de borrado y numeracion diff --git a/docs/customer-invoices/split-proformas-issued-invoices-migration.md b/docs/customer-invoices/split-proformas-issued-invoices-migration.md index 76b9d340..4ce20c71 100644 --- a/docs/customer-invoices/split-proformas-issued-invoices-migration.md +++ b/docs/customer-invoices/split-proformas-issued-invoices-migration.md @@ -111,17 +111,15 @@ Preparar el esquema fisico para separar: ## Regla operativa actual de series en proformas -- al crear una proforma, `document-series` se invoca con `document_type = proforma` para resolver la numeracion visible de la proforma -- ese resultado solo alimenta `document_series_id`, `proforma_number` y `proforma_reference` -- `target_invoice_series_code` no se rellena automaticamente con la serie documental de proforma -- si el usuario no selecciona serie futura de factura, `target_invoice_series_code` se persiste en `NULL` -- si el usuario informa `target_invoice_series_code`, el backend lo conserva solo si corresponde a una serie activa de `issued_invoice` -- al emitir una proforma, la factura definitiva se numera con `document_type = issued_invoice` -- si la proforma trae `target_invoice_series_code`, se usa como `seriesCode` -- si `target_invoice_series_code` es `NULL`, `document-series` resuelve la serie default activa de `issued_invoice` -- la referencia final devuelta por `document-series` se persiste en `issued_invoices.invoice_reference` -- `invoice_reference` es snapshot documental y no se recalcula despues -- `proforma_series_code` es un input de create para `document_type = proforma` y no vuelve a ser editable despues +La semantica operativa vigente ya no se mantiene en este documento historico. + +Fuente canonica actual: + +- `docs/customer-invoices/proforma-series-contract.md` +- `docs/customer-invoices/proforma-create-contract.md` +- `docs/customer-invoices/proforma-tax-config.md` + +Este documento conserva solo el contexto de migracion, fases y SQL historico. ## Estado Fase 2B @@ -142,30 +140,19 @@ Preparar el esquema fisico para separar: ## Estado Fase 2D - el contrato publico de proformas ya soporta `target_invoice_series_code` en create y update -- `series` se mantiene como alias legacy temporal en responses -- si `series` y `target_invoice_series_code` llegan a la vez con distinto valor, el request falla por validacion -- las responses de proforma prefieren `target_invoice_series_code` -- `series` se mantiene temporalmente en responses como alias legacy con el mismo valor +- la semantica detallada del alias legacy `series` se consolido despues en `proforma-series-contract.md` - el cliente interno del modulo ya emite `target_invoice_series_code` en requests nuevos ## Estado Fase 2E - create separa `proforma_series_code` de `target_invoice_series_code` -- `proforma_series_code` solo se usa para numerar la proforma -- update ya no usa `series` en el flujo nuevo ni permite cambiar la serie propia de proforma -- `target_invoice_series_code` sigue siendo el unico concepto de serie futura de factura emitida +- las reglas operativas exactas se consolidaron despues en `proforma-series-contract.md` - la UI nueva de proformas ya envia `proforma_series_code` y `target_invoice_series_code` ## Estado Fase 2F - create de proforma se consolida como creacion de cabecera/documento inicial -- una proforma sin lineas es valida y debe persistirse con `items = []` -- `tax_regime_code` queda como configuracion fiscal inicial de la cabecera -- `tax_config` queda persistido en `proformas` como configuracion fiscal de cabecera -- `proforma_taxes` solo representa impuestos calculados, no defaults fiscales ni placeholders sobre base `0` -- si una proforma nace sin lineas, la response debe devolver `taxes = []` y totales monetarios a `0` -- update sigue siendo el punto donde se anaden lineas y se materializan impuestos/totales reales -- la UI deja de prometer que la fiscalidad queda "aplicada" en create y pasa a hablar de valores iniciales +- la semantica funcional exacta de create se consolido despues en `proforma-create-contract.md` ## Estado Fase 2G @@ -174,9 +161,7 @@ Preparar el esquema fisico para separar: - el archivado es logico mediante `archived_at` - el listado V2 de proformas usa `Criteria filters[]` para `archived_at` y `status` - `GET /proformas/:id` permite abrir proformas archivadas -- el borrado de `draft` no depende de `archived_at` -- solo `draft` puede borrarse -- `rejected` sigue fuera del alcance y queda reservado para archivado futuro +- las reglas funcionales exactas de borrado y numeracion se consolidaron despues en `proforma-series-contract.md` - la validacion de borrado comprueba tambien `linked_invoice_id` y `issued_invoices.source_proforma_id` - la numeracion de `document-series` no se reutiliza ni se decrementa tras un borrado diff --git a/docs/prompt-auth.md b/docs/prompt-auth.md index 3b648112..fb2e6b5e 100644 --- a/docs/prompt-auth.md +++ b/docs/prompt-auth.md @@ -1,83 +1,77 @@ -📌 CONTEXTO DEL PROYECTO: -Estoy desarrollando una API en **Node.js** con **TypeScript**, **Express.js**, **Sequelize**, y **Arquitectura Hexagonal (Ports & Adapters)** bajo los principios de **DDD (Domain-Driven Design)** y **SOLID**. -La API maneja **autenticación con PassportJS**, soporte para **JWT y LocalStrategy**, gestión segura de contraseñas con **bcrypt**, y transformación de respuestas con **DTOs (Data Transfer Objects)**. +# Prompt Auth -📌 PRINCIPIOS Y PATRONES A APLICAR: -✅ **DDD (Domain-Driven Design):** - - Los **agregados** encapsulan su estado y exponen solo lo necesario. - - Cada **agregado** tiene su propio **mapper** para conversión entre persistencia y dominio. - - **Repositorios** manejan **agregados** y deben desacoplarse de Sequelize. +Base comun reutilizada: [`backend-prompt-base.md`](./backend-prompt-base.md) -✅ **SOLID (Principios de Diseño):** - - **SRP:** Cada clase tiene una única responsabilidad. - - **OCP:** El código debe ser fácil de extender sin modificarlo. - - **LSP:** Los objetos derivados deben poder sustituir a sus clases base. - - **ISP:** Usar interfaces específicas en lugar de dependencias directas. - - **DIP:** Usar **interfaces** (`IAuthProvider`, `ITransactionManager`) en lugar de acoplarse a implementaciones concretas. +Este prompt añade solo las reglas especificas de autenticacion sobre esa base. -📌 GESTIÓN DE DEPENDENCIAS: -✅ **Factory Pattern (`createAuthService`)** - - `AuthService` debe crearse con un **Factory** (`createAuthService()`), permitiendo cambiar su implementación sin afectar la API. - - `AuthService` depende de `IAuthenticatedUserRepository`, `ITransactionManager` y `IAuthProvider`. +## Contexto especifico -📌 AUTENTICACIÓN CON `PassportJS`: -✅ **Soporte para JWT y LocalStrategy** - - `PassportAuthProvider` maneja **JWTStrategy** para autenticación con tokens y **LocalStrategy** para validación con email y contraseña. - - Se debe usar `bcrypt` para cifrar contraseñas antes de almacenarlas. - - `validateUser()` en `PassportAuthProvider` verifica credenciales con `LocalStrategy`. +La API maneja autenticacion con `PassportJS`, soporte para `JWT` y `LocalStrategy`, gestion segura de contrasenas con `bcrypt` y transformacion de respuestas con DTOs. -📌 GESTIÓN SEGURA DE CONTRASEÑAS: -✅ **Uso de `bcrypt` para cifrado de contraseñas** - - `PasswordHash.create()` valida la fuerza de la contraseña antes de cifrarla. - - `bcrypt.compare()` se usa para validar la contraseña al hacer login. - - Las contraseñas **nunca deben almacenarse en texto plano en la base de datos**. +## Gestion de dependencias -📌 GESTIÓN DE ERRORES: -✅ **Uso de `ApiError` para respuestas de error estructuradas** - - `ExpressController` debe devolver errores con `ApiError` (`status`, `title`, `detail`, `timestamp`). - - `RegisterController` y `LoginController` deben usar `ApiError` en caso de errores (`409 Conflict`, `401 Unauthorized`, `500 Internal Server Error`). - - Se deben capturar errores de **Sequelize** en `BaseRepository` (`UniqueConstraintError`, `ConnectionError`, `TimeoutError`). - - **Errores de unicidad (`SequelizeUniqueConstraintError`) deben ser personalizados según el contexto usando `errorMapper` en los repositorios.** +- `AuthService` debe crearse con un factory como `createAuthService()` +- `AuthService` depende de `IAuthenticatedUserRepository`, `ITransactionManager` y `IAuthProvider` -📌 FLUJO DE LA API `/register`: -1️⃣ **Request llega a `RegisterController`**. -2️⃣ **Valida `email`, `username`, `password` como Value Objects**. -3️⃣ **Llama a `AuthService.registerUser()`**. -4️⃣ **`AuthService` usa `validateUser()` para evitar usuarios duplicados**. -5️⃣ **Si el usuario es nuevo, se cifra la contraseña con `bcrypt.hash()`**. -6️⃣ **Se almacena en la BD con `AuthenticatedUserRepository.create()`**. -7️⃣ **Se generan `accessToken` y `refreshToken` con `PassportAuthProvider`**. -8️⃣ **Se responde con los tokens y el `userId`**. +## Autenticacion con `PassportJS` -📌 FLUJO DE LA API `/login`: -1️⃣ **Request llega a `LoginController`**. -2️⃣ **Llama a `AuthService.loginUser()` con `email` y `password`**. -3️⃣ **`AuthService` busca el usuario en `AuthenticatedUserRepository.findUserByEmail()`**. -4️⃣ **Si el usuario existe, compara contraseñas con `bcrypt.compare()`**. -5️⃣ **Si la contraseña es correcta, genera `accessToken` y `refreshToken`**. -6️⃣ **Devuelve los tokens en la respuesta**. +- `PassportAuthProvider` maneja `JWTStrategy` y `LocalStrategy` +- `validateUser()` verifica credenciales con `LocalStrategy` +- usar `bcrypt` para cifrar contrasenas antes de almacenarlas +- `PasswordHash.create()` valida la fuerza de la contrasena antes de cifrarla +- `bcrypt.compare()` valida la contrasena al hacer login +- las contrasenas nunca deben almacenarse en texto plano -📌 TRANSFORMACIÓN DE RESPUESTAS: -✅ **Los Controladores son responsables de la transformación de datos antes de enviarlos.** -✅ **Se usan `Presenters` (`AuthResponsePresenter`) para mapear datos del dominio a DTOs (`AuthResponseDTO`).** -✅ **Se inyectan `Presenters` en los controladores para desacoplar la lógica de transformación.** +## Gestion de errores -📌 SEGURIDAD Y MEJORES PRÁCTICAS: -✅ **Usar `TransactionManager.complete()` para manejar transacciones.** -✅ **Evitar acoplamientos directos entre servicios y repositorios.** -✅ **Usar `bcrypt` con `SALT_ROUNDS=12` configurables en `.env`.** -✅ **Si ocurre un error en la BD, `rollback()` debe ejecutarse automáticamente.** -✅ **Devolver códigos HTTP adecuados (`401 Unauthorized`, `409 Conflict`, `500 Internal Server Error`).** +- `ExpressController` debe devolver errores con `ApiError` +- `RegisterController` y `LoginController` deben mapear errores a `409`, `401` o `500` segun contrato +- capturar errores de Sequelize relevantes en repositorios +- personalizar errores de unicidad mediante `errorMapper` -📌 SOLICITUDES QUE PUEDO HACERTE: -- **Generar código** cumpliendo con estas reglas. -- **Revisar código y detectar violaciones de SOLID o DDD**. -- **Optimizar `AuthService` para mejorar la seguridad y escalabilidad**. -- **Implementar `/refresh` para renovar `accessTokens`**. -- **Escribir pruebas unitarias y de integración para `registerUser()` y `loginUser()`**. -- **Optimizar `AuthResponsePresenter` para soportar más formatos (`XML`, `CSV`).** +## Flujo de `/register` -⚠️ **IMPORTANTE:** -- **NO generes código que acople dependencias directamente**. -- **NO uses Sequelize directamente en los servicios**. -- **SIEMPRE usa interfaces (`IAuthProvider`, `ITransactionManager`) en lugar de instancias concretas**. +1. Request llega a `RegisterController` +2. valida `email`, `username` y `password` como Value Objects +3. llama a `AuthService.registerUser()` +4. `AuthService` evita usuarios duplicados con `validateUser()` +5. si el usuario es nuevo, cifra la contrasena con `bcrypt.hash()` +6. persiste con `AuthenticatedUserRepository.create()` +7. genera `accessToken` y `refreshToken` +8. responde con tokens y `userId` + +## Flujo de `/login` + +1. Request llega a `LoginController` +2. llama a `AuthService.loginUser()` con `email` y `password` +3. busca el usuario en `AuthenticatedUserRepository.findUserByEmail()` +4. compara contrasenas con `bcrypt.compare()` +5. si la contrasena es correcta, genera tokens +6. devuelve los tokens en la response + +## Transformacion de respuestas + +- los controladores transforman antes de responder +- usar `AuthResponsePresenter` para mapear dominio a `AuthResponseDTO` +- inyectar presenters en controllers cuando ayude a desacoplar + +## Seguridad y mejores practicas + +- usar `TransactionManager.complete()` para manejar transacciones +- evitar acoplamientos directos entre servicios y repositorios +- usar `bcrypt` con `SALT_ROUNDS=12` configurable en `.env` +- si ocurre un error en BD, `rollback()` debe ejecutarse automaticamente + +## Solicitudes tipicas + +- generar codigo cumpliendo estas reglas +- revisar codigo y detectar violaciones de SOLID o DDD +- optimizar `AuthService` para seguridad y escalabilidad +- implementar `/refresh` +- escribir pruebas unitarias y de integracion para `registerUser()` y `loginUser()` + +## Restricciones + +- no acoplar dependencias directamente +- no usar Sequelize directamente en servicios +- usar interfaces como `IAuthProvider` e `ITransactionManager` diff --git a/docs/prompt-customer-invoices.md b/docs/prompt-customer-invoices.md index 64ce6bc5..c754e2aa 100644 --- a/docs/prompt-customer-invoices.md +++ b/docs/prompt-customer-invoices.md @@ -1,22 +1,23 @@ -📌 CONTEXTO DEL PROYECTO: -Estoy desarrollando una API en **Node.js** con **TypeScript**, **Express.js**, **Sequelize**, y **Arquitectura Hexagonal (Ports & Adapters)** bajo los principios de **DDD (Domain-Driven Design)** y **SOLID**. -Este módulo es para **facturas de cliente (Customer Invoices)** y debe cumplir con los estándares definidos previamente para el contexto de autenticación, adaptados a los datos, reglas y operaciones de las facturas. +# Prompt Customer Invoices -📌 PRINCIPIOS Y PATRONES A APLICAR: -✅ **DDD (Domain-Driven Design)** - - Las **facturas** serán agregados raíz (`CustomerInvoice`) con propiedades como `id`, `customerId`, `invoiceNumber`, `date`, `lines`, `totals`, etc. - - Las **líneas de factura** (`InvoiceLine`) se modelan como entidades o value objects dentro del agregado. - - Se usará un `Mapper` para convertir entre dominio y persistencia. - - Repositorios (`ICustomerInvoiceRepository`) solo manejan agregados. - - Operaciones como `createInvoice`, `updateInvoice`, `getInvoiceById` serán gestionadas en `CustomerInvoiceApplicationService`. +Base comun reutilizada: [`backend-prompt-base.md`](./backend-prompt-base.md) -✅ **SOLID** - - Usar SRP: cada clase con una responsabilidad clara. - - Usar interfaces como `ICustomerInvoiceRepository`, `ITransactionManager`, `IInvoicePresenter`. - - Evitar acoplamientos entre capa de aplicación y capa de infraestructura. - - Controladores deben transformar los datos en DTOs antes de enviarlos. +Este prompt añade solo las reglas especificas de `customer-invoices`. -📌 ESTRUCTURA DE DATOS: +## Contexto especifico + +Este modulo es para facturas de cliente y adapta la base comun a datos, reglas y operaciones de facturacion. + +## DDD y SOLID aplicados + +- `CustomerInvoice` es agregado raiz +- `InvoiceLine` se modela como entidad o Value Object dentro del agregado +- usar mapper para dominio <-> persistencia +- `ICustomerInvoiceRepository` maneja agregados +- operaciones como `createInvoice`, `updateInvoice` y `getInvoiceById` viven en `CustomerInvoiceApplicationService` +- usar SRP y contratos como `ICustomerInvoiceRepository`, `ITransactionManager` e `IInvoicePresenter` + +## Estructura de datos La entidad `CustomerInvoice` tendrá: - `id`: UUID (ValueObject `UniqueID`) @@ -37,8 +38,8 @@ La entidad `CustomerInvoice` tendrá: - `tax`: Amount (number) and scale (number) (ValueObject Percentage) - `totalAmount`: Amount (number), scale (number) and currency (string) (ValueObject MoneyValue) +## Operaciones principales -📌 OPERACIONES PRINCIPALES: - `createInvoice(data: CreateCustomerInvoiceDTO)` - `getInvoiceById(id: string)` - `updateInvoice(id: string, changes: UpdateCustomerInvoiceDTO)` @@ -46,39 +47,43 @@ La entidad `CustomerInvoice` tendrá: - `deleteInvoice(id: string)` - `markAsPaid(id: string)` -📌 VALIDACIONES: -✅ Usar `Zod` para validaciones en Value Objects (`InvoiceNumber`, `InvoiceDate`, `InvoiceLine`). -✅ No se deben lanzar excepciones para errores de validación. Usar `Result` como en autenticación. +## Validaciones -📌 PRESENTACIÓN: -✅ Los controladores convierten el agregado `CustomerInvoice` a `CustomerInvoiceDTO`. -✅ Se usan `Presenters` (`CustomerInvoicePresenter`) para construir los DTOs. -✅ DTOs solo exponen campos necesarios: nada sensible, nada interno. -✅ Se puede extender con `CsvInvoicePresenter`, `PdfInvoicePresenter`, etc. +- usar `Zod` en Value Objects como `InvoiceNumber`, `InvoiceDate` e `InvoiceLine` +- no lanzar excepciones para errores de validacion esperados; usar `Result` -📌 AUTORIZACIÓN Y ACCESO: -✅ Se protegerán rutas con middleware JWT (`authenticateJWT`). -✅ Se validará que el usuario autenticado pueda acceder/modificar la factura (p. ej. pertenece a su empresa). +## Presentacion -📌 PERSISTENCIA: -✅ `SequelizeCustomerInvoiceRepository` no accede directamente al modelo de Sequelize, lo hace a través de `Mapper`. -✅ La clase `CustomerInvoiceMapper` mapea de/agregado <-> persistencia. -✅ Soporta transacciones Sequelize (`TransactionManager.complete(...)`). -✅ Los nombres de los campos y las tablas en la base de datos seguirán la notación "snake_case". -✅ Los nombres de los campos en los modelos Sequelize seguirán la notación "snake_case". -✅ Los modelos Sequelize de los agregados tienen campos 'timestamp' con los nombres "created_at", "updated_at" y "deleted_at". +- los controladores convierten el agregado a `CustomerInvoiceDTO` +- usar `CustomerInvoicePresenter` para construir DTOs +- DTOs solo exponen campos necesarios +- se puede extender con `CsvInvoicePresenter`, `PdfInvoicePresenter`, etc. +## Autorizacion y acceso -📌 ERRORES: -✅ Usar `ApiError` para errores en la API. -✅ Los repositorios deben capturar errores de Sequelize (`UniqueConstraintError`, etc.) y convertirlos a errores de dominio con mensajes claros y específicos (mediante `errorMapper`). +- proteger rutas con middleware JWT o equivalente vigente del modulo +- validar que el usuario autenticado pueda acceder o modificar la factura de su empresa -📌 TESTING: -✅ Los servicios (`CustomerInvoiceApplicationService`) serán testeados con mocks de repositorio. -✅ Las rutas serán testeadas con `supertest`. -✅ Las validaciones de ValueObjects tendrán pruebas unitarias. +## Persistencia + +- `SequelizeCustomerInvoiceRepository` accede mediante mapper +- `CustomerInvoiceMapper` mapea agregado <-> persistencia +- soportar transacciones con `TransactionManager.complete(...)` +- seguir `snake_case` en tablas, columnas y timestamps `created_at`, `updated_at`, `deleted_at` + +## Errores + +- usar `ApiError` para errores de API +- capturar errores de Sequelize y convertirlos con mensajes claros mediante `errorMapper` + +## Testing + +- testear `CustomerInvoiceApplicationService` con mocks de repositorio +- testear rutas con `supertest` +- cubrir Value Objects con pruebas unitarias + +## Nomenclatura -📌 NOMENCLATURA: - Agregado: `CustomerInvoice` - Línea: `CustomerInvoiceLine` - DTOs: `CustomerInvoiceDTO`, `InvoiceLineDTO` @@ -86,8 +91,9 @@ La entidad `CustomerInvoice` tendrá: - Repositorio: `ICustomerInvoiceRepository`, `SequelizeCustomerInvoiceRepository` - ValueObjects: `CustomerInvoiceNumber`, `CustomerInvoiceDate`, `CustomerInvoiceTotal`, `CustomerInvoiceItemQuantity`, etc. -⚠️ IMPORTANTE: -- NO incluir `passwords`, `tokens`, o datos sensibles en respuestas. -- NO usar Sequelize directamente en servicios ni controladores. -- NO devolver el agregado como respuesta bruta. Usar DTOs. -- TODO debe estar tipado, incluso los métodos de los servicios (`Promise>`). +## Restricciones + +- no incluir `passwords`, `tokens` u otros datos sensibles en responses +- no usar Sequelize directamente en servicios ni controllers +- no devolver el agregado bruto; usar DTOs +- tipar explicitamente metodos y resultados