Modelo de datos
MySQL/InnoDB multi-esquema, TypeORM, entidades en src/schemas/v1/<esquema>/ y
src/modules/<mod>/entities/.
Esta página es el mapa y las trampas. La referencia entidad por entidad (columnas, relaciones,
propósito) vive en docs/Obsidian/old/entidades-dominio.md del workspace de Oxitesa.
Los cinco esquemas
| Esquema | Qué guarda |
|---|---|
configuration | Catálogos maestros e identidad: USERS, ROLE, PRODUCTS, ELEMENTS, AGREEMENTS, EntityStatus, CommonEntityDescriptions, MANAGEMENTUNITS, VEHICLE, REPORTS, NOTIFICATIONS, MOBILECONFIG |
customer | PATIENTS, PATIENTINFORMATION, PATIENTSAGREEMENTS, CONTACTNUMBERS |
finances | El núcleo operativo: autorizaciones, remitos, hojas de ruta, almacenes, empleados, vehículos, facturación, PATIENTSUPPLIES y todo el inventario |
callcenter | CONTACTLOGS y el motor de encuestas |
logs | EVENTSLOGS, logs del bot PAMI, REQUESTLOGS, MOBILELOGS, mensajes de WhatsApp |
Cada entidad declara su database derivándolo del directorio donde vive el archivo
(path.basename(path.dirname(__filename))). Mover un archivo de entidad de carpeta le cambia el
esquema en runtime.
Clases base
| Clase | Aporta |
|---|---|
BasicEntity | PK autogenerado, sin auditoría |
LoggeableEntity (sí, con doble e en el nombre del archivo) | id, registerUser, updateUser, creationDate, lastModifiedDate, state → EntityStatus, y userAction (el User en contexto) |
ContactableEntity | LoggableEntity + contactNumbers |
ReportEntity | Entidades de reporte, uso reducido |
Toda mutación de una LoggableEntity produce un EVENTSLOGS. Esa es la auditoría universal del
sistema.
userAction se setea antes de cualquier bifurcaciónSi el service lo deja sin setear antes de guardar, la generación del EventLog explota al leer
this.userAction.currentSecurityLevel, y el usuario ve un genérico "No se pudo guardar el registro
del evento". Ya pasó exactamente eso al agregar una rama de lógica antes de la asignación.
Casing de tablas y columnas
UPPERCASE por defecto (PATIENTS, SUPPLIES, DELIVERYNOTES, CREATIONDATE). Las excepciones
muerden al escribir SQL a mano:
| Excepción | Cómo se escribe |
|---|---|
| Mixed case | EntityStatus, CommonEntityDescriptions, Authorizations, AuthorizationDetails |
| Singular | ROLE, VEHICLE |
Dos patrones que atraviesan todo el modelo
EntityCode — punteros polimórficos
src/schemas/EntityCode.ts define códigos numéricos que, junto a un id, identifican "a qué entidad
apunta esto" sin usar una FK. Los más frecuentes:
0 COMPANY 3 PATIENT 4 SUPPLY 6 WAREHOUSE 7 USER
9 PRODUCT 16 ELEMENT 31 GLOBALENTITY 39 DELIVERYNOTE 63 REPAIRSHOP
Se usa en SUPPLIES.ATENTITYCODE/ATENTITYID, EntityStatus.ENTITYCODE, CONTACTNUMBERS,
CONTACTLOGS, EVENTSLOGS y SIGNATURE.toEntityCode/toEntityId.
EntityStatus — estados por entidad
No hay columnas enum de estado: cada entidad apunta a una fila de configuration.EntityStatus, que
se resuelve por CODE + ENTITYCODE. Los catálogos de códigos están en
src/migrations/EntityStatusMigrations.ts (SupplyStatus, DispatchStatus, GlobalEntityStatus…).
Dónde está físicamente un suministro
La dupla ATENTITYCODE + ATENTITYID es la ubicación. No hay una FK distinta por tipo de
tenedor. Cada transición:
- cierra la fila abierta de
SUPPLYMOVEMENTHISTORY(le poneendDate), - abre una nueva,
- actualiza
SUPPLIES.ATENTITYCODE/ATENTITYID.
Eso lo hace un stored procedure, no TypeScript — ver Stock y transferencias.
Migraciones: dos cosas distintas con el mismo nombre
| Carpeta | Qué es |
|---|---|
src/migrations/*.ts | Scripts de datos one-shot en TypeScript (roles, permisos, estados, tipos de movimiento). Se corren a mano con ts-node. No son migraciones de schema de TypeORM: no existen archivos de schema migration |
migrations/**/*.sql (raíz del repo) | SQL crudo que se aplica directo a la base, organizado por dominio. Acá viven los stored procedures, las views, los fix/ y los test/ |
Trampas de TypeORM que ya costaron caro
manager.query ni save() con la relación en nullPara anular un FK de relación usá:
await repository.update(id, { relation: null });
entity.relation = null; save() no persiste el SET NULL de un @ManyToOne: falla en silencio
y el vínculo queda pegado. Eso causó el bug de "fallar una tarea deja el remito no re-vinculable".
getRepository()sin elqueryRunnerescapa de la transacción del caller. Si estás dentro de una, pasásela siempre.- Redis se vacía entero al arrancar (salvo en
dev). No guardes ahí nada que deba sobrevivir un deploy.