Docs
This commit is contained in:
parent
0b07382919
commit
cc14cdacf1
29
README.md
29
README.md
@ -1 +1,28 @@
|
|||||||
Monorepo con cliente, servidor y packages modulares.
|
# ERP Monorepo
|
||||||
|
|
||||||
|
Monorepo con aplicaciones, servidor y paquetes modulares para el ERP.
|
||||||
|
|
||||||
|
## Estado actual
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
## Documentación clave
|
||||||
|
|
||||||
|
- [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)
|
||||||
|
|
||||||
|
## Reglas rápidas
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
|||||||
74
apps/web/README.md
Normal file
74
apps/web/README.md
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
# `apps/web`
|
||||||
|
|
||||||
|
Aplicación frontend principal del ERP.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## 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
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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`.
|
||||||
|
|
||||||
|
## App company switcher
|
||||||
|
|
||||||
|
Componente:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
apps/web/src/layout/app-company-switcher.tsx
|
||||||
|
```
|
||||||
|
|
||||||
|
Propósito:
|
||||||
|
|
||||||
|
- mostrar la empresa activa real en el layout autenticado
|
||||||
|
- permitir cambiar entre `availableCompanies` de la sesión actual
|
||||||
|
|
||||||
|
Reglas:
|
||||||
|
|
||||||
|
- usar el contexto de `identity`
|
||||||
|
- no manejar tokens
|
||||||
|
- no llamar directamente a `GET /companies/available`
|
||||||
|
- no usar `companySlug` para auth
|
||||||
|
- no modificar el access token
|
||||||
|
|
||||||
|
## Refresh automático
|
||||||
|
|
||||||
|
La app implementa refresh automático global sobre `401` con retry único por request y `refreshPromise` compartida.
|
||||||
|
|
||||||
|
Si el refresh falla:
|
||||||
|
|
||||||
|
- limpiar sesión
|
||||||
|
- limpiar empresa activa persistida
|
||||||
|
- redirigir a `/login`
|
||||||
@ -20,6 +20,10 @@ docs/
|
|||||||
- [Estado de migración a identity](./architecture/identity-migration-status.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)
|
- [Flujo frontend de auth y selección de empresa](./frontend/auth-and-company-selection.md)
|
||||||
- [Seed local de admin](./dev/seed-local-admin.sql)
|
- [Seed local de admin](./dev/seed-local-admin.sql)
|
||||||
|
- [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)
|
||||||
|
|
||||||
## Estado actual resumido
|
## Estado actual resumido
|
||||||
|
|
||||||
@ -33,4 +37,5 @@ docs/
|
|||||||
- `modules/auth` debe considerarse legacy/deprecated para backend nuevo.
|
- `modules/auth` debe considerarse legacy/deprecated para backend nuevo.
|
||||||
- `GET /companies/available` es el endpoint vigente para cargar companies accesibles.
|
- `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 /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`.
|
- Las rutas tenant-scoped deben usar `Authorization` + `X-Company-Id`.
|
||||||
|
|||||||
@ -17,8 +17,8 @@ Gestiona:
|
|||||||
- session
|
- session
|
||||||
- `RefreshToken`
|
- `RefreshToken`
|
||||||
- `CompanyMembership`
|
- `CompanyMembership`
|
||||||
|
- acceso `account-company`
|
||||||
- roles/permisos futuros
|
- roles/permisos futuros
|
||||||
- validación de acceso `accountId + companyId`
|
|
||||||
|
|
||||||
Endpoints activos:
|
Endpoints activos:
|
||||||
|
|
||||||
@ -83,6 +83,7 @@ INDEX(tin)
|
|||||||
- `Company` vive en `companies`.
|
- `Company` vive en `companies`.
|
||||||
- No mover memberships a `companies`.
|
- No mover memberships a `companies`.
|
||||||
- No evolucionar `identity.Company` como ficha operativa ERP.
|
- No evolucionar `identity.Company` como ficha operativa ERP.
|
||||||
|
- No usar `customers` para representar la empresa tenant.
|
||||||
|
|
||||||
## Servicios públicos
|
## Servicios públicos
|
||||||
|
|
||||||
@ -133,6 +134,8 @@ GET /companies/available
|
|||||||
Authorization: Bearer <access_token>
|
Authorization: Bearer <access_token>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Tampoco debe documentarse ni introducirse `GET /account/companies` como variante válida.
|
||||||
|
|
||||||
## Dependencias permitidas
|
## Dependencias permitidas
|
||||||
|
|
||||||
Para módulos tenant-scoped que usan `requireIdentityTenant(params)`, la dependencia explícita debe ser hacia:
|
Para módulos tenant-scoped que usan `requireIdentityTenant(params)`, la dependencia explícita debe ser hacia:
|
||||||
|
|||||||
@ -10,6 +10,21 @@ GET /identity/auth/session
|
|||||||
GET /companies/available
|
GET /companies/available
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Endpoints que no forman parte del diseño vigente
|
||||||
|
|
||||||
|
No documentar ni crear como ruta válida:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /identity/companies
|
||||||
|
GET /account/companies
|
||||||
|
```
|
||||||
|
|
||||||
|
Motivo:
|
||||||
|
|
||||||
|
- `identity` no debe devolver fichas completas de empresa.
|
||||||
|
- `companies` ya depende de `identity` para autenticación.
|
||||||
|
- Un endpoint de ese tipo introduciría el ciclo `identity -> companies -> identity`.
|
||||||
|
|
||||||
## POST `/identity/auth/login`
|
## POST `/identity/auth/login`
|
||||||
|
|
||||||
### Request
|
### Request
|
||||||
@ -159,6 +174,7 @@ export type AvailableCompanyDTO = {
|
|||||||
- no requiere `X-Company-Id`
|
- no requiere `X-Company-Id`
|
||||||
- usa `requireIdentityAuthenticated(params)`
|
- usa `requireIdentityAuthenticated(params)`
|
||||||
- devuelve solo companies activas accesibles para la cuenta autenticada
|
- devuelve solo companies activas accesibles para la cuenta autenticada
|
||||||
|
- no modifica sesión ni access token
|
||||||
|
|
||||||
## Errores esperados
|
## Errores esperados
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,10 @@
|
|||||||
-- Purpose:
|
-- Purpose:
|
||||||
-- Creates a local admin account, one active company and the
|
-- Creates a local admin account, one active company and the
|
||||||
-- active membership between them.
|
-- active membership between them.
|
||||||
|
-- Tables touched:
|
||||||
|
-- - identity_accounts
|
||||||
|
-- - companies
|
||||||
|
-- - identity_company_memberships
|
||||||
--
|
--
|
||||||
-- Intended use:
|
-- Intended use:
|
||||||
-- Local/dev environments only.
|
-- Local/dev environments only.
|
||||||
|
|||||||
@ -118,6 +118,36 @@ export type AvailableCompaniesResponseDTO = {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## App company switcher
|
||||||
|
|
||||||
|
Componente documentado:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
apps/web/src/layout/app-company-switcher.tsx
|
||||||
|
```
|
||||||
|
|
||||||
|
Propósito:
|
||||||
|
|
||||||
|
- cambiar de empresa activa una vez iniciada la sesión
|
||||||
|
- mostrar la empresa activa actual dentro del layout autenticado
|
||||||
|
|
||||||
|
Reglas:
|
||||||
|
|
||||||
|
- consume `availableCompanies` y `activeCompany` desde `useIdentityAuthSession()`
|
||||||
|
- usa `changeActiveCompany()` o `selectActiveCompany()` del contexto, no requests directas
|
||||||
|
- no llama directamente a `GET /companies/available` si el contexto ya está hidratado
|
||||||
|
- no maneja tokens
|
||||||
|
- no usa `@erp/auth/client`
|
||||||
|
- no usa `companySlug` para autenticación
|
||||||
|
- no modifica el access token
|
||||||
|
- al cambiar empresa, las siguientes requests tenant-scoped deben salir con el nuevo `X-Company-Id`
|
||||||
|
|
||||||
|
Comportamiento esperado:
|
||||||
|
|
||||||
|
- `0` empresas: no rompe; render deshabilitado o equivalente
|
||||||
|
- `1` empresa: muestra la empresa activa; no hace falta selector operativo
|
||||||
|
- `N` empresas: permite cambiar entre las permitidas y marca la activa
|
||||||
|
|
||||||
## Headers
|
## Headers
|
||||||
|
|
||||||
### No deben llevar `X-Company-Id`
|
### No deben llevar `X-Company-Id`
|
||||||
@ -172,3 +202,14 @@ Comportamiento:
|
|||||||
/identity/auth/session
|
/identity/auth/session
|
||||||
/companies/available
|
/companies/available
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Pruebas manuales recomendadas
|
||||||
|
|
||||||
|
1. Login con usuario sin empresas: debe ir a `/no-companies`.
|
||||||
|
2. Login con usuario con una empresa: debe auto-seleccionarse y entrar al área privada.
|
||||||
|
3. Login con usuario con varias empresas: debe mostrarse `/company-selection`.
|
||||||
|
4. Cambio de empresa desde `app-company-switcher`: la request tenant-scoped siguiente debe llevar el nuevo `X-Company-Id`.
|
||||||
|
5. Access token expirado con refresh válido: debe ejecutarse refresh + retry único.
|
||||||
|
6. Refresh token inválido: debe limpiarse la sesión y redirigir a `/login`.
|
||||||
|
7. Endpoints auth y `GET /companies/available`: no deben llevar `X-Company-Id`.
|
||||||
|
8. Rutas tenant-scoped: deben llevar `X-Company-Id` cuando exista `activeCompanyId`.
|
||||||
|
|||||||
@ -14,3 +14,5 @@ Importante:
|
|||||||
|
|
||||||
- Este paquete sigue exponiendo `@erp/auth/client`, por lo que no debe retirarse todavía.
|
- Este paquete sigue exponiendo `@erp/auth/client`, por lo que no debe retirarse todavía.
|
||||||
- No cambiar ni borrar exports legacy mientras `supplier` siga sin migrarse.
|
- No cambiar ni borrar exports legacy mientras `supplier` siga sin migrarse.
|
||||||
|
- No usar `mockUser` ni middlewares legacy de auth en backend nuevo.
|
||||||
|
- No borrar `modules/auth` hasta retirar consumidores legacy reales.
|
||||||
|
|||||||
78
modules/companies/README.md
Normal file
78
modules/companies/README.md
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# `modules/companies`
|
||||||
|
|
||||||
|
`companies` es el módulo independiente y fuente de verdad de la ficha operativa de empresa.
|
||||||
|
|
||||||
|
## Responsabilidades
|
||||||
|
|
||||||
|
Gestiona la entidad `Company` funcional del ERP:
|
||||||
|
|
||||||
|
- datos legales
|
||||||
|
- datos operativos básicos
|
||||||
|
- `slug`
|
||||||
|
- `status`
|
||||||
|
|
||||||
|
Separación arquitectónica:
|
||||||
|
|
||||||
|
- `modules/identity`: autenticación, cuentas, memberships, acceso account-company
|
||||||
|
- `modules/companies`: ficha real de empresa y validación de company activa
|
||||||
|
|
||||||
|
## Tabla `companies`
|
||||||
|
|
||||||
|
Campos actuales:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
id
|
||||||
|
legal_name
|
||||||
|
trade_name
|
||||||
|
tin
|
||||||
|
slug
|
||||||
|
email
|
||||||
|
phone
|
||||||
|
website
|
||||||
|
status
|
||||||
|
created_at
|
||||||
|
updated_at
|
||||||
|
```
|
||||||
|
|
||||||
|
Índices esperados:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
UNIQUE(slug)
|
||||||
|
INDEX(status)
|
||||||
|
INDEX(tin)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Servicios públicos
|
||||||
|
|
||||||
|
El módulo expone:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
companies:general
|
||||||
|
```
|
||||||
|
|
||||||
|
Responsabilidad:
|
||||||
|
|
||||||
|
- validar existencia y estado activo de la company
|
||||||
|
- devolver datos públicos/operativos de empresa
|
||||||
|
|
||||||
|
No debe asumir:
|
||||||
|
|
||||||
|
- memberships
|
||||||
|
- autenticación por cuenta
|
||||||
|
- roles/permisos de identity
|
||||||
|
|
||||||
|
## Dependencias
|
||||||
|
|
||||||
|
`modules/companies` depende de `identity`.
|
||||||
|
|
||||||
|
Uso actual:
|
||||||
|
|
||||||
|
- sus endpoints usan `requireIdentityAuthenticated(params)`
|
||||||
|
- `GET /companies/available` no usa `requireIdentityTenant(params)`
|
||||||
|
|
||||||
|
## Reglas
|
||||||
|
|
||||||
|
- `CompanyMembership` vive en `identity`.
|
||||||
|
- No mover memberships a `companies`.
|
||||||
|
- No usar `customers` para representar la empresa tenant.
|
||||||
|
- `companySlug` no debe venir de token, `X-Company-Slug` ni `req.user.companySlug`.
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import type { IDocumentProperties, IDocumentPropertiesFactory } from "@erp/core/api";
|
import type { IDocumentProperties, IDocumentPropertiesFactory } from "@erp/core/api";
|
||||||
|
|
||||||
import type { IssuedInvoiceReportSnapshot } from "../application-models";
|
import type { IssuedInvoiceReportSnapshot } from "../models";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construye los metadatos del documento PDF de una factura emitida.
|
* Construye los metadatos del documento PDF de una factura emitida.
|
||||||
|
|||||||
@ -1,14 +1,123 @@
|
|||||||
# Identity Module
|
# `modules/identity`
|
||||||
|
|
||||||
`identity` convivirá temporalmente con `@erp/auth`.
|
`identity` es el runtime nuevo de autenticación, sesión y acceso por memberships del ERP.
|
||||||
|
|
||||||
`@erp/auth` sigue siendo el runtime de autenticación actual.
|
## Responsabilidades
|
||||||
|
|
||||||
`identity` incorporará progresivamente la implementación V1 aprobada:
|
Gestiona:
|
||||||
- `email/password`
|
|
||||||
- `access token`
|
|
||||||
- `refresh token` persistido hasheado
|
|
||||||
- tenant activo mediante `X-Company-Id`
|
|
||||||
- roles y permisos company-scoped
|
|
||||||
|
|
||||||
No se deben registrar middlewares ni reemplazar dependencias de `@erp/auth` hasta que `identity` tenga implementación funcional validada.
|
- `Account`
|
||||||
|
- login
|
||||||
|
- refresh
|
||||||
|
- logout
|
||||||
|
- session
|
||||||
|
- `RefreshToken`
|
||||||
|
- `CompanyMembership`
|
||||||
|
- acceso `account-company`
|
||||||
|
- roles/permisos futuros
|
||||||
|
|
||||||
|
No gestiona:
|
||||||
|
|
||||||
|
- la ficha operativa completa de empresa
|
||||||
|
- `companySlug` en auth
|
||||||
|
- el tenant dentro del access token
|
||||||
|
|
||||||
|
## Endpoints activos
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /identity/auth/login
|
||||||
|
POST /identity/auth/refresh
|
||||||
|
POST /identity/auth/logout
|
||||||
|
GET /identity/auth/session
|
||||||
|
```
|
||||||
|
|
||||||
|
El endpoint para companies accesibles es:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /companies/available
|
||||||
|
```
|
||||||
|
|
||||||
|
Ese endpoint pertenece al flujo actual, pero no implica que `identity` deba exponer `GET /identity/companies`.
|
||||||
|
|
||||||
|
## Access token
|
||||||
|
|
||||||
|
El access token contiene solo:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
{
|
||||||
|
accountId: string;
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
No debe contener:
|
||||||
|
|
||||||
|
- `companyId`
|
||||||
|
- `companySlug`
|
||||||
|
- roles
|
||||||
|
- permisos
|
||||||
|
|
||||||
|
## Middlewares públicos
|
||||||
|
|
||||||
|
Para rutas autenticadas sin tenant:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
router.use(...requireIdentityAuthenticated(params));
|
||||||
|
```
|
||||||
|
|
||||||
|
Para rutas tenant-scoped:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
router.use(...requireIdentityTenant(params));
|
||||||
|
```
|
||||||
|
|
||||||
|
Código backend nuevo no debe usar:
|
||||||
|
|
||||||
|
- `@erp/auth/api`
|
||||||
|
- `mockUser`
|
||||||
|
- `requireAuthenticated` legacy
|
||||||
|
- `requireCompanyContext` legacy
|
||||||
|
|
||||||
|
## Servicios públicos
|
||||||
|
|
||||||
|
`identity` expone el servicio:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
identity:general
|
||||||
|
```
|
||||||
|
|
||||||
|
Con acceso a `companyAccess` para casos como:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
canAccessCompany(...)
|
||||||
|
findAccessibleCompanyIds(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
Responsabilidad:
|
||||||
|
|
||||||
|
- `identity` decide qué `companyId` puede usar una cuenta según memberships activas.
|
||||||
|
- no devuelve la ficha completa de empresa.
|
||||||
|
|
||||||
|
## Tenant context
|
||||||
|
|
||||||
|
`requireIdentityTenant(params)` compone:
|
||||||
|
|
||||||
|
- `identity:general.companyAccess`
|
||||||
|
- `companies:general.finder`
|
||||||
|
|
||||||
|
Validación esperada:
|
||||||
|
|
||||||
|
1. access token válido
|
||||||
|
2. cuenta autenticable
|
||||||
|
3. `X-Company-Id` presente
|
||||||
|
4. `X-Company-Id` UUID válido
|
||||||
|
5. membership activa
|
||||||
|
6. company activa
|
||||||
|
7. `req.user.companyId` poblado
|
||||||
|
|
||||||
|
## Reglas de mantenimiento
|
||||||
|
|
||||||
|
- Backend nuevo: usar `@erp/identity/api`.
|
||||||
|
- No documentar ni crear `GET /identity/companies`.
|
||||||
|
- No borrar `modules/auth` mientras existan consumidores legacy reales.
|
||||||
|
- `modules/supplier` sigue pendiente de migración si continúa usando el runtime legacy.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user