615 lines
10 KiB
Markdown
615 lines
10 KiB
Markdown
|
|
# Frontend ERP – Integración de módulos con Settings
|
|||
|
|
|
|||
|
|
## 1. Propósito
|
|||
|
|
|
|||
|
|
Este documento complementa **Frontend ERP – Sistema modular de Settings** y define cómo debe integrarse un módulo funcional con `/settings` sin acoplarse al shell de aplicación ni a otros módulos.
|
|||
|
|
|
|||
|
|
Aplica a módulos actuales y futuros que necesiten exponer configuración de la empresa activa.
|
|||
|
|
|
|||
|
|
Ejemplos:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
customer-invoices
|
|||
|
|
→ VeriFactu
|
|||
|
|
|
|||
|
|
document-series
|
|||
|
|
→ Series documentales
|
|||
|
|
|
|||
|
|
identity/company
|
|||
|
|
→ Datos de empresa
|
|||
|
|
|
|||
|
|
módulo de certificados
|
|||
|
|
→ Certificados
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. Principio operativo
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
Módulo
|
|||
|
|
→ declara metadata de Settings
|
|||
|
|
→ registra su propia route
|
|||
|
|
→ implementa su propia Page
|
|||
|
|
→ mantiene su propia persistencia
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
El shell únicamente descubre y presenta la opción.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. Separación de responsabilidades
|
|||
|
|
|
|||
|
|
### Settings shell
|
|||
|
|
|
|||
|
|
Conoce:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
id de sección
|
|||
|
|
label
|
|||
|
|
descripción
|
|||
|
|
orden
|
|||
|
|
path
|
|||
|
|
permiso
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
No conoce:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
DTO
|
|||
|
|
Entity
|
|||
|
|
Controller
|
|||
|
|
formulario
|
|||
|
|
endpoint
|
|||
|
|
reglas de negocio
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Módulo
|
|||
|
|
|
|||
|
|
Conoce:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
routing concreto
|
|||
|
|
Page/Page Controller
|
|||
|
|
controllers
|
|||
|
|
shared/api
|
|||
|
|
shared/adapters
|
|||
|
|
shared/entities
|
|||
|
|
shared/hooks
|
|||
|
|
validación
|
|||
|
|
persistencia
|
|||
|
|
reglas de negocio
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. Flujo completo
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
Module manifest
|
|||
|
|
│
|
|||
|
|
│ settings contribution
|
|||
|
|
▼
|
|||
|
|
SettingsRegistry
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
SettingsNavigation
|
|||
|
|
│
|
|||
|
|
│ usuario navega
|
|||
|
|
▼
|
|||
|
|
/settings/<section>/<entry>
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
Route del módulo
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
Settings Page del módulo
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
Page Controller
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
Controller
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
shared/hooks
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
shared/api
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
La cadena de datos del módulo sigue siendo:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
API → DTO → Adapter → Entity → Hook → Controller → Page Controller → UI
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. Metadata mínima del módulo
|
|||
|
|
|
|||
|
|
Contrato conceptual:
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
export interface ModuleSettingsSectionContribution {
|
|||
|
|
id: string;
|
|||
|
|
labelKey: string;
|
|||
|
|
order?: number;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export interface ModuleSettingsEntryContribution {
|
|||
|
|
id: string;
|
|||
|
|
sectionId: string;
|
|||
|
|
|
|||
|
|
labelKey: string;
|
|||
|
|
descriptionKey?: string;
|
|||
|
|
|
|||
|
|
path: string;
|
|||
|
|
|
|||
|
|
order?: number;
|
|||
|
|
permission?: string;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Ejemplo conceptual:
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
export const customerInvoicesManifest = {
|
|||
|
|
settings: {
|
|||
|
|
sections: [
|
|||
|
|
{
|
|||
|
|
id: "billing",
|
|||
|
|
labelKey: "settings.sections.billing",
|
|||
|
|
order: 200,
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
|
|||
|
|
entries: [
|
|||
|
|
{
|
|||
|
|
id: "verifactu",
|
|||
|
|
sectionId: "billing",
|
|||
|
|
labelKey: "customerInvoices.settings.verifactu.title",
|
|||
|
|
descriptionKey: "customerInvoices.settings.verifactu.description",
|
|||
|
|
path: "/settings/billing/verifactu",
|
|||
|
|
order: 100,
|
|||
|
|
permission: "customerInvoices.settings.verifactu.manage",
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
El shape definitivo debe adaptarse al manifest real del repositorio.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. Regla de routing
|
|||
|
|
|
|||
|
|
Una contribución de Settings no debe registrar un componente React directamente.
|
|||
|
|
|
|||
|
|
Incorrecto:
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
{
|
|||
|
|
id: "verifactu",
|
|||
|
|
component: VerifactuSettingsPage,
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Correcto conceptualmente:
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
{
|
|||
|
|
id: "verifactu",
|
|||
|
|
path: "/settings/billing/verifactu",
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Y en el routing propio del módulo:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
/settings/billing/verifactu
|
|||
|
|
→ VerifactuSettingsPage
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
El shell y el módulo se acoplan únicamente mediante una ruta estable.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. Ubicación de código dentro del módulo
|
|||
|
|
|
|||
|
|
La ubicación concreta debe seguir la estructura real del módulo.
|
|||
|
|
|
|||
|
|
Dirección conceptual recomendada:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
[module]/
|
|||
|
|
├── manifest.ts
|
|||
|
|
├── [module]-routes.tsx
|
|||
|
|
├── shared/
|
|||
|
|
│ ├── api/
|
|||
|
|
│ ├── adapters/
|
|||
|
|
│ ├── entities/
|
|||
|
|
│ └── hooks/
|
|||
|
|
│
|
|||
|
|
└── settings/
|
|||
|
|
└── [setting]/
|
|||
|
|
├── controllers/
|
|||
|
|
├── entities/
|
|||
|
|
├── ui/
|
|||
|
|
└── index.ts
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
No introducir esta estructura si el módulo real ya tiene una convención equivalente mejor establecida.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 8. Page Controller
|
|||
|
|
|
|||
|
|
Una página de settings debe seguir la misma regla que el resto del ERP.
|
|||
|
|
|
|||
|
|
Responsabilidades del Page Controller:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
integrar contexto externo
|
|||
|
|
componer controllers
|
|||
|
|
integrar navegación si aplica
|
|||
|
|
exponer API estructurada a la Page
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
No debe:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
transportar DTO
|
|||
|
|
realizar llamadas API directas
|
|||
|
|
contener lógica visual
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 9. Controller
|
|||
|
|
|
|||
|
|
Responsable de:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
estado del caso de uso
|
|||
|
|
formulario
|
|||
|
|
submit
|
|||
|
|
validaciones de flujo
|
|||
|
|
side effects
|
|||
|
|
coordinación con hooks de shared
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
No debe:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
usar useParams/useNavigate directamente
|
|||
|
|
usar DTO
|
|||
|
|
conocer el SettingsRegistry
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 10. Shared
|
|||
|
|
|
|||
|
|
Una configuración de módulo reutiliza las reglas generales de `shared/`.
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
shared/api
|
|||
|
|
→ transporte remoto
|
|||
|
|
|
|||
|
|
shared/adapters
|
|||
|
|
→ DTO ↔ Entity cuando corresponda
|
|||
|
|
|
|||
|
|
shared/entities
|
|||
|
|
→ contratos frontend
|
|||
|
|
|
|||
|
|
shared/hooks
|
|||
|
|
→ React Query
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
El hecho de que la UI aparezca bajo `/settings` no cambia estas responsabilidades.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 11. Persistencia
|
|||
|
|
|
|||
|
|
Cada módulo decide su persistencia.
|
|||
|
|
|
|||
|
|
No utilizar por defecto un almacenamiento genérico de Settings.
|
|||
|
|
|
|||
|
|
Ejemplos:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
VeriFactu
|
|||
|
|
→ configuración persistida del dominio correspondiente
|
|||
|
|
|
|||
|
|
Series documentales
|
|||
|
|
→ agregado DocumentSeries
|
|||
|
|
|
|||
|
|
Datos de empresa
|
|||
|
|
→ Company/Identity
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
La navegación compartida no implica almacenamiento compartido.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 12. Empresa activa
|
|||
|
|
|
|||
|
|
Toda configuración integrada en `/settings` pertenece a la empresa activa.
|
|||
|
|
|
|||
|
|
Un módulo no debe introducir su propio selector de empresa si la aplicación ya dispone de contexto global.
|
|||
|
|
|
|||
|
|
La query/mutation debe usar la infraestructura existente para resolver la empresa activa.
|
|||
|
|
|
|||
|
|
Cuando cambie la empresa activa:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
los datos visibles deben corresponder a la nueva empresa
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
La implementación exacta depende de cómo estén construidas actualmente las query keys, headers y mecanismos de invalidación.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 13. Permisos
|
|||
|
|
|
|||
|
|
La contribución puede declarar:
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
permission?: string;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
El permiso solo controla la disponibilidad de la navegación frontend.
|
|||
|
|
|
|||
|
|
La operación backend debe seguir autorizándose independientemente.
|
|||
|
|
|
|||
|
|
Un módulo no debe implementar un segundo sistema de permisos para Settings.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 14. i18n
|
|||
|
|
|
|||
|
|
La metadata debe usar claves de traducción.
|
|||
|
|
|
|||
|
|
Ejemplo:
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
labelKey: "customerInvoices.settings.verifactu.title";
|
|||
|
|
descriptionKey: "customerInvoices.settings.verifactu.description";
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
El módulo mantiene sus traducciones.
|
|||
|
|
|
|||
|
|
La sección compartida puede usar una key común del shell si el diseño final establece un catálogo global de secciones.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 15. UI
|
|||
|
|
|
|||
|
|
La página del módulo debe usar el sistema visual Rdx vigente.
|
|||
|
|
|
|||
|
|
El shell proporciona:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
layout
|
|||
|
|
navegación
|
|||
|
|
título general de Configuración
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
El módulo proporciona:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
título específico
|
|||
|
|
contenido
|
|||
|
|
formularios
|
|||
|
|
acciones
|
|||
|
|
loading/error states
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Evitar duplicar dentro del módulo el sidebar global de Settings.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 16. Guardado
|
|||
|
|
|
|||
|
|
Cada setting debe guardar de forma independiente.
|
|||
|
|
|
|||
|
|
Ejemplo:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
VeriFactu
|
|||
|
|
[Cancelar] [Guardar]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
No existe un submit global de `/settings`.
|
|||
|
|
|
|||
|
|
Si la pantalla tiene varios bloques pero forman una única configuración transaccional, el módulo puede resolverlos con un único formulario/controlador.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 17. Loading y errores
|
|||
|
|
|
|||
|
|
El módulo conserva responsabilidad sobre:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
loading inicial
|
|||
|
|
error inicial
|
|||
|
|
refetch error
|
|||
|
|
submit error
|
|||
|
|
validation errors
|
|||
|
|
retry
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
El shell no interpreta errores de dominio.
|
|||
|
|
|
|||
|
|
Debe reutilizarse el patrón consolidado de `PageErrorState`, `ErrorAlert` y errores de formulario cuando corresponda.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 18. Unsaved changes
|
|||
|
|
|
|||
|
|
Cada módulo es responsable de avisar o bloquear navegación cuando existan cambios no guardados.
|
|||
|
|
|
|||
|
|
No crear un estado dirty global dentro del Settings shell.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 19. Deep linking
|
|||
|
|
|
|||
|
|
Una entrada debe ser accesible directamente por URL.
|
|||
|
|
|
|||
|
|
Ejemplo:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
/settings/billing/verifactu
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
El módulo no debe depender de que el usuario haya visitado antes `/settings`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 20. Varias contribuciones a una misma sección
|
|||
|
|
|
|||
|
|
Ejemplo:
|
|||
|
|
|
|||
|
|
### customer-invoices
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
billing
|
|||
|
|
└── VeriFactu
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### document-series
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
billing
|
|||
|
|
└── Series documentales
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Resultado:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
Facturación
|
|||
|
|
├── VeriFactu
|
|||
|
|
└── Series documentales
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Los módulos no se importan entre sí.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 21. Caso piloto: VeriFactu
|
|||
|
|
|
|||
|
|
El primer setting real será VeriFactu.
|
|||
|
|
|
|||
|
|
Su contribución debe limitarse a metadata:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
section
|
|||
|
|
entry
|
|||
|
|
path
|
|||
|
|
orden
|
|||
|
|
permiso si existe
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
La implementación funcional de VeriFactu debe documentarse en un documento específico posterior.
|
|||
|
|
|
|||
|
|
Este documento específico deberá explicar:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
estado/configuración de la empresa
|
|||
|
|
onboarding inicial
|
|||
|
|
valores por defecto de facturación
|
|||
|
|
certificados si pertenecen al mismo dominio
|
|||
|
|
snapshot de IssuedInvoice
|
|||
|
|
API
|
|||
|
|
DTOs
|
|||
|
|
Entities
|
|||
|
|
controllers
|
|||
|
|
UI
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
sin redefinir el framework de Settings.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 22. Checklist para añadir un setting nuevo
|
|||
|
|
|
|||
|
|
### Manifest
|
|||
|
|
|
|||
|
|
- [ ] Declara sección si es necesaria.
|
|||
|
|
- [ ] Declara entry con `id` estable.
|
|||
|
|
- [ ] Usa `labelKey`.
|
|||
|
|
- [ ] Usa `descriptionKey` si aporta valor.
|
|||
|
|
- [ ] Declara `path` estable.
|
|||
|
|
- [ ] Declara `order` cuando importe el orden semántico.
|
|||
|
|
- [ ] Declara `permission` solo si existe permiso real.
|
|||
|
|
|
|||
|
|
### Routing
|
|||
|
|
|
|||
|
|
- [ ] La route pertenece al módulo.
|
|||
|
|
- [ ] Funciona mediante deep-link.
|
|||
|
|
- [ ] No requiere estado previo del shell.
|
|||
|
|
|
|||
|
|
### Arquitectura
|
|||
|
|
|
|||
|
|
- [ ] API → DTO → Adapter → Entity → Hook → Controller → Page Controller → UI.
|
|||
|
|
- [ ] No hay DTO en UI/controller.
|
|||
|
|
- [ ] No hay routing en controller.
|
|||
|
|
- [ ] No hay dependencia del SettingsRegistry en dominio/shared.
|
|||
|
|
|
|||
|
|
### UI
|
|||
|
|
|
|||
|
|
- [ ] Usa sistema visual Rdx.
|
|||
|
|
- [ ] No duplica SettingsNavigation.
|
|||
|
|
- [ ] Soporta teclado y focus-visible.
|
|||
|
|
- [ ] Maneja loading/error/empty correctamente.
|
|||
|
|
|
|||
|
|
### Empresa
|
|||
|
|
|
|||
|
|
- [ ] Usa la empresa activa existente.
|
|||
|
|
- [ ] No introduce un selector paralelo.
|
|||
|
|
- [ ] El cambio de empresa no deja datos stale de la anterior.
|
|||
|
|
|
|||
|
|
### Seguridad
|
|||
|
|
|
|||
|
|
- [ ] La navegación respeta permisos frontend.
|
|||
|
|
- [ ] El backend valida autorización de forma independiente.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 23. Anti-patterns
|
|||
|
|
|
|||
|
|
No hacer:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
importar SettingsPage desde el shell
|
|||
|
|
registrar componentes React como metadata principal
|
|||
|
|
crear un GenericSettingsForm
|
|||
|
|
crear un GenericSettingsRepository
|
|||
|
|
usar localStorage para configuración de negocio
|
|||
|
|
crear otro selector de company
|
|||
|
|
hacer imports entre módulos para agrupar settings
|
|||
|
|
usar un switch central por setting
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 24. Regla mental final
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
El módulo declara que su setting existe.
|
|||
|
|
El shell decide cómo descubrirlo y mostrarlo.
|
|||
|
|
El router decide cómo llegar.
|
|||
|
|
El módulo decide cómo funciona.
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Criterio de validación:
|
|||
|
|
|
|||
|
|
```txt
|
|||
|
|
Si para añadir una nueva configuración hay que modificar SettingsPage,
|
|||
|
|
SettingsNavigation o un switch central, la integración está mal diseñada.
|
|||
|
|
```
|