FactuGES_ERP/docs/settings/frontend-erp-settings-module-integration-design.md
David 78f5236fe9 feat(series-documentales): implementar la página de configuración de series documentales con funcionalidad CRUD
* Añadir el componente `DocumentSeriesSettingsPage` para gestionar las series documentales.
* Crear diálogos para añadir, editar, avanzar y deshabilitar series documentales.
* Implementar esquemas de validación de formularios para crear y editar series documentales.
* Añadir funciones de utilidad para construir los payloads de las peticiones de las operaciones sobre series documentales.
* Escribir pruebas unitarias para los esquemas de formularios y las funciones de utilidad.
* Implementar pruebas end-to-end para la configuración de series documentales, cubriendo la creación, edición, establecimiento como predeterminada, avance y deshabilitación.
2026-09-18 22:57:37 +02:00

805 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Frontend ERP – Integración de módulos con Settings
## 1. Estado del documento
Estado: **DISEÑO V1 AJUSTADO A LA ARQUITECTURA REAL**
Este documento complementa **Frontend ERP – Sistema modular de Settings** y define cómo un módulo frontend publica una opción de configuración empresarial.
La V1 se ajusta al funcionamiento real del monorepo:
```txt
IModuleClient[]
→ routing modular existente
→ shell de aplicación
```
Settings amplía metadata de módulo; no crea un segundo sistema de módulos ni un router paralelo.
---
## 2. Principio operativo
```txt
Módulo
→ declara una entry de Settings en su manifest
→ registra su propia route
→ implementa su propia Page
→ mantiene su propia persistencia
Shell
→ agrega entries
→ agrupa por sección central
→ muestra navegación
```
La integración entre shell y módulo se realiza por metadata + ruta.
Nunca por import directo de una Page desde `apps/web`.
---
## 3. Alcance
Este documento aplica a configuraciones de la empresa activa publicadas bajo:
```txt
/settings
```
No aplica a:
```txt
/account
preferencias personales
localStorage de UI
configuración técnica del frontend
```
---
## 4. Arquitectura de módulo existente
La arquitectura base de los módulos permanece intacta.
```txt
[module]/
├── manifest.ts
├── [module]-routes.tsx
├── index.ts
├── shared/
└── casos de uso
```
Para una configuración:
```txt
Route
→ Page
→ Page Controller
→ Controller
→ shared/hooks
→ shared/api
```
Flujo de datos:
```txt
API
→ DTO
→ Adapter
→ Entity
→ Hook
→ Controller
→ Page Controller
→ UI
```
Settings no introduce excepciones a estas reglas.
---
## 5. Manifest como extension point
Los manifests reales implementan `IModuleClient`.
Actualmente el contrato ya expone metadata del módulo y `routes()`.
V1 añade metadata opcional de Settings.
Contrato conceptual mínimo:
```ts
export interface ModuleSettingsEntryContribution {
id: string;
sectionId: string;
labelKey: string;
path: string;
}
export interface ModuleSettingsContribution {
entries: ModuleSettingsEntryContribution[];
}
export interface IModuleClient {
// contrato existente
settings?: ModuleSettingsContribution;
}
```
El shape definitivo debe implementarse respetando el fichero real:
```txt
modules/core/src/web/lib/modules/module-client.interface.ts
```
No duplicar `ModuleMetadata`, routing ni tipos ya existentes.
---
## 6. Qué declara un módulo
Una entry V1 contiene únicamente:
```txt
id
sectionId
labelKey
path
```
Ejemplo conceptual:
```ts
export const CustomerInvoicesModuleManifest: IModuleClient = {
// metadata y routes existentes
settings: {
entries: [
{
id: "verifactu",
sectionId: "billing",
labelKey: "settings.verifactu.title",
path: "/settings/billing/verifactu",
},
],
},
};
```
No añadir en V1:
```txt
descriptionKey
order
permission
component
icon
loader
featureFlag
badge
```
salvo que una necesidad real aparezca durante el piloto y se documente antes.
---
## 7. Secciones
Los módulos **no declaran secciones en V1**.
Las secciones son un catálogo del shell.
Ejemplo:
```txt
company
billing
```
Un módulo solo referencia:
```ts
sectionId: "billing";
```
Esto permite:
```txt
customer-invoices
→ billing / VeriFactu
document-series
→ billing / Series documentales
```
sin imports entre módulos y sin duplicar metadata de sección.
Si un módulo usa un `sectionId` inexistente, la agregación debe detectarlo en desarrollo/test.
---
## 8. i18n de la entry
El proyecto registra traducciones por namespace de módulo.
Por tanto:
```txt
manifest.name
→ namespace i18n
entry.labelKey
→ key dentro de ese namespace
```
Ejemplo:
```ts
labelKey: "settings.verifactu.title";
```
No es necesario repetir el namespace en la key si el agregador ya conoce el módulo propietario.
Las labels de las secciones pertenecen al shell.
---
## 9. Routing
La entry de Settings no contiene un componente React.
Incorrecto:
```ts
{
id: "verifactu",
component: VerifactuSettingsPage,
}
```
Correcto:
```ts
{
id: "verifactu",
path: "/settings/billing/verifactu",
}
```
El mismo módulo debe registrar una route real para ese path mediante su infraestructura actual.
La entry y la route deben permanecer alineadas.
---
## 10. Subtree `/settings/*`
El router actual todavía no dispone de un `SettingsLayout` compartido.
La V1 del shell debe añadir esa capacidad.
Desde el punto de vista del módulo, la regla es:
> Registrar una route compatible con el namespace `/settings/...` y con el mecanismo que el shell defina para montar esas routes bajo `SettingsLayout`.
Implementación V1:
```txt
buildSettingsRoute(...)
→ rama /settings
→ SettingsLayout
→ buildModuleRoutes(..., pathPrefix: "settings")
```
Las routes de módulos bajo `/settings/...` se recortan y anidan bajo el shell sin crear un router paralelo.
No crear dentro del módulo:
```txt
SettingsRouter
nested RouterProvider
routing alternativo
```
---
## 11. Ubicación de código
No se impone una nueva estructura rígida en V1.
El código debe respetar la convención real del módulo.
Una posible organización, si encaja con el módulo, es:
```txt
[module]/src/web/
├── manifest.ts
├── [module]-routes.tsx
└── settings/
└── [setting]/
├── controllers/
├── entities/
├── ui/
└── index.ts
```
Esto es una recomendación, no una norma estructural.
Si el módulo ya posee una organización equivalente más consistente, debe reutilizarse.
---
## 12. Page Controller
La Page Controller mantiene las responsabilidades normales:
```txt
leer contexto de routing si aplica
componer controllers
exponer estado y acciones a Page
```
No debe:
```txt
consultar DTOs
llamar API directamente
conocer buildSettingsEntries()
conocer SettingsNavigation
```
Los controllers de dominio siguen sin usar `useParams` ni `useNavigate`.
---
## 13. Controller
Responsable de:
```txt
estado del caso de uso
formulario
builders/Params
submit
validaciones de flujo
side effects
coordinación con shared hooks
```
No debe conocer:
```txt
IModuleClient
Settings shell
metadata de navegación
router
DTO
```
---
## 14. Shared
El hecho de estar bajo `/settings` no altera `shared/`.
```txt
shared/api
→ transporte remoto
shared/adapters
→ DTO → Entity
shared/entities
→ contratos frontend
shared/hooks
→ React Query
```
No ubicar metadata de Settings en `shared/`.
La metadata pertenece al manifest del módulo.
---
## 15. Empresa activa
Todo setting bajo `/settings` opera sobre la empresa activa.
El módulo debe reutilizar:
```txt
IdentityAuthSessionProvider
activeCompanyStorage
X-Company-Id
RequireCompany guard
```
No debe:
```txt
añadir companyId a la URL solo por ser Settings
crear selector local de company
crear un contexto alternativo de company
```
Las operaciones remotas deben seguir el mismo mecanismo de tenant que el resto del ERP.
---
## 16. React Query y cambio de empresa
Las query keys actuales del proyecto no incluyen `companyId`.
El cambio de empresa resetea globalmente React Query.
Un setting debe seguir ese patrón y no introducir una estrategia particular salvo que se rediseñe globalmente la política tenant/cache del ERP.
Criterio:
```txt
cambiar empresa
→ nunca mostrar datos de la empresa anterior
```
---
## 17. Persistencia
Cada setting conserva su persistencia de dominio.
Ejemplos:
```txt
VeriFactu
→ dominio VeriFactu/customer-invoices
Series documentales
→ document-series
Datos empresa
→ identity/company
```
No crear:
```txt
GenericSettingsRepository
settings key/value
localStorage de negocio
```
---
## 18. UI
El shell proporciona:
```txt
SettingsLayout
SettingsNavigation
título/estructura general
```
El módulo proporciona:
```txt
título de la configuración
contenido
formularios
acciones
loading/error
```
No duplicar el sidebar de Settings dentro de la Page del módulo.
Usar el sistema visual Rdx vigente.
---
## 19. Formularios
Aplican las mismas reglas de fields del resto del ERP.
Inputs nativos simples:
```txt
register
```
Controles complejos:
```txt
Controller / useController
```
Mantener:
```txt
Field
FormFieldLabel
FieldDescription
FieldError
```
No crear `GenericSettingsField` ni `GenericSettingsForm`.
---
## 20. Guardado
Cada entry guarda de forma independiente.
Ejemplo:
```txt
/settings/billing/verifactu
→ formulario propio
→ mutation propia
→ persistencia propia
```
No existe un submit global para `/settings`.
---
## 21. Loading y errores
La Page del módulo mantiene responsabilidad sobre:
```txt
loading inicial
error inicial
retry
refetch error
submit error
validation errors
```
Debe usar los patrones generales existentes, como `PageErrorState` y `ErrorAlert`, cuando corresponda.
El shell no interpreta errores de dominio.
---
## 22. Unsaved changes
Cada módulo gestiona sus cambios sin guardar.
No existe un dirty state global de Settings.
---
## 23. Deep linking
La route debe funcionar directamente:
```txt
/settings/billing/verifactu
```
sin visitar antes `/settings`.
Debe ser compatible con:
```txt
refresh
bookmark
deep link
```
---
## 24. Permisos
En V1 no se exige `permission` en metadata porque el frontend todavía no dispone de un sistema operativo de filtrado de navegación/route guards por permiso.
Existe el tipo:
```ts
PermissionCode = string;
```
Cuando se implemente infraestructura frontend real, el contrato podrá evolucionar a:
```ts
permission?: PermissionCode;
```
Reglas futuras:
```txt
frontend
→ oculta navegación no disponible
backend
→ sigue autorizando siempre
```
No implementar permisos específicos de Settings antes de disponer del mecanismo global.
---
## 25. Orden
`order` no forma parte obligatoria de V1.
Mientras exista un único piloto, el shell puede usar orden determinista sencillo.
Cuando dos o más módulos contribuyan realmente a una misma sección, se revisará si hace falta:
```ts
order?: number;
```
No depender del orden accidental de imports.
---
## 26. Caso piloto: VeriFactu
`customer-invoices` es el primer consumidor previsto.
La integración de Settings de VeriFactu debe limitarse inicialmente a:
```txt
manifest entry
route bajo /settings/billing/verifactu
Page
Page Controller
controllers/shared del dominio
```
No trasladar al shell conocimiento de:
```txt
obligada/exenta
renovación
certificados
AEAT
régimen
calificación
causas de exención
```
Todo eso se diseñará en el documento específico de VeriFactu.
---
## 27. Segundo consumidor recomendado
Después de VeriFactu, integrar una entry real de otro módulo, preferentemente `document-series`.
Objetivo:
```txt
billing
├── VeriFactu customer-invoices
└── Series documentales document-series
```
Este segundo consumidor valida que:
- las secciones centrales funcionan;
- las entries son realmente modulares;
- no hay imports cruzados;
- la agregación no depende de un único módulo.
Solo entonces evaluar `order`, conflictos u otras extensiones.
---
## 28. Checklist para añadir una entry
### Manifest
- [ ] Usa `settings.entries`.
- [ ] `id` es estable y semántico.
- [ ] `sectionId` existe en el catálogo del shell.
- [ ] `labelKey` existe en el namespace del módulo.
- [ ] `path` es absoluto y estable.
- [ ] No registra un componente React.
### Routing
- [ ] Existe una route real que corresponde a `path`.
- [ ] Funciona por deep-link.
- [ ] Se integra con el subtree `/settings/*`.
- [ ] No crea un RouterProvider propio.
### Arquitectura
- [ ] API → DTO → Adapter → Entity → Hook → Controller → Page Controller → UI.
- [ ] No hay DTOs en controller/UI.
- [ ] No hay routing en controller.
- [ ] `shared/` no conoce Settings.
- [ ] El dominio no conoce `IModuleClient`.
### Empresa
- [ ] Usa empresa activa existente.
- [ ] No añade `companyId` a la route.
- [ ] No introduce selector alternativo.
- [ ] Cambiar empresa no conserva datos stale.
### UI
- [ ] No duplica `SettingsNavigation`.
- [ ] Usa Rdx cuando corresponda.
- [ ] Soporta teclado y `focus-visible`.
- [ ] Maneja loading/error correctamente.
### Persistencia
- [ ] Usa persistencia del dominio.
- [ ] No crea storage genérico de Settings.
- [ ] No usa localStorage para configuración empresarial.
---
## 29. Anti-patterns
No hacer:
```txt
apps/web importa VerifactuSettingsPage
module settings declara component
SettingsRouter paralelo
SettingsRegistry con estado global
secciones declaradas por cada módulo en V1
GenericSettingsForm
GenericSettingsRepository
SettingsProvider global
companyId duplicado en URL
selector de empresa dentro del setting
imports entre módulos para agrupar entries
switch(setting.id) para renderizar Pages
```
---
## 30. Criterio de validación
La integración de un módulo es correcta si:
```txt
1. el shell descubre su entry desde IModuleClient;
2. el shell no importa su Page;
3. la route pertenece al módulo;
4. el módulo no conoce SettingsNavigation;
5. shared no conoce Settings;
6. los datos pertenecen a la empresa activa;
7. el backend conserva la autorización;
8. la persistencia sigue siendo del dominio;
9. eliminar el módulo elimina su entry;
10. añadir otra entry no obliga a modificar SettingsLayout.
```
---
## 31. Regla mental final
```txt
Manifest
→ declara que la configuración existe
Shell
→ la muestra
Router
→ lleva hasta ella
Módulo
→ la implementa
Dominio
→ la persiste
```
Si para añadir una Page de configuración hay que modificar `SettingsLayout`, importar su componente en `apps/web` o añadir un `switch` central, la integración está mal diseñada.
## 32. Referencia: series documentales
`document-series` publica la entrada `Series documentales` en la sección Billing y conserva la ruta y la administración completa en su módulo. La ruta es `/settings/billing/document-series`; la pantalla no recibe ni muestra `companyId` o `branchId`, pues usa el contexto de empresa activo. Su UI combina listado server-side y diálogos de alta, edición, avance de contador y confirmación de disable sin que el shell de Settings conozca sus contratos ni reglas.