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)
|
||||
- [Flujo frontend de auth y selección de empresa](./frontend/auth-and-company-selection.md)
|
||||
- [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
|
||||
|
||||
@ -33,4 +37,5 @@ docs/
|
||||
- `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`.
|
||||
|
||||
@ -17,8 +17,8 @@ Gestiona:
|
||||
- session
|
||||
- `RefreshToken`
|
||||
- `CompanyMembership`
|
||||
- acceso `account-company`
|
||||
- roles/permisos futuros
|
||||
- validación de acceso `accountId + companyId`
|
||||
|
||||
Endpoints activos:
|
||||
|
||||
@ -83,6 +83,7 @@ INDEX(tin)
|
||||
- `Company` vive en `companies`.
|
||||
- No mover memberships a `companies`.
|
||||
- No evolucionar `identity.Company` como ficha operativa ERP.
|
||||
- No usar `customers` para representar la empresa tenant.
|
||||
|
||||
## Servicios públicos
|
||||
|
||||
@ -133,6 +134,8 @@ GET /companies/available
|
||||
Authorization: Bearer <access_token>
|
||||
```
|
||||
|
||||
Tampoco debe documentarse ni introducirse `GET /account/companies` como variante válida.
|
||||
|
||||
## Dependencias permitidas
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
## 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`
|
||||
|
||||
### Request
|
||||
@ -159,6 +174,7 @@ export type AvailableCompanyDTO = {
|
||||
- no requiere `X-Company-Id`
|
||||
- usa `requireIdentityAuthenticated(params)`
|
||||
- devuelve solo companies activas accesibles para la cuenta autenticada
|
||||
- no modifica sesión ni access token
|
||||
|
||||
## Errores esperados
|
||||
|
||||
|
||||
@ -6,6 +6,10 @@
|
||||
-- Purpose:
|
||||
-- Creates a local admin account, one active company and the
|
||||
-- active membership between them.
|
||||
-- Tables touched:
|
||||
-- - identity_accounts
|
||||
-- - companies
|
||||
-- - identity_company_memberships
|
||||
--
|
||||
-- Intended use:
|
||||
-- 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
|
||||
|
||||
### No deben llevar `X-Company-Id`
|
||||
@ -172,3 +202,14 @@ Comportamiento:
|
||||
/identity/auth/session
|
||||
/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.
|
||||
- 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 { IssuedInvoiceReportSnapshot } from "../application-models";
|
||||
import type { IssuedInvoiceReportSnapshot } from "../models";
|
||||
|
||||
/**
|
||||
* 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:
|
||||
- `email/password`
|
||||
- `access token`
|
||||
- `refresh token` persistido hasheado
|
||||
- tenant activo mediante `X-Company-Id`
|
||||
- roles y permisos company-scoped
|
||||
Gestiona:
|
||||
|
||||
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