Centro médico activo
Además de la empresa, la sesión tiene un centro médico activo que acota lo que se ve y lo que se crea. Es un scoping ortogonal al de multi-tenant: ambos son fail-closed y ambos pueden producir el mismo síntoma.
El flujo completo
GET /v1/health-center/minedevuelve los centros del usuario. No requiere permiso: cualquier usuario autenticado necesita conocer sus centros para poder elegir uno.ActiveHealthCenterService(frontend) guardacenters,activeCenterIdyactiveCentercomo signals, y persiste la selección en localStorage.healthCenterInterceptorlee ese localStorage y agrega el headerX-Health-Center-Ida cada request.- El backend lo pone en
req.userData.healthCenterId, lo valida contra la membresía del usuario y lo expone al service víaSecuredController.getHealthCenterId(req). - Los episodios y los insumos llevan
HEALTHCENTERID, y las bandejas filtran por él.
Autoselección
Solo se autoselecciona si el usuario tiene un único centro. Con varios queda sin elegir a propósito:
el usuario multi-centro debe decidir explícitamente, y un ADMIN ve todos.
Fail-closed
NO_HEALTH_CENTER_SCOPE = -1, igual que el de empresa:
| Valor | Significado |
|---|---|
null | Ve todos los centros (para SUPERUSER / ADMIN sin uno puntual seleccionado) |
-1 | No ve nada |
El módulo
src/modules/health-center/ — entidades HealthCenter y HealthCenterServiceLine (las líneas de
atención del centro), un controller y dos services. Es el molde limpio del
patrón de módulo de feature: si vas a escribir un módulo nuevo, copiá
este.
- Pantallas:
health-centersyhealth-centers/:id. - Permisos:
HEALTHCENTER_ABM_VIEW,HEALTHCENTER_DETAIL_VIEW, etc. - El código del centro tiene unicidad case-insensitive y a prueba de espacios: el índice normaliza con
UPPER(TRIM(...)).
Gotchas
X-Health-Center-Id debe estar en la allowlist de CORS del backend y en nginx. Si falta en
cualquiera de los dos, todos los requests fallan por preflight.
El DDL va en orden: centros → usuarios → columnas en episodios/insumos → backfill del centro por defecto. Sin el backfill, los episodios y los insumos previos quedan sin centro y desaparecen de la bandeja.
- Al cerrar sesión hay que llamar
reset(). Si no, el centro del usuario anterior queda en localStorage y se manda con la sesión nueva. El backend lo rechaza por membresía, pero el usuario ve cero filas sin entender por qué. - Es un scoping distinto del de empresa. Un usuario puede tener la empresa correcta y el centro incorrecto: el síntoma es idéntico ("no veo nada"). Chequeá los dos.
- La validación de aplicación tiene que espejar la expresión del índice único exactamente. Si el índice
normaliza con
UPPER(TRIM(...))y el service compara el valor crudo, el usuario ve un error23505de Postgres en vez del mensaje de negocio — y hay que validar también en el update, no solo en el create.