paperclip-plugin-autopilot-ops 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/manifest.js +22 -6
- package/dist/ui/index.js +22 -6
- package/dist/worker.js +334 -38
- package/package.json +1 -1
package/dist/manifest.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/manifest.ts
|
|
2
2
|
var PLUGIN_ID = "alephoo.autopilot-ops";
|
|
3
|
-
var PLUGIN_VERSION = "0.1.
|
|
3
|
+
var PLUGIN_VERSION = "0.1.9";
|
|
4
4
|
var DB_NAMESPACE_SLUG = "autopilot_ops";
|
|
5
5
|
var PAGE_ROUTE = "autopilot";
|
|
6
6
|
var TOOL_NAMES = {
|
|
@@ -34,7 +34,7 @@ var manifest = {
|
|
|
34
34
|
apiVersion: 1,
|
|
35
35
|
version: PLUGIN_VERSION,
|
|
36
36
|
displayName: "Autopilot Care",
|
|
37
|
-
description: "Operaci\xF3n aut\xF3noma gobernada para instituciones de salud: workflows determin\xEDsticos por puesto, validaciones, registro de negocio con evidencia, pol\xEDticas (observa / propone / ejecuta / aut\xF3nomo / corte), carga manual del equipo del hospital con comprobaci\xF3n del agente, recupero de turnos liberados desde la lista de espera, tableros de gesti\xF3n publicados por agentes y panel del director.",
|
|
37
|
+
description: "Operaci\xF3n aut\xF3noma gobernada para instituciones de salud: workflows determin\xEDsticos por puesto, validaciones, registro de negocio con evidencia, pol\xEDticas (observa / propone / ejecuta / aut\xF3nomo / corte), carga manual del equipo del hospital con comprobaci\xF3n del agente, recupero de turnos liberados desde la lista de espera, control de cuenta completa antes de facturar, tableros de gesti\xF3n publicados por agentes y panel del director.",
|
|
38
38
|
author: "Alephoo",
|
|
39
39
|
categories: ["automation", "ui"],
|
|
40
40
|
capabilities: [
|
|
@@ -84,7 +84,7 @@ var manifest = {
|
|
|
84
84
|
{
|
|
85
85
|
name: TOOL_NAMES.abrirCaso,
|
|
86
86
|
displayName: "Abrir caso",
|
|
87
|
-
description: 'Abre un caso nuevo en un workflow, en su estado inicial. Workflows: `actualizacion_arancel`, `tarea_operativa`, `recupero_turno` (datos: fecha AAAA-MM-DD, hora HH:MM, profesional, especialidad del turno libre; referencia "AAAA-MM-DD HH:MM \xB7 Profesional"). `referencia` identifica el objeto real (convenio, cuenta, turno) y evita duplicados: si ya existe un caso abierto con la misma referencia, se devuelve ese. Pasar `idempotencyKey` para reintentos seguros.',
|
|
87
|
+
description: 'Abre un caso nuevo en un workflow, en su estado inicial. Workflows: `actualizacion_arancel`, `tarea_operativa`, `recupero_turno` (datos: fecha AAAA-MM-DD, hora HH:MM, profesional, especialidad del turno libre; referencia "AAAA-MM-DD HH:MM \xB7 Profesional"), `cuenta_completa` (una prestaci\xF3n pendiente de facturar con faltantes; referencia obligatoria "bono:<n\xFAmero>", datos: bono_id, bono_numero, obra_social, plan, prestacion, fecha_prestacion AAAA-MM-DD, monto_pesos). `referencia` identifica el objeto real (convenio, cuenta, turno) y evita duplicados: si ya existe un caso abierto con la misma referencia, se devuelve ese. Pasar `idempotencyKey` para reintentos seguros.',
|
|
88
88
|
parametersSchema: {
|
|
89
89
|
type: "object",
|
|
90
90
|
properties: {
|
|
@@ -101,7 +101,7 @@ var manifest = {
|
|
|
101
101
|
{
|
|
102
102
|
name: TOOL_NAMES.transicionar,
|
|
103
103
|
displayName: "Transicionar caso",
|
|
104
|
-
description:
|
|
104
|
+
description: 'Aplica una transici\xF3n del workflow al caso (p. ej. `proponer`, `aplicar`, `verificar`). El plugin valida contra la tabla de transiciones, la pol\xEDtica vigente (observa / propone / ejecuta / corte) y las reglas de negocio; si algo falla, el caso no cambia y se devuelve el motivo. Las transiciones humanas (aprobar, rechazar, corregir) no est\xE1n disponibles para agentes. Cuando una persona del hospital carga a mano lo aprobado, el caso queda en `aplicado`: el agente lo comprueba releyendo el sistema y llamando `verificar` con `datos.valor_leido`. En `recupero_turno`: `proponer` con solicitud_id, solicitud_estado, solicitud_especialidad, cola (la lista de espera pendiente de esa especialidad tal como se vio: [{solicitud_id, prioridad, alta}]; el plugin la ordena y exige justificaci\xF3n para no elegir la primera) y fuente; si la instituci\xF3n pide consulta previa (ops_leer_caso lo dice), `ofrecer` con canal y esperar `aceptar`/`no_acepta`; **antes de escribir en el sistema, `autorizar`** (sin esa autorizaci\xF3n no se escribe); despu\xE9s de escribir, `asignar` con accion_id; `verificar` con leido_fecha, leido_hora, leido_profesional y leido_solicitud_id; `sin_candidato` y `descartar` con motivo. En `cuenta_completa` el agente solo lee: `observar` con bono_id, bono_estado (no_confirmado, confirmado o prefacturado), faltantes [{regla, detalle, item?}] con reglas del cat\xE1logo (autorizacion_faltante, validacion_faltante, afiliado_faltante, orden_faltante, prestacion_sin_valor, epicrisis_faltante), lectura_id (la lectura registrada con ops_registrar_accion, conector "base") y fuente; `comprobar` con faltantes_leidos (lista vac\xEDa si ya est\xE1 completo), bono_estado_leido y lectura_id; `descartar` con motivo si el bono se elimin\xF3 o anul\xF3.',
|
|
105
105
|
parametersSchema: {
|
|
106
106
|
type: "object",
|
|
107
107
|
properties: {
|
|
@@ -123,7 +123,7 @@ var manifest = {
|
|
|
123
123
|
properties: {
|
|
124
124
|
casoId: { type: "string" },
|
|
125
125
|
tipo: { type: "string", enum: ["lectura", "escritura", "verificacion"] },
|
|
126
|
-
conector: { type: "string", enum: ["api", "archivo", "pantalla", "manual"] },
|
|
126
|
+
conector: { type: "string", enum: ["api", "base", "archivo", "pantalla", "manual"], description: "Por d\xF3nde se ley\xF3 o escribi\xF3: api, base (lectura de la base de datos del hospital), archivo, pantalla o manual." },
|
|
127
127
|
objetivo: { type: "string" },
|
|
128
128
|
resultado: { type: "string", enum: ["ok", "error", "pendiente"] },
|
|
129
129
|
evidencia: { type: "object" },
|
|
@@ -149,7 +149,7 @@ var manifest = {
|
|
|
149
149
|
{
|
|
150
150
|
name: TOOL_NAMES.publicarTablero,
|
|
151
151
|
displayName: "Publicar tablero",
|
|
152
|
-
description: "Publica o actualiza un tablero de gesti\xF3n para la direcci\xF3n del hospital, armado sobre datos reales de Alephoo (turnos, facturaci\xF3n, internaci\xF3n, producci\xF3n). `clave` identifica el tablero de forma estable (min\xFAsculas y guiones; volver a publicar con la misma clave lo actualiza en el lugar). `widgets` es la lista de piezas que se muestran, en orden: {tipo:'indicador', etiqueta, valor, unidad?, contexto?, tono?: neutro|ambar|senal|corte} \xB7 {tipo:'barras', titulo, unidad?, series:[{etiqueta, valor}]} \xB7 {tipo:'linea', titulo, unidad?, puntos:[{x, y}]} \xB7 {tipo:'tabla', titulo, columnas:[\u2026], filas:[[\u2026]]} \xB7 {tipo:'texto', titulo?, texto}. Cada widget admite `ancho` 1, 2 o 3 (columnas de 3). `fuentes` lista qu\xE9 se consult\xF3 (p. ej. 'Alephoo \xB7
|
|
152
|
+
description: "Publica o actualiza un tablero de gesti\xF3n para la direcci\xF3n del hospital, armado sobre datos reales de Alephoo (turnos, facturaci\xF3n, internaci\xF3n, producci\xF3n). `clave` identifica el tablero de forma estable (min\xFAsculas y guiones; volver a publicar con la misma clave lo actualiza en el lugar). `widgets` es la lista de piezas que se muestran, en orden: {tipo:'indicador', etiqueta, valor, unidad?, contexto?, tono?: neutro|ambar|senal|corte} \xB7 {tipo:'barras', titulo, unidad?, series:[{etiqueta, valor}]} \xB7 {tipo:'linea', titulo, unidad?, puntos:[{x, y}]} \xB7 {tipo:'tabla', titulo, columnas:[\u2026], filas:[[\u2026]]} \xB7 {tipo:'texto', titulo?, texto}. Cada widget admite `ancho` 1, 2 o 3 (columnas de 3). **N\xFAmeros oficiales:** cada indicador, barras, l\xEDnea o tabla calculado con calcular.mjs lleva `calculo` = el bloque `calculo` que devuelve calcular.mjs, tal cual (cat\xE1logo, m\xE9tricas con versi\xF3n, par\xE1metros, huella, consulta); el plugin lo valida contra el cat\xE1logo de m\xE9tricas y marca el widget como certificado, propuesta (m\xE9trica sin aprobar) o exploratorio (sin `calculo`). Controles que frenan la publicaci\xF3n: una tabla con fila \xABTOTAL\xBB tiene que sumar ese total en cada columna num\xE9rica (las filas \xABSubtotal \u2026\xBB no suman); un desglose con `calculo` y `por` lleva la fila TOTAL; un porcentaje (`unidad` '%' o columna con '%') va de 0 a 100 salvo que el t\xEDtulo diga variaci\xF3n, diferencia o cumplimiento de meta. `fuentes` lista qu\xE9 se consult\xF3 (p. ej. 'Alephoo \xB7 base \xB7 semantica_turnos \xB7 turnos 01/09\u201330/09'). Si el pedido vino de una tarea, pasar `issueId`.",
|
|
153
153
|
parametersSchema: {
|
|
154
154
|
type: "object",
|
|
155
155
|
properties: {
|
|
@@ -229,6 +229,14 @@ var manifest = {
|
|
|
229
229
|
capability: "api.routes.register",
|
|
230
230
|
companyResolution: { from: "body", key: "companyId" }
|
|
231
231
|
},
|
|
232
|
+
{
|
|
233
|
+
routeKey: "no-corresponde",
|
|
234
|
+
method: "POST",
|
|
235
|
+
path: "/casos/:casoId/no-corresponde",
|
|
236
|
+
auth: "board",
|
|
237
|
+
capability: "api.routes.register",
|
|
238
|
+
companyResolution: { from: "body", key: "companyId" }
|
|
239
|
+
},
|
|
232
240
|
{
|
|
233
241
|
routeKey: "version",
|
|
234
242
|
method: "GET",
|
|
@@ -269,6 +277,14 @@ var manifest = {
|
|
|
269
277
|
capability: "api.routes.register",
|
|
270
278
|
companyResolution: { from: "body", key: "companyId" }
|
|
271
279
|
},
|
|
280
|
+
{
|
|
281
|
+
routeKey: "metricas",
|
|
282
|
+
method: "GET",
|
|
283
|
+
path: "/metricas",
|
|
284
|
+
auth: "board",
|
|
285
|
+
capability: "api.routes.register",
|
|
286
|
+
companyResolution: { from: "query", key: "companyId" }
|
|
287
|
+
},
|
|
272
288
|
{
|
|
273
289
|
routeKey: "tableros",
|
|
274
290
|
method: "GET",
|
package/dist/ui/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
|
|
9
9
|
// src/manifest.ts
|
|
10
10
|
var PLUGIN_ID = "alephoo.autopilot-ops";
|
|
11
|
-
var PLUGIN_VERSION = "0.1.
|
|
11
|
+
var PLUGIN_VERSION = "0.1.9";
|
|
12
12
|
var DB_NAMESPACE_SLUG = "autopilot_ops";
|
|
13
13
|
var PAGE_ROUTE = "autopilot";
|
|
14
14
|
var TOOL_NAMES = {
|
|
@@ -42,7 +42,7 @@ var manifest = {
|
|
|
42
42
|
apiVersion: 1,
|
|
43
43
|
version: PLUGIN_VERSION,
|
|
44
44
|
displayName: "Autopilot Care",
|
|
45
|
-
description: "Operaci\xF3n aut\xF3noma gobernada para instituciones de salud: workflows determin\xEDsticos por puesto, validaciones, registro de negocio con evidencia, pol\xEDticas (observa / propone / ejecuta / aut\xF3nomo / corte), carga manual del equipo del hospital con comprobaci\xF3n del agente, recupero de turnos liberados desde la lista de espera, tableros de gesti\xF3n publicados por agentes y panel del director.",
|
|
45
|
+
description: "Operaci\xF3n aut\xF3noma gobernada para instituciones de salud: workflows determin\xEDsticos por puesto, validaciones, registro de negocio con evidencia, pol\xEDticas (observa / propone / ejecuta / aut\xF3nomo / corte), carga manual del equipo del hospital con comprobaci\xF3n del agente, recupero de turnos liberados desde la lista de espera, control de cuenta completa antes de facturar, tableros de gesti\xF3n publicados por agentes y panel del director.",
|
|
46
46
|
author: "Alephoo",
|
|
47
47
|
categories: ["automation", "ui"],
|
|
48
48
|
capabilities: [
|
|
@@ -92,7 +92,7 @@ var manifest = {
|
|
|
92
92
|
{
|
|
93
93
|
name: TOOL_NAMES.abrirCaso,
|
|
94
94
|
displayName: "Abrir caso",
|
|
95
|
-
description: 'Abre un caso nuevo en un workflow, en su estado inicial. Workflows: `actualizacion_arancel`, `tarea_operativa`, `recupero_turno` (datos: fecha AAAA-MM-DD, hora HH:MM, profesional, especialidad del turno libre; referencia "AAAA-MM-DD HH:MM \xB7 Profesional"). `referencia` identifica el objeto real (convenio, cuenta, turno) y evita duplicados: si ya existe un caso abierto con la misma referencia, se devuelve ese. Pasar `idempotencyKey` para reintentos seguros.',
|
|
95
|
+
description: 'Abre un caso nuevo en un workflow, en su estado inicial. Workflows: `actualizacion_arancel`, `tarea_operativa`, `recupero_turno` (datos: fecha AAAA-MM-DD, hora HH:MM, profesional, especialidad del turno libre; referencia "AAAA-MM-DD HH:MM \xB7 Profesional"), `cuenta_completa` (una prestaci\xF3n pendiente de facturar con faltantes; referencia obligatoria "bono:<n\xFAmero>", datos: bono_id, bono_numero, obra_social, plan, prestacion, fecha_prestacion AAAA-MM-DD, monto_pesos). `referencia` identifica el objeto real (convenio, cuenta, turno) y evita duplicados: si ya existe un caso abierto con la misma referencia, se devuelve ese. Pasar `idempotencyKey` para reintentos seguros.',
|
|
96
96
|
parametersSchema: {
|
|
97
97
|
type: "object",
|
|
98
98
|
properties: {
|
|
@@ -109,7 +109,7 @@ var manifest = {
|
|
|
109
109
|
{
|
|
110
110
|
name: TOOL_NAMES.transicionar,
|
|
111
111
|
displayName: "Transicionar caso",
|
|
112
|
-
description:
|
|
112
|
+
description: 'Aplica una transici\xF3n del workflow al caso (p. ej. `proponer`, `aplicar`, `verificar`). El plugin valida contra la tabla de transiciones, la pol\xEDtica vigente (observa / propone / ejecuta / corte) y las reglas de negocio; si algo falla, el caso no cambia y se devuelve el motivo. Las transiciones humanas (aprobar, rechazar, corregir) no est\xE1n disponibles para agentes. Cuando una persona del hospital carga a mano lo aprobado, el caso queda en `aplicado`: el agente lo comprueba releyendo el sistema y llamando `verificar` con `datos.valor_leido`. En `recupero_turno`: `proponer` con solicitud_id, solicitud_estado, solicitud_especialidad, cola (la lista de espera pendiente de esa especialidad tal como se vio: [{solicitud_id, prioridad, alta}]; el plugin la ordena y exige justificaci\xF3n para no elegir la primera) y fuente; si la instituci\xF3n pide consulta previa (ops_leer_caso lo dice), `ofrecer` con canal y esperar `aceptar`/`no_acepta`; **antes de escribir en el sistema, `autorizar`** (sin esa autorizaci\xF3n no se escribe); despu\xE9s de escribir, `asignar` con accion_id; `verificar` con leido_fecha, leido_hora, leido_profesional y leido_solicitud_id; `sin_candidato` y `descartar` con motivo. En `cuenta_completa` el agente solo lee: `observar` con bono_id, bono_estado (no_confirmado, confirmado o prefacturado), faltantes [{regla, detalle, item?}] con reglas del cat\xE1logo (autorizacion_faltante, validacion_faltante, afiliado_faltante, orden_faltante, prestacion_sin_valor, epicrisis_faltante), lectura_id (la lectura registrada con ops_registrar_accion, conector "base") y fuente; `comprobar` con faltantes_leidos (lista vac\xEDa si ya est\xE1 completo), bono_estado_leido y lectura_id; `descartar` con motivo si el bono se elimin\xF3 o anul\xF3.',
|
|
113
113
|
parametersSchema: {
|
|
114
114
|
type: "object",
|
|
115
115
|
properties: {
|
|
@@ -131,7 +131,7 @@ var manifest = {
|
|
|
131
131
|
properties: {
|
|
132
132
|
casoId: { type: "string" },
|
|
133
133
|
tipo: { type: "string", enum: ["lectura", "escritura", "verificacion"] },
|
|
134
|
-
conector: { type: "string", enum: ["api", "archivo", "pantalla", "manual"] },
|
|
134
|
+
conector: { type: "string", enum: ["api", "base", "archivo", "pantalla", "manual"], description: "Por d\xF3nde se ley\xF3 o escribi\xF3: api, base (lectura de la base de datos del hospital), archivo, pantalla o manual." },
|
|
135
135
|
objetivo: { type: "string" },
|
|
136
136
|
resultado: { type: "string", enum: ["ok", "error", "pendiente"] },
|
|
137
137
|
evidencia: { type: "object" },
|
|
@@ -157,7 +157,7 @@ var manifest = {
|
|
|
157
157
|
{
|
|
158
158
|
name: TOOL_NAMES.publicarTablero,
|
|
159
159
|
displayName: "Publicar tablero",
|
|
160
|
-
description: "Publica o actualiza un tablero de gesti\xF3n para la direcci\xF3n del hospital, armado sobre datos reales de Alephoo (turnos, facturaci\xF3n, internaci\xF3n, producci\xF3n). `clave` identifica el tablero de forma estable (min\xFAsculas y guiones; volver a publicar con la misma clave lo actualiza en el lugar). `widgets` es la lista de piezas que se muestran, en orden: {tipo:'indicador', etiqueta, valor, unidad?, contexto?, tono?: neutro|ambar|senal|corte} \xB7 {tipo:'barras', titulo, unidad?, series:[{etiqueta, valor}]} \xB7 {tipo:'linea', titulo, unidad?, puntos:[{x, y}]} \xB7 {tipo:'tabla', titulo, columnas:[\u2026], filas:[[\u2026]]} \xB7 {tipo:'texto', titulo?, texto}. Cada widget admite `ancho` 1, 2 o 3 (columnas de 3). `fuentes` lista qu\xE9 se consult\xF3 (p. ej. 'Alephoo \xB7
|
|
160
|
+
description: "Publica o actualiza un tablero de gesti\xF3n para la direcci\xF3n del hospital, armado sobre datos reales de Alephoo (turnos, facturaci\xF3n, internaci\xF3n, producci\xF3n). `clave` identifica el tablero de forma estable (min\xFAsculas y guiones; volver a publicar con la misma clave lo actualiza en el lugar). `widgets` es la lista de piezas que se muestran, en orden: {tipo:'indicador', etiqueta, valor, unidad?, contexto?, tono?: neutro|ambar|senal|corte} \xB7 {tipo:'barras', titulo, unidad?, series:[{etiqueta, valor}]} \xB7 {tipo:'linea', titulo, unidad?, puntos:[{x, y}]} \xB7 {tipo:'tabla', titulo, columnas:[\u2026], filas:[[\u2026]]} \xB7 {tipo:'texto', titulo?, texto}. Cada widget admite `ancho` 1, 2 o 3 (columnas de 3). **N\xFAmeros oficiales:** cada indicador, barras, l\xEDnea o tabla calculado con calcular.mjs lleva `calculo` = el bloque `calculo` que devuelve calcular.mjs, tal cual (cat\xE1logo, m\xE9tricas con versi\xF3n, par\xE1metros, huella, consulta); el plugin lo valida contra el cat\xE1logo de m\xE9tricas y marca el widget como certificado, propuesta (m\xE9trica sin aprobar) o exploratorio (sin `calculo`). Controles que frenan la publicaci\xF3n: una tabla con fila \xABTOTAL\xBB tiene que sumar ese total en cada columna num\xE9rica (las filas \xABSubtotal \u2026\xBB no suman); un desglose con `calculo` y `por` lleva la fila TOTAL; un porcentaje (`unidad` '%' o columna con '%') va de 0 a 100 salvo que el t\xEDtulo diga variaci\xF3n, diferencia o cumplimiento de meta. `fuentes` lista qu\xE9 se consult\xF3 (p. ej. 'Alephoo \xB7 base \xB7 semantica_turnos \xB7 turnos 01/09\u201330/09'). Si el pedido vino de una tarea, pasar `issueId`.",
|
|
161
161
|
parametersSchema: {
|
|
162
162
|
type: "object",
|
|
163
163
|
properties: {
|
|
@@ -237,6 +237,14 @@ var manifest = {
|
|
|
237
237
|
capability: "api.routes.register",
|
|
238
238
|
companyResolution: { from: "body", key: "companyId" }
|
|
239
239
|
},
|
|
240
|
+
{
|
|
241
|
+
routeKey: "no-corresponde",
|
|
242
|
+
method: "POST",
|
|
243
|
+
path: "/casos/:casoId/no-corresponde",
|
|
244
|
+
auth: "board",
|
|
245
|
+
capability: "api.routes.register",
|
|
246
|
+
companyResolution: { from: "body", key: "companyId" }
|
|
247
|
+
},
|
|
240
248
|
{
|
|
241
249
|
routeKey: "version",
|
|
242
250
|
method: "GET",
|
|
@@ -277,6 +285,14 @@ var manifest = {
|
|
|
277
285
|
capability: "api.routes.register",
|
|
278
286
|
companyResolution: { from: "body", key: "companyId" }
|
|
279
287
|
},
|
|
288
|
+
{
|
|
289
|
+
routeKey: "metricas",
|
|
290
|
+
method: "GET",
|
|
291
|
+
path: "/metricas",
|
|
292
|
+
auth: "board",
|
|
293
|
+
capability: "api.routes.register",
|
|
294
|
+
companyResolution: { from: "query", key: "companyId" }
|
|
295
|
+
},
|
|
280
296
|
{
|
|
281
297
|
routeKey: "tableros",
|
|
282
298
|
method: "GET",
|
package/dist/worker.js
CHANGED
|
@@ -39932,7 +39932,7 @@ function startWorkerRpcHost(options) {
|
|
|
39932
39932
|
|
|
39933
39933
|
// src/manifest.ts
|
|
39934
39934
|
var PLUGIN_ID = "alephoo.autopilot-ops";
|
|
39935
|
-
var PLUGIN_VERSION = "0.1.
|
|
39935
|
+
var PLUGIN_VERSION = "0.1.9";
|
|
39936
39936
|
var DB_NAMESPACE_SLUG = "autopilot_ops";
|
|
39937
39937
|
var PAGE_ROUTE = "autopilot";
|
|
39938
39938
|
var TOOL_NAMES = {
|
|
@@ -39966,7 +39966,7 @@ var manifest = {
|
|
|
39966
39966
|
apiVersion: 1,
|
|
39967
39967
|
version: PLUGIN_VERSION,
|
|
39968
39968
|
displayName: "Autopilot Care",
|
|
39969
|
-
description: "Operaci\xF3n aut\xF3noma gobernada para instituciones de salud: workflows determin\xEDsticos por puesto, validaciones, registro de negocio con evidencia, pol\xEDticas (observa / propone / ejecuta / aut\xF3nomo / corte), carga manual del equipo del hospital con comprobaci\xF3n del agente, recupero de turnos liberados desde la lista de espera, tableros de gesti\xF3n publicados por agentes y panel del director.",
|
|
39969
|
+
description: "Operaci\xF3n aut\xF3noma gobernada para instituciones de salud: workflows determin\xEDsticos por puesto, validaciones, registro de negocio con evidencia, pol\xEDticas (observa / propone / ejecuta / aut\xF3nomo / corte), carga manual del equipo del hospital con comprobaci\xF3n del agente, recupero de turnos liberados desde la lista de espera, control de cuenta completa antes de facturar, tableros de gesti\xF3n publicados por agentes y panel del director.",
|
|
39970
39970
|
author: "Alephoo",
|
|
39971
39971
|
categories: ["automation", "ui"],
|
|
39972
39972
|
capabilities: [
|
|
@@ -40016,7 +40016,7 @@ var manifest = {
|
|
|
40016
40016
|
{
|
|
40017
40017
|
name: TOOL_NAMES.abrirCaso,
|
|
40018
40018
|
displayName: "Abrir caso",
|
|
40019
|
-
description: 'Abre un caso nuevo en un workflow, en su estado inicial. Workflows: `actualizacion_arancel`, `tarea_operativa`, `recupero_turno` (datos: fecha AAAA-MM-DD, hora HH:MM, profesional, especialidad del turno libre; referencia "AAAA-MM-DD HH:MM \xB7 Profesional"). `referencia` identifica el objeto real (convenio, cuenta, turno) y evita duplicados: si ya existe un caso abierto con la misma referencia, se devuelve ese. Pasar `idempotencyKey` para reintentos seguros.',
|
|
40019
|
+
description: 'Abre un caso nuevo en un workflow, en su estado inicial. Workflows: `actualizacion_arancel`, `tarea_operativa`, `recupero_turno` (datos: fecha AAAA-MM-DD, hora HH:MM, profesional, especialidad del turno libre; referencia "AAAA-MM-DD HH:MM \xB7 Profesional"), `cuenta_completa` (una prestaci\xF3n pendiente de facturar con faltantes; referencia obligatoria "bono:<n\xFAmero>", datos: bono_id, bono_numero, obra_social, plan, prestacion, fecha_prestacion AAAA-MM-DD, monto_pesos). `referencia` identifica el objeto real (convenio, cuenta, turno) y evita duplicados: si ya existe un caso abierto con la misma referencia, se devuelve ese. Pasar `idempotencyKey` para reintentos seguros.',
|
|
40020
40020
|
parametersSchema: {
|
|
40021
40021
|
type: "object",
|
|
40022
40022
|
properties: {
|
|
@@ -40033,7 +40033,7 @@ var manifest = {
|
|
|
40033
40033
|
{
|
|
40034
40034
|
name: TOOL_NAMES.transicionar,
|
|
40035
40035
|
displayName: "Transicionar caso",
|
|
40036
|
-
description:
|
|
40036
|
+
description: 'Aplica una transici\xF3n del workflow al caso (p. ej. `proponer`, `aplicar`, `verificar`). El plugin valida contra la tabla de transiciones, la pol\xEDtica vigente (observa / propone / ejecuta / corte) y las reglas de negocio; si algo falla, el caso no cambia y se devuelve el motivo. Las transiciones humanas (aprobar, rechazar, corregir) no est\xE1n disponibles para agentes. Cuando una persona del hospital carga a mano lo aprobado, el caso queda en `aplicado`: el agente lo comprueba releyendo el sistema y llamando `verificar` con `datos.valor_leido`. En `recupero_turno`: `proponer` con solicitud_id, solicitud_estado, solicitud_especialidad, cola (la lista de espera pendiente de esa especialidad tal como se vio: [{solicitud_id, prioridad, alta}]; el plugin la ordena y exige justificaci\xF3n para no elegir la primera) y fuente; si la instituci\xF3n pide consulta previa (ops_leer_caso lo dice), `ofrecer` con canal y esperar `aceptar`/`no_acepta`; **antes de escribir en el sistema, `autorizar`** (sin esa autorizaci\xF3n no se escribe); despu\xE9s de escribir, `asignar` con accion_id; `verificar` con leido_fecha, leido_hora, leido_profesional y leido_solicitud_id; `sin_candidato` y `descartar` con motivo. En `cuenta_completa` el agente solo lee: `observar` con bono_id, bono_estado (no_confirmado, confirmado o prefacturado), faltantes [{regla, detalle, item?}] con reglas del cat\xE1logo (autorizacion_faltante, validacion_faltante, afiliado_faltante, orden_faltante, prestacion_sin_valor, epicrisis_faltante), lectura_id (la lectura registrada con ops_registrar_accion, conector "base") y fuente; `comprobar` con faltantes_leidos (lista vac\xEDa si ya est\xE1 completo), bono_estado_leido y lectura_id; `descartar` con motivo si el bono se elimin\xF3 o anul\xF3.',
|
|
40037
40037
|
parametersSchema: {
|
|
40038
40038
|
type: "object",
|
|
40039
40039
|
properties: {
|
|
@@ -40055,7 +40055,7 @@ var manifest = {
|
|
|
40055
40055
|
properties: {
|
|
40056
40056
|
casoId: { type: "string" },
|
|
40057
40057
|
tipo: { type: "string", enum: ["lectura", "escritura", "verificacion"] },
|
|
40058
|
-
conector: { type: "string", enum: ["api", "archivo", "pantalla", "manual"] },
|
|
40058
|
+
conector: { type: "string", enum: ["api", "base", "archivo", "pantalla", "manual"], description: "Por d\xF3nde se ley\xF3 o escribi\xF3: api, base (lectura de la base de datos del hospital), archivo, pantalla o manual." },
|
|
40059
40059
|
objetivo: { type: "string" },
|
|
40060
40060
|
resultado: { type: "string", enum: ["ok", "error", "pendiente"] },
|
|
40061
40061
|
evidencia: { type: "object" },
|
|
@@ -40081,7 +40081,7 @@ var manifest = {
|
|
|
40081
40081
|
{
|
|
40082
40082
|
name: TOOL_NAMES.publicarTablero,
|
|
40083
40083
|
displayName: "Publicar tablero",
|
|
40084
|
-
description: "Publica o actualiza un tablero de gesti\xF3n para la direcci\xF3n del hospital, armado sobre datos reales de Alephoo (turnos, facturaci\xF3n, internaci\xF3n, producci\xF3n). `clave` identifica el tablero de forma estable (min\xFAsculas y guiones; volver a publicar con la misma clave lo actualiza en el lugar). `widgets` es la lista de piezas que se muestran, en orden: {tipo:'indicador', etiqueta, valor, unidad?, contexto?, tono?: neutro|ambar|senal|corte} \xB7 {tipo:'barras', titulo, unidad?, series:[{etiqueta, valor}]} \xB7 {tipo:'linea', titulo, unidad?, puntos:[{x, y}]} \xB7 {tipo:'tabla', titulo, columnas:[\u2026], filas:[[\u2026]]} \xB7 {tipo:'texto', titulo?, texto}. Cada widget admite `ancho` 1, 2 o 3 (columnas de 3). `fuentes` lista qu\xE9 se consult\xF3 (p. ej. 'Alephoo \xB7
|
|
40084
|
+
description: "Publica o actualiza un tablero de gesti\xF3n para la direcci\xF3n del hospital, armado sobre datos reales de Alephoo (turnos, facturaci\xF3n, internaci\xF3n, producci\xF3n). `clave` identifica el tablero de forma estable (min\xFAsculas y guiones; volver a publicar con la misma clave lo actualiza en el lugar). `widgets` es la lista de piezas que se muestran, en orden: {tipo:'indicador', etiqueta, valor, unidad?, contexto?, tono?: neutro|ambar|senal|corte} \xB7 {tipo:'barras', titulo, unidad?, series:[{etiqueta, valor}]} \xB7 {tipo:'linea', titulo, unidad?, puntos:[{x, y}]} \xB7 {tipo:'tabla', titulo, columnas:[\u2026], filas:[[\u2026]]} \xB7 {tipo:'texto', titulo?, texto}. Cada widget admite `ancho` 1, 2 o 3 (columnas de 3). **N\xFAmeros oficiales:** cada indicador, barras, l\xEDnea o tabla calculado con calcular.mjs lleva `calculo` = el bloque `calculo` que devuelve calcular.mjs, tal cual (cat\xE1logo, m\xE9tricas con versi\xF3n, par\xE1metros, huella, consulta); el plugin lo valida contra el cat\xE1logo de m\xE9tricas y marca el widget como certificado, propuesta (m\xE9trica sin aprobar) o exploratorio (sin `calculo`). Controles que frenan la publicaci\xF3n: una tabla con fila \xABTOTAL\xBB tiene que sumar ese total en cada columna num\xE9rica (las filas \xABSubtotal \u2026\xBB no suman); un desglose con `calculo` y `por` lleva la fila TOTAL; un porcentaje (`unidad` '%' o columna con '%') va de 0 a 100 salvo que el t\xEDtulo diga variaci\xF3n, diferencia o cumplimiento de meta. `fuentes` lista qu\xE9 se consult\xF3 (p. ej. 'Alephoo \xB7 base \xB7 semantica_turnos \xB7 turnos 01/09\u201330/09'). Si el pedido vino de una tarea, pasar `issueId`.",
|
|
40085
40085
|
parametersSchema: {
|
|
40086
40086
|
type: "object",
|
|
40087
40087
|
properties: {
|
|
@@ -40161,6 +40161,14 @@ var manifest = {
|
|
|
40161
40161
|
capability: "api.routes.register",
|
|
40162
40162
|
companyResolution: { from: "body", key: "companyId" }
|
|
40163
40163
|
},
|
|
40164
|
+
{
|
|
40165
|
+
routeKey: "no-corresponde",
|
|
40166
|
+
method: "POST",
|
|
40167
|
+
path: "/casos/:casoId/no-corresponde",
|
|
40168
|
+
auth: "board",
|
|
40169
|
+
capability: "api.routes.register",
|
|
40170
|
+
companyResolution: { from: "body", key: "companyId" }
|
|
40171
|
+
},
|
|
40164
40172
|
{
|
|
40165
40173
|
routeKey: "version",
|
|
40166
40174
|
method: "GET",
|
|
@@ -40201,6 +40209,14 @@ var manifest = {
|
|
|
40201
40209
|
capability: "api.routes.register",
|
|
40202
40210
|
companyResolution: { from: "body", key: "companyId" }
|
|
40203
40211
|
},
|
|
40212
|
+
{
|
|
40213
|
+
routeKey: "metricas",
|
|
40214
|
+
method: "GET",
|
|
40215
|
+
path: "/metricas",
|
|
40216
|
+
auth: "board",
|
|
40217
|
+
capability: "api.routes.register",
|
|
40218
|
+
companyResolution: { from: "query", key: "companyId" }
|
|
40219
|
+
},
|
|
40204
40220
|
{
|
|
40205
40221
|
routeKey: "tableros",
|
|
40206
40222
|
method: "GET",
|
|
@@ -40707,10 +40723,146 @@ var recuperoTurno = {
|
|
|
40707
40723
|
}
|
|
40708
40724
|
]
|
|
40709
40725
|
};
|
|
40726
|
+
var REGLAS_CUENTA = {
|
|
40727
|
+
autorizacion_faltante: {
|
|
40728
|
+
nombre: "Falta el n\xFAmero de autorizaci\xF3n",
|
|
40729
|
+
dondeSeCorrige: "Alephoo \u203A Facturaci\xF3n \u203A Preparar Informaci\xF3n \u203A Editar bono: n\xFAmero de autorizaci\xF3n de la prestaci\xF3n (o \xABValidar con broker\xBB)"
|
|
40730
|
+
},
|
|
40731
|
+
validacion_faltante: {
|
|
40732
|
+
nombre: "Falta la validaci\xF3n online del financiador",
|
|
40733
|
+
dondeSeCorrige: "Alephoo \u203A Facturaci\xF3n \u203A Preparar Informaci\xF3n \u203A Editar bono: \xABValidar con broker\xBB en la prestaci\xF3n"
|
|
40734
|
+
},
|
|
40735
|
+
afiliado_faltante: {
|
|
40736
|
+
nombre: "Falta el n\xFAmero de afiliado",
|
|
40737
|
+
dondeSeCorrige: "Alephoo \u203A Facturaci\xF3n \u203A Preparar Informaci\xF3n \u203A Editar bono: n\xFAmero de afiliado (tambi\xE9n actualiza el padr\xF3n del paciente)"
|
|
40738
|
+
},
|
|
40739
|
+
orden_faltante: {
|
|
40740
|
+
nombre: "Faltan los datos de la orden m\xE9dica",
|
|
40741
|
+
dondeSeCorrige: "Alephoo \u203A Facturaci\xF3n \u203A Preparar Informaci\xF3n \u203A Editar bono: m\xE9dico solicitante y fecha de la receta de la prestaci\xF3n"
|
|
40742
|
+
},
|
|
40743
|
+
prestacion_sin_valor: {
|
|
40744
|
+
nombre: "Prestaci\xF3n valorizada en $0",
|
|
40745
|
+
dondeSeCorrige: "Alephoo \u203A Facturaci\xF3n \u203A Configuraci\xF3n de convenios: cargar el valor de la pr\xE1ctica; despu\xE9s Facturaci\xF3n \u203A Actualizar Bonos"
|
|
40746
|
+
},
|
|
40747
|
+
epicrisis_faltante: {
|
|
40748
|
+
nombre: "Internaci\xF3n con egreso y sin epicrisis",
|
|
40749
|
+
dondeSeCorrige: "Alephoo \u203A historia cl\xEDnica del paciente internado \u203A Epicrisis"
|
|
40750
|
+
}
|
|
40751
|
+
};
|
|
40752
|
+
var BONO_POR_FACTURAR = ["no_confirmado", "confirmado", "prefacturado"];
|
|
40753
|
+
var BONO_FACTURADO = ["facturado", "refacturado"];
|
|
40754
|
+
function leerFaltantes(v) {
|
|
40755
|
+
if (!Array.isArray(v)) return null;
|
|
40756
|
+
const out = [];
|
|
40757
|
+
for (const e of v) {
|
|
40758
|
+
if (!e || typeof e !== "object") return null;
|
|
40759
|
+
const r = e;
|
|
40760
|
+
const regla = texto(r.regla);
|
|
40761
|
+
const detalle = texto(r.detalle);
|
|
40762
|
+
if (!regla || !REGLAS_CUENTA[regla] || !detalle) return null;
|
|
40763
|
+
out.push({ regla, detalle, item: refExterna(r.item) });
|
|
40764
|
+
}
|
|
40765
|
+
return out;
|
|
40766
|
+
}
|
|
40767
|
+
function validarFaltantes(campo, v, alMenosUno) {
|
|
40768
|
+
const lista = leerFaltantes(v);
|
|
40769
|
+
if (!lista) {
|
|
40770
|
+
return falla("faltantes_validos", `${campo} tiene que ser una lista [{regla, detalle, item?}] con reglas del cat\xE1logo: ${Object.keys(REGLAS_CUENTA).join(", ")}`);
|
|
40771
|
+
}
|
|
40772
|
+
if (alMenosUno && lista.length === 0) return falla("faltantes_validos", `${campo} est\xE1 vac\xEDo: una cuenta sin faltantes no se abre`);
|
|
40773
|
+
return ok("faltantes_validos", lista.length === 0 ? "sin faltantes" : lista.length === 1 ? "1 faltante" : `${lista.length} faltantes`);
|
|
40774
|
+
}
|
|
40775
|
+
function estadoBono(v) {
|
|
40776
|
+
const s = texto(v);
|
|
40777
|
+
return s ? s.toLowerCase().replace(/[\s-]+/g, "_") : null;
|
|
40778
|
+
}
|
|
40779
|
+
var cuentaCompleta = {
|
|
40780
|
+
clave: "cuenta_completa",
|
|
40781
|
+
nombre: "Cuenta completa antes de facturar",
|
|
40782
|
+
puesto: "Admisi\xF3n y cuenta",
|
|
40783
|
+
descripcion: "Lee en el sistema del hospital cada prestaci\xF3n pendiente de facturar y marca lo que le falta para facturarse sin d\xE9bito (autorizaci\xF3n, validaci\xF3n, afiliado, orden, valor, epicrisis). El equipo lo completa y el puesto lo comprueba volviendo a leer. El puesto no escribe en el sistema.",
|
|
40784
|
+
inicial: "detectado",
|
|
40785
|
+
estados: ["detectado", "para_completar", "completado", "con_faltantes", "completa", "facturado_incompleto", "no_corresponde", "descartado"],
|
|
40786
|
+
finalesOk: ["completa"],
|
|
40787
|
+
finalesNoOk: ["facturado_incompleto", "no_corresponde", "descartado"],
|
|
40788
|
+
minutosAbsorbidos: 10,
|
|
40789
|
+
transiciones: [
|
|
40790
|
+
{
|
|
40791
|
+
via: "observar",
|
|
40792
|
+
de: ["detectado"],
|
|
40793
|
+
a: "para_completar",
|
|
40794
|
+
actor: ["agente", "humano"],
|
|
40795
|
+
nivelRequerido: "observa",
|
|
40796
|
+
descripcion: "Informar lo que le falta a la prestaci\xF3n (bono) para facturarse, con la lectura registrada que lo muestra. Solo faltantes del cat\xE1logo; el bono tiene que estar todav\xEDa sin facturar.",
|
|
40797
|
+
validar: (caso, datos) => {
|
|
40798
|
+
const l = { ...caso.datos, ...datos };
|
|
40799
|
+
const estado = estadoBono(datos.bono_estado);
|
|
40800
|
+
return [
|
|
40801
|
+
refExterna(l.bono_id) ? ok("bono_presente") : falla("bono_presente", "observar requiere bono_id: el n\xFAmero de bono en el sistema del hospital"),
|
|
40802
|
+
estado && BONO_POR_FACTURAR.includes(estado) ? ok("bono_sin_facturar", estado) : falla("bono_sin_facturar", `el bono tiene que estar sin facturar (${BONO_POR_FACTURAR.join(", ")}); est\xE1: ${texto(datos.bono_estado) ?? "sin informar"}`),
|
|
40803
|
+
validarFaltantes("faltantes", datos.faltantes, true),
|
|
40804
|
+
texto(datos.lectura_id) ? ok("lectura_registrada") : falla("lectura_registrada", "observar requiere lectura_id: la lectura de la base registrada con ops_registrar_accion"),
|
|
40805
|
+
texto(datos.fuente) ? ok("fuente_presente") : falla("fuente_presente", "fuente (base y consulta de donde salen los faltantes) es obligatoria")
|
|
40806
|
+
];
|
|
40807
|
+
}
|
|
40808
|
+
},
|
|
40809
|
+
{
|
|
40810
|
+
via: "completar",
|
|
40811
|
+
de: ["para_completar", "con_faltantes"],
|
|
40812
|
+
a: "completado",
|
|
40813
|
+
actor: ["humano"],
|
|
40814
|
+
cargaManual: true,
|
|
40815
|
+
descripcion: "Una persona del hospital complet\xF3 en el sistema lo que faltaba. El puesto lo comprueba volviendo a leer.",
|
|
40816
|
+
validar: (_caso, datos) => [
|
|
40817
|
+
texto(datos.accion_id) ? ok("accion_registrada") : falla("accion_registrada", "completar requiere accion_id de la carga registrada")
|
|
40818
|
+
]
|
|
40819
|
+
},
|
|
40820
|
+
{
|
|
40821
|
+
// Lo decide el plugin con lo que se volvió a leer: sin faltantes, completa; facturada con faltantes, sale con riesgo de débito.
|
|
40822
|
+
via: "comprobar",
|
|
40823
|
+
de: ["para_completar", "completado", "con_faltantes"],
|
|
40824
|
+
a: (caso, datos) => {
|
|
40825
|
+
const faltan = leerFaltantes(datos.faltantes_leidos) ?? [];
|
|
40826
|
+
if (faltan.length === 0) return "completa";
|
|
40827
|
+
if (BONO_FACTURADO.includes(estadoBono(datos.bono_estado_leido) ?? "")) return "facturado_incompleto";
|
|
40828
|
+
return caso.estado === "completado" ? "con_faltantes" : caso.estado;
|
|
40829
|
+
},
|
|
40830
|
+
actor: ["agente", "humano", "sistema"],
|
|
40831
|
+
nivelRequerido: "observa",
|
|
40832
|
+
descripcion: "Volver a leer el bono en el sistema e informar lo que todav\xEDa falta (lista vac\xEDa si est\xE1 completo) y su estado. El plugin decide: completa, sigue para completar, con faltantes o facturada incompleta.",
|
|
40833
|
+
validar: (_caso, datos) => {
|
|
40834
|
+
const estado = estadoBono(datos.bono_estado_leido);
|
|
40835
|
+
return [
|
|
40836
|
+
validarFaltantes("faltantes_leidos", datos.faltantes_leidos, false),
|
|
40837
|
+
estado && [...BONO_POR_FACTURAR, ...BONO_FACTURADO].includes(estado) ? ok("estado_leido", estado) : falla("estado_leido", `bono_estado_leido tiene que ser uno de: ${[...BONO_POR_FACTURAR, ...BONO_FACTURADO].join(", ")} (si el bono se elimin\xF3 o se anul\xF3, descartar)`),
|
|
40838
|
+
texto(datos.lectura_id) ? ok("lectura_registrada") : falla("lectura_registrada", "comprobar requiere lectura_id: la lectura de la base registrada con ops_registrar_accion")
|
|
40839
|
+
];
|
|
40840
|
+
}
|
|
40841
|
+
},
|
|
40842
|
+
{
|
|
40843
|
+
via: "no_corresponde",
|
|
40844
|
+
de: ["para_completar", "completado", "con_faltantes"],
|
|
40845
|
+
a: "no_corresponde",
|
|
40846
|
+
actor: ["humano"],
|
|
40847
|
+
descripcion: "El equipo indica que lo marcado no hace falta para esta prestaci\xF3n (por ejemplo, el financiador no pide autorizaci\xF3n). Queda registrado con el motivo.",
|
|
40848
|
+
validar: (_caso, datos) => [texto(datos.motivo) ? ok("motivo_presente") : falla("motivo_presente", "no_corresponde requiere motivo")]
|
|
40849
|
+
},
|
|
40850
|
+
{
|
|
40851
|
+
via: "descartar",
|
|
40852
|
+
de: ["detectado", "para_completar", "completado", "con_faltantes"],
|
|
40853
|
+
a: "descartado",
|
|
40854
|
+
actor: ["agente", "humano"],
|
|
40855
|
+
nivelRequerido: "observa",
|
|
40856
|
+
descripcion: "El bono se elimin\xF3, se anul\xF3 o qued\xF3 como no cobrable: ya no hay nada que completar.",
|
|
40857
|
+
validar: (_caso, datos) => [texto(datos.motivo) ? ok("motivo_presente") : falla("motivo_presente", "descartar requiere motivo")]
|
|
40858
|
+
}
|
|
40859
|
+
]
|
|
40860
|
+
};
|
|
40710
40861
|
var WORKFLOWS = {
|
|
40711
40862
|
[actualizacionArancel.clave]: actualizacionArancel,
|
|
40712
40863
|
[tareaOperativa.clave]: tareaOperativa,
|
|
40713
|
-
[recuperoTurno.clave]: recuperoTurno
|
|
40864
|
+
[recuperoTurno.clave]: recuperoTurno,
|
|
40865
|
+
[cuentaCompleta.clave]: cuentaCompleta
|
|
40714
40866
|
};
|
|
40715
40867
|
function getWorkflow(clave) {
|
|
40716
40868
|
return WORKFLOWS[clave] ?? null;
|
|
@@ -41030,6 +41182,11 @@ async function abrirCaso(ctx, input2) {
|
|
|
41030
41182
|
if (prev[0]) return { caso: mapCaso(prev[0]), creado: false };
|
|
41031
41183
|
}
|
|
41032
41184
|
const referencia = str(input2.referencia);
|
|
41185
|
+
if (wf.clave === "cuenta_completa") {
|
|
41186
|
+
const bono = refExterna(input2.datos?.bono_id);
|
|
41187
|
+
if (!referencia || !/^bono:\S+$/.test(referencia)) throw new OpsError("referencia_requerida", "en cuenta_completa la referencia es obligatoria: bono:<n\xFAmero de bono>");
|
|
41188
|
+
if (bono && referencia !== `bono:${bono}`) throw new OpsError("referencia_requerida", `la referencia ${referencia} no coincide con el bono_id ${bono}`);
|
|
41189
|
+
}
|
|
41033
41190
|
if (referencia) {
|
|
41034
41191
|
const abiertos = await ctx.db.query(
|
|
41035
41192
|
`SELECT ${CASO_COLS} FROM ${t(ctx, "caso")} WHERE company_id = $1 AND workflow = $2 AND referencia = $3 AND cerrado_at IS NULL ORDER BY created_at DESC LIMIT 1`,
|
|
@@ -41124,6 +41281,35 @@ async function transicionar(ctx, input2) {
|
|
|
41124
41281
|
throw new OpsError("validacion", `accion_registrada: ${problema}`, { validaciones: [{ regla: "accion_registrada", ok: false, detalle: problema }], estado: caso.estado });
|
|
41125
41282
|
}
|
|
41126
41283
|
}
|
|
41284
|
+
const lecturaId = str(datos.lectura_id);
|
|
41285
|
+
if (lecturaId) {
|
|
41286
|
+
const rows = await ctx.db.query(
|
|
41287
|
+
`SELECT id, caso_id, tipo, resultado, created_at FROM ${t(ctx, "accion")} WHERE company_id = $1 AND id = $2 LIMIT 1`,
|
|
41288
|
+
[input2.companyId, lecturaId]
|
|
41289
|
+
);
|
|
41290
|
+
const lectura = rows[0];
|
|
41291
|
+
let vieja = false;
|
|
41292
|
+
if (lectura && resultado.via === "comprobar") {
|
|
41293
|
+
const ultima = await ctx.db.query(
|
|
41294
|
+
`SELECT max(created_at) AS ultima FROM ${t(ctx, "transicion")} WHERE company_id = $1 AND caso_id = $2`,
|
|
41295
|
+
[input2.companyId, caso.id]
|
|
41296
|
+
);
|
|
41297
|
+
const desde = ultima[0]?.ultima ? Date.parse(iso(ultima[0].ultima)) : NaN;
|
|
41298
|
+
vieja = Number.isFinite(desde) && Date.parse(iso(lectura.created_at)) <= desde;
|
|
41299
|
+
}
|
|
41300
|
+
const problema = !lectura ? `lectura_id ${lecturaId} no existe en esta instituci\xF3n` : String(lectura.caso_id) !== caso.id ? `la lectura ${lecturaId} pertenece a otro caso` : !["lectura", "verificacion"].includes(String(lectura.tipo)) ? `la acci\xF3n ${lecturaId} es de tipo ${String(lectura.tipo)}; hace falta una lectura o verificaci\xF3n` : String(lectura.resultado) !== "ok" ? `la lectura ${lecturaId} termin\xF3 con resultado ${String(lectura.resultado)}` : vieja ? `la lectura ${lecturaId} es anterior al \xFAltimo cambio del caso: volver a leer el bono y registrar esa lectura` : null;
|
|
41301
|
+
if (problema) {
|
|
41302
|
+
throw new OpsError("validacion", `lectura_registrada: ${problema}`, { validaciones: [{ regla: "lectura_registrada", ok: false, detalle: problema }], estado: caso.estado });
|
|
41303
|
+
}
|
|
41304
|
+
}
|
|
41305
|
+
if (caso.workflow === "cuenta_completa" && resultado.via === "observar") {
|
|
41306
|
+
datos.faltantes = leerFaltantes(datos.faltantes) ?? [];
|
|
41307
|
+
datos.faltantes_iniciales = datos.faltantes;
|
|
41308
|
+
}
|
|
41309
|
+
if (caso.workflow === "cuenta_completa" && resultado.via === "comprobar") {
|
|
41310
|
+
datos.faltantes = leerFaltantes(datos.faltantes_leidos) ?? [];
|
|
41311
|
+
delete datos.faltantes_leidos;
|
|
41312
|
+
}
|
|
41127
41313
|
if (caso.workflow === "recupero_turno" && resultado.via === "no_acepta") {
|
|
41128
41314
|
const previas = Array.isArray(caso.datos.declinaron) ? caso.datos.declinaron.map((v) => refExterna(v)).filter((v) => v !== null) : [];
|
|
41129
41315
|
const esta = refExterna(caso.datos.solicitud_id);
|
|
@@ -41179,7 +41365,7 @@ async function transicionar(ctx, input2) {
|
|
|
41179
41365
|
};
|
|
41180
41366
|
}
|
|
41181
41367
|
var TIPOS_ACCION = ["lectura", "escritura", "verificacion"];
|
|
41182
|
-
var CONECTORES = ["api", "archivo", "pantalla", "manual"];
|
|
41368
|
+
var CONECTORES = ["api", "base", "archivo", "pantalla", "manual"];
|
|
41183
41369
|
var RESULTADOS_ACCION = ["ok", "error", "pendiente"];
|
|
41184
41370
|
async function registrarAccion(ctx, input2) {
|
|
41185
41371
|
const caso = await leerCaso(ctx, input2.companyId, input2.casoId);
|
|
@@ -41189,6 +41375,9 @@ async function registrarAccion(ctx, input2) {
|
|
|
41189
41375
|
if (!RESULTADOS_ACCION.includes(input2.resultado)) throw new OpsError("resultado_invalido", `resultado debe ser: ${RESULTADOS_ACCION.join(", ")}`);
|
|
41190
41376
|
const objetivo = str(input2.objetivo);
|
|
41191
41377
|
if (!objetivo) throw new OpsError("objetivo_requerido", "objetivo es obligatorio (qu\xE9 pantalla, endpoint o archivo se toc\xF3)");
|
|
41378
|
+
if (caso.workflow === "cuenta_completa" && input2.tipo === "escritura" && input2.actor.tipo === "agente") {
|
|
41379
|
+
throw new OpsError("politica", "en cuenta_completa el puesto solo lee: no registra escrituras sobre el sistema del hospital; lo que falta lo completa el equipo");
|
|
41380
|
+
}
|
|
41192
41381
|
if (input2.tipo === "escritura" && input2.actor.tipo === "agente") {
|
|
41193
41382
|
const modo = await obtenerModo(ctx, input2.companyId, caso.workflow);
|
|
41194
41383
|
if (!modoHabilita(modo, "ejecuta")) throw new OpsError("politica", `la pol\xEDtica vigente de ${caso.workflow} es "${modo}": un agente no puede registrar escrituras sobre el sistema del hospital`);
|
|
@@ -41275,6 +41464,7 @@ async function registrarCargaManual(ctx, input2) {
|
|
|
41275
41464
|
...valorCargado !== null ? { valor_cargado: valorCargado } : {},
|
|
41276
41465
|
...caso.datos.valor_nuevo !== void 0 ? { valor_aprobado: caso.datos.valor_nuevo } : {},
|
|
41277
41466
|
...caso.datos.vigencia_desde !== void 0 ? { vigencia_desde: caso.datos.vigencia_desde } : {},
|
|
41467
|
+
...caso.workflow === "cuenta_completa" ? { faltantes_que_completo: caso.datos.faltantes ?? [] } : {},
|
|
41278
41468
|
...comentario ? { nota: comentario } : {}
|
|
41279
41469
|
},
|
|
41280
41470
|
detalle: comentario,
|
|
@@ -41487,6 +41677,32 @@ function errorParaAgente(err) {
|
|
|
41487
41677
|
return { error: `error_interno: ${message2}`, data: { codigo: "error_interno" } };
|
|
41488
41678
|
}
|
|
41489
41679
|
|
|
41680
|
+
// src/metricas.ts
|
|
41681
|
+
var CATALOGO_VERSION = "2026-10-15.1";
|
|
41682
|
+
var METRICAS = [
|
|
41683
|
+
{ id: "turnos_dados", version: 1, estado: "validada", nombre: "Turnos dados", unidad: "turnos", definicion: "Turnos agendados en el per\xEDodo que no fueron cancelados ni bloqueados (incluye atendidos, ausentes y los que siguen pendientes)." },
|
|
41684
|
+
{ id: "turnos_atendidos", version: 1, estado: "validada", nombre: "Turnos atendidos", unidad: "turnos", definicion: "Turnos en los que hubo atenci\xF3n: en atenci\xF3n, atendido o revisado por el profesional." },
|
|
41685
|
+
{ id: "ausencias", version: 1, estado: "validada", nombre: "Ausencias", unidad: "turnos", definicion: "Turnos marcados como \xABno asisti\xF3\xBB." },
|
|
41686
|
+
{ id: "ausentismo", version: 1, estado: "validada", nombre: "Ausentismo", unidad: "%", definicion: "Ausencias sobre turnos dados. Las cancelaciones no cuentan como ausencia. Los turnos que nadie cerr\xF3 no figuran como ausencia, as\xED que con muchos turnos sin cerrar el ausentismo real es mayor." },
|
|
41687
|
+
{ id: "turnos_sin_cerrar", version: 1, estado: "propuesta", nombre: "Turnos sin cerrar", unidad: "turnos", definicion: "Turnos con fecha ya pasada que siguen en \xABpendiente\xBB o \xABanunciado\xBB: nadie los cerr\xF3 como atendidos ni como ausentes." },
|
|
41688
|
+
{ id: "cancelaciones", version: 1, estado: "validada", nombre: "Cancelaciones", unidad: "turnos", definicion: "Turnos cancelados del per\xEDodo." },
|
|
41689
|
+
{ id: "cancelaciones_mismo_dia", version: 1, estado: "validada", nombre: "Cancelaciones del mismo d\xEDa", unidad: "turnos", definicion: "Turnos cancelados el mismo d\xEDa del turno (el cupo ya casi no se puede reutilizar). Es una aproximaci\xF3n: Alephoo no guarda la fecha de cancelaci\xF3n y se usa la \xFAltima modificaci\xF3n del turno." },
|
|
41690
|
+
{ id: "tasa_cancelacion", version: 1, estado: "validada", nombre: "Tasa de cancelaci\xF3n", unidad: "%", definicion: "Cancelaciones sobre lo agendado (turnos dados m\xE1s cancelados)." },
|
|
41691
|
+
{ id: "turnos_agendados", version: 1, estado: "validada", nombre: "Turnos agendados", unidad: "turnos", definicion: "Turnos dados m\xE1s cancelados: todo lo que se agend\xF3 en el per\xEDodo, sin los cupos bloqueados." },
|
|
41692
|
+
{ id: "facturado", version: 1, estado: "validada", nombre: "Facturado", unidad: "$", definicion: "Importe de las facturas emitidas en el per\xEDodo, sin las anuladas, con IVA, por fecha de emisi\xF3n (la de AFIP en las electr\xF3nicas)." },
|
|
41693
|
+
{ id: "facturas_emitidas", version: 1, estado: "validada", nombre: "Facturas emitidas", unidad: "facturas", definicion: "Cantidad de facturas emitidas en el per\xEDodo, sin las anuladas." },
|
|
41694
|
+
{ id: "notas_credito", version: 1, estado: "validada", nombre: "Notas de cr\xE9dito", unidad: "$", definicion: "Notas de cr\xE9dito no anuladas sobre las facturas emitidas en el per\xEDodo (se asignan al mes de la factura). Incluyen d\xE9bitos de financiadores, anulaciones parciales y ajustes propios: no son lo mismo que \xABd\xE9bitos\xBB." },
|
|
41695
|
+
{ id: "notas_debito", version: 1, estado: "validada", nombre: "Notas de d\xE9bito", unidad: "$", definicion: "Notas de d\xE9bito no anuladas sobre las facturas emitidas en el per\xEDodo." },
|
|
41696
|
+
{ id: "cobrado_de_lo_facturado", version: 1, estado: "validada", nombre: "Cobrado de lo facturado", unidad: "$", definicion: "Lo cobrado (recibos no anulados aplicados) sobre las facturas emitidas en el per\xEDodo, cualquiera sea la fecha del cobro." },
|
|
41697
|
+
{ id: "deuda_pendiente", version: 1, estado: "validada", nombre: "Deuda pendiente", unidad: "$", definicion: "Saldo sin cobrar de las facturas emitidas en el per\xEDodo que siguen pendientes (facturado menos notas de cr\xE9dito, m\xE1s notas de d\xE9bito, menos lo cobrado)." },
|
|
41698
|
+
{ id: "bonos_pendientes_de_facturar", version: 1, estado: "validada", nombre: "Bonos pendientes de facturar", unidad: "bonos", definicion: "Prestaciones (bonos) del per\xEDodo todav\xEDa no confirmadas o confirmadas y sin prefacturar ni facturar." },
|
|
41699
|
+
{ id: "monto_pendiente_de_facturar", version: 1, estado: "validada", nombre: "Monto pendiente de facturar", unidad: "$", definicion: "Importe de los bonos pendientes de facturar del per\xEDodo, por fecha de la prestaci\xF3n." },
|
|
41700
|
+
{ id: "debitos_financiador", version: 1, estado: "a_definir", nombre: "D\xE9bitos de financiadores", unidad: "$", definicion: "Sin definir: el hospital tiene que decidir si cuenta todas las notas de cr\xE9dito o solo las que responden a un d\xE9bito del financiador. Hasta entonces no se publica como n\xFAmero oficial." }
|
|
41701
|
+
];
|
|
41702
|
+
function metricaPorId(id) {
|
|
41703
|
+
return METRICAS.find((m) => m.id === id) ?? null;
|
|
41704
|
+
}
|
|
41705
|
+
|
|
41490
41706
|
// src/tableros.ts
|
|
41491
41707
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
41492
41708
|
var TIPOS = ["indicador", "barras", "linea", "tabla", "texto"];
|
|
@@ -41533,6 +41749,79 @@ function numero2(v, campo, problemas) {
|
|
|
41533
41749
|
function ancho(v) {
|
|
41534
41750
|
return v === 1 || v === 2 || v === 3 ? v : void 0;
|
|
41535
41751
|
}
|
|
41752
|
+
function aNumero(v) {
|
|
41753
|
+
if (typeof v === "number") return Number.isFinite(v) ? v : null;
|
|
41754
|
+
if (typeof v !== "string") return null;
|
|
41755
|
+
let s = v.trim().replace(/[$\s%]/g, "");
|
|
41756
|
+
if (s === "") return null;
|
|
41757
|
+
if (s.includes(",")) s = s.replace(/\./g, "").replace(",", ".");
|
|
41758
|
+
else if (/^-?\d{1,3}(\.\d{3})+$/.test(s)) s = s.replace(/\./g, "");
|
|
41759
|
+
const n = Number(s);
|
|
41760
|
+
return Number.isFinite(n) ? n : null;
|
|
41761
|
+
}
|
|
41762
|
+
var ES_VARIACION = /varia|cambio|crec|difer|dif\.|Δ|meta|cumpl|\bvs\b|versus/i;
|
|
41763
|
+
function controlarPorcentaje(valor, donde, problemas) {
|
|
41764
|
+
const n = aNumero(valor);
|
|
41765
|
+
if (n !== null && (n < 0 || n > 100)) problemas.push(`${donde} es un porcentaje y vale ${n}: tiene que estar entre 0 y 100 (si es una variaci\xF3n o un cumplimiento sobre meta, decirlo en el t\xEDtulo)`);
|
|
41766
|
+
}
|
|
41767
|
+
var MAX_CONSULTA = 8e3;
|
|
41768
|
+
function leerCalculo(v, ref, problemas) {
|
|
41769
|
+
if (v === void 0 || v === null) return { origen: "exploratorio" };
|
|
41770
|
+
if (typeof v !== "object" || Array.isArray(v)) {
|
|
41771
|
+
problemas.push(`${ref}.calculo tiene que ser el bloque \xABcalculo\xBB que devuelve calcular.mjs`);
|
|
41772
|
+
return { origen: "exploratorio" };
|
|
41773
|
+
}
|
|
41774
|
+
const o = v;
|
|
41775
|
+
const lista = arr(o.metricas);
|
|
41776
|
+
if (lista.length === 0) problemas.push(`${ref}.calculo.metricas est\xE1 vac\xEDo`);
|
|
41777
|
+
let propuesta = false;
|
|
41778
|
+
const metricas = [];
|
|
41779
|
+
lista.slice(0, 12).forEach((m, j) => {
|
|
41780
|
+
const mo = m && typeof m === "object" ? m : {};
|
|
41781
|
+
const id = typeof mo.id === "string" ? mo.id : "";
|
|
41782
|
+
const version2 = Number(mo.version);
|
|
41783
|
+
const def2 = metricaPorId(id);
|
|
41784
|
+
if (!def2) {
|
|
41785
|
+
problemas.push(`${ref}.calculo.metricas[${j}]: \xAB${id}\xBB no est\xE1 en el cat\xE1logo de m\xE9tricas; un n\xFAmero fuera del cat\xE1logo se publica sin calculo (queda como exploratorio)`);
|
|
41786
|
+
return;
|
|
41787
|
+
}
|
|
41788
|
+
if (def2.version !== version2) problemas.push(`${ref}.calculo.metricas[${j}]: \xAB${id}\xBB versi\xF3n ${Number.isFinite(version2) ? version2 : "?"} no es la vigente (${def2.version}): recalcular con calcular.mjs`);
|
|
41789
|
+
if (def2.estado === "a_definir") problemas.push(`${ref}.calculo.metricas[${j}]: \xAB${id}\xBB todav\xEDa no tiene definici\xF3n aprobada y no se publica como n\xFAmero oficial`);
|
|
41790
|
+
if (def2.estado === "propuesta") propuesta = true;
|
|
41791
|
+
metricas.push({ id, version: def2.version });
|
|
41792
|
+
});
|
|
41793
|
+
const huella = typeof o.huella === "string" && /^[0-9a-f]{16}$/.test(o.huella) ? o.huella : "";
|
|
41794
|
+
if (!huella) problemas.push(`${ref}.calculo.huella falta o no tiene la forma de calcular.mjs (16 caracteres hexadecimales)`);
|
|
41795
|
+
const parametros = o.parametros && typeof o.parametros === "object" && !Array.isArray(o.parametros) ? o.parametros : {};
|
|
41796
|
+
if (JSON.stringify(parametros).length > 2e3) problemas.push(`${ref}.calculo.parametros es demasiado largo`);
|
|
41797
|
+
const consulta = typeof o.consulta === "string" && o.consulta.trim() ? o.consulta.slice(0, MAX_CONSULTA) : void 0;
|
|
41798
|
+
const catalogo = typeof o.catalogo === "string" ? o.catalogo.slice(0, 40) : "";
|
|
41799
|
+
return { calculo: { catalogo, metricas, parametros, huella, ...consulta ? { consulta } : {} }, origen: propuesta ? "propuesta" : "certificado" };
|
|
41800
|
+
}
|
|
41801
|
+
var esTotal = (c) => typeof c === "string" && /^total\b/i.test(c.trim());
|
|
41802
|
+
var esSubtotal = (c) => typeof c === "string" && /^subtotal\b/i.test(c.trim());
|
|
41803
|
+
function controlarTabla(ref, columnas, filas, problemas) {
|
|
41804
|
+
columnas.forEach((col, j) => {
|
|
41805
|
+
if (!col.includes("%") || ES_VARIACION.test(col)) return;
|
|
41806
|
+
filas.forEach((f, i) => controlarPorcentaje(f[j], `${ref}.filas[${i}] \xAB${col}\xBB`, problemas));
|
|
41807
|
+
});
|
|
41808
|
+
const totales = filas.filter((f) => esTotal(f[0]));
|
|
41809
|
+
if (totales.length !== 1) return;
|
|
41810
|
+
const total = totales[0];
|
|
41811
|
+
const detalle = filas.filter((f) => !esTotal(f[0]) && !esSubtotal(f[0]));
|
|
41812
|
+
if (detalle.length === 0) return;
|
|
41813
|
+
for (let j = 1; j < columnas.length; j += 1) {
|
|
41814
|
+
if (columnas[j].includes("%")) continue;
|
|
41815
|
+
const esperado = aNumero(total[j]);
|
|
41816
|
+
if (esperado === null) continue;
|
|
41817
|
+
const valores = detalle.map((f) => f[j]);
|
|
41818
|
+
if (valores.some((x) => x !== null && x !== "" && aNumero(x) === null)) continue;
|
|
41819
|
+
const suma = valores.reduce((acc, x) => acc + (aNumero(x) ?? 0), 0);
|
|
41820
|
+
if (Math.abs(suma - esperado) > Math.max(0.011, Math.abs(esperado) * 1e-6)) {
|
|
41821
|
+
problemas.push(`${ref}: la columna \xAB${columnas[j]}\xBB suma ${Math.round(suma * 100) / 100} y la fila TOTAL dice ${esperado}`);
|
|
41822
|
+
}
|
|
41823
|
+
}
|
|
41824
|
+
}
|
|
41536
41825
|
function validarWidgets(entrada) {
|
|
41537
41826
|
const problemas = [];
|
|
41538
41827
|
const lista = Array.isArray(entrada) ? entrada : [];
|
|
@@ -41554,39 +41843,38 @@ function validarWidgets(entrada) {
|
|
|
41554
41843
|
const valor = typeof o.valor === "number" && Number.isFinite(o.valor) ? o.valor : texto2(o.valor, 40, `${ref}.valor`, problemas, true) ?? "";
|
|
41555
41844
|
const tono = typeof o.tono === "string" && TONOS.includes(o.tono) ? o.tono : void 0;
|
|
41556
41845
|
if (o.tono !== void 0 && !tono) problemas.push(`${ref}.tono debe ser uno de ${TONOS.join(", ")}`);
|
|
41557
|
-
|
|
41846
|
+
const unidad = texto2(o.unidad, 16, `${ref}.unidad`, problemas);
|
|
41847
|
+
if (unidad === "%" && !ES_VARIACION.test(etiqueta)) controlarPorcentaje(valor, `${ref}.valor`, problemas);
|
|
41848
|
+
const { calculo, origen } = leerCalculo(o.calculo, ref, problemas);
|
|
41849
|
+
salida.push({ tipo, etiqueta, valor, unidad, contexto: texto2(o.contexto, 160, `${ref}.contexto`, problemas), tono, ancho: a, ...calculo ? { calculo } : {}, origen });
|
|
41558
41850
|
break;
|
|
41559
41851
|
}
|
|
41560
41852
|
case "barras": {
|
|
41561
41853
|
const titulo = texto2(o.titulo, 120, `${ref}.titulo`, problemas, true) ?? "";
|
|
41562
41854
|
const series = arr(o.series);
|
|
41563
41855
|
if (series.length === 0 || series.length > 40) problemas.push(`${ref}.series debe tener entre 1 y 40 barras`);
|
|
41564
|
-
|
|
41565
|
-
|
|
41566
|
-
|
|
41567
|
-
|
|
41568
|
-
series: series.slice(0, 40).map((s, j) => {
|
|
41569
|
-
const so = s && typeof s === "object" ? s : {};
|
|
41570
|
-
return { etiqueta: texto2(so.etiqueta, 60, `${ref}.series[${j}].etiqueta`, problemas, true) ?? "", valor: numero2(so.valor, `${ref}.series[${j}].valor`, problemas) };
|
|
41571
|
-
}),
|
|
41572
|
-
ancho: a
|
|
41856
|
+
const unidad = texto2(o.unidad, 16, `${ref}.unidad`, problemas);
|
|
41857
|
+
const barras = series.slice(0, 40).map((s, j) => {
|
|
41858
|
+
const so = s && typeof s === "object" ? s : {};
|
|
41859
|
+
return { etiqueta: texto2(so.etiqueta, 60, `${ref}.series[${j}].etiqueta`, problemas, true) ?? "", valor: numero2(so.valor, `${ref}.series[${j}].valor`, problemas) };
|
|
41573
41860
|
});
|
|
41861
|
+
if (unidad === "%" && !ES_VARIACION.test(titulo)) barras.forEach((b, j) => controlarPorcentaje(b.valor, `${ref}.series[${j}].valor`, problemas));
|
|
41862
|
+
const { calculo, origen } = leerCalculo(o.calculo, ref, problemas);
|
|
41863
|
+
salida.push({ tipo, titulo, unidad, series: barras, ancho: a, ...calculo ? { calculo } : {}, origen });
|
|
41574
41864
|
break;
|
|
41575
41865
|
}
|
|
41576
41866
|
case "linea": {
|
|
41577
41867
|
const titulo = texto2(o.titulo, 120, `${ref}.titulo`, problemas, true) ?? "";
|
|
41578
41868
|
const puntos = arr(o.puntos);
|
|
41579
41869
|
if (puntos.length === 0 || puntos.length > 400) problemas.push(`${ref}.puntos debe tener entre 1 y 400 puntos`);
|
|
41580
|
-
|
|
41581
|
-
|
|
41582
|
-
|
|
41583
|
-
|
|
41584
|
-
puntos: puntos.slice(0, 400).map((p, j) => {
|
|
41585
|
-
const po = p && typeof p === "object" ? p : {};
|
|
41586
|
-
return { x: texto2(po.x, 40, `${ref}.puntos[${j}].x`, problemas, true) ?? "", y: numero2(po.y, `${ref}.puntos[${j}].y`, problemas) };
|
|
41587
|
-
}),
|
|
41588
|
-
ancho: a
|
|
41870
|
+
const unidad = texto2(o.unidad, 16, `${ref}.unidad`, problemas);
|
|
41871
|
+
const serie2 = puntos.slice(0, 400).map((p, j) => {
|
|
41872
|
+
const po = p && typeof p === "object" ? p : {};
|
|
41873
|
+
return { x: texto2(po.x, 40, `${ref}.puntos[${j}].x`, problemas, true) ?? "", y: numero2(po.y, `${ref}.puntos[${j}].y`, problemas) };
|
|
41589
41874
|
});
|
|
41875
|
+
if (unidad === "%" && !ES_VARIACION.test(titulo)) serie2.forEach((p, j) => controlarPorcentaje(p.y, `${ref}.puntos[${j}].y`, problemas));
|
|
41876
|
+
const { calculo, origen } = leerCalculo(o.calculo, ref, problemas);
|
|
41877
|
+
salida.push({ tipo, titulo, unidad, puntos: serie2, ancho: a, ...calculo ? { calculo } : {}, origen });
|
|
41590
41878
|
break;
|
|
41591
41879
|
}
|
|
41592
41880
|
case "tabla": {
|
|
@@ -41596,17 +41884,18 @@ function validarWidgets(entrada) {
|
|
|
41596
41884
|
const filas = arr(o.filas);
|
|
41597
41885
|
if (filas.length > 200) problemas.push(`${ref}.filas admite hasta 200 filas`);
|
|
41598
41886
|
const cols = columnas.slice(0, 12).map((c, j) => texto2(c, 60, `${ref}.columnas[${j}]`, problemas, true) ?? "");
|
|
41599
|
-
|
|
41600
|
-
|
|
41601
|
-
|
|
41602
|
-
|
|
41603
|
-
filas: filas.slice(0, 200).map((f, j) => {
|
|
41604
|
-
const celdas = arr(f);
|
|
41605
|
-
if (celdas.length !== cols.length) problemas.push(`${ref}.filas[${j}] tiene ${celdas.length} celdas y la tabla ${cols.length} columnas`);
|
|
41606
|
-
return celdas.slice(0, cols.length).map((c) => typeof c === "number" && Number.isFinite(c) ? c : c === null || c === void 0 ? null : String(c).slice(0, 200));
|
|
41607
|
-
}),
|
|
41608
|
-
ancho: a
|
|
41887
|
+
const celdasTabla = filas.slice(0, 200).map((f, j) => {
|
|
41888
|
+
const celdas = arr(f);
|
|
41889
|
+
if (celdas.length !== cols.length) problemas.push(`${ref}.filas[${j}] tiene ${celdas.length} celdas y la tabla ${cols.length} columnas`);
|
|
41890
|
+
return celdas.slice(0, cols.length).map((c) => typeof c === "number" && Number.isFinite(c) ? c : c === null || c === void 0 ? null : String(c).slice(0, 200));
|
|
41609
41891
|
});
|
|
41892
|
+
controlarTabla(ref, cols, celdasTabla, problemas);
|
|
41893
|
+
const { calculo, origen } = leerCalculo(o.calculo, ref, problemas);
|
|
41894
|
+
const por = calculo && Array.isArray(calculo.parametros.por) ? calculo.parametros.por : [];
|
|
41895
|
+
if (calculo && por.length > 0 && !celdasTabla.some((f) => esTotal(f[0]))) {
|
|
41896
|
+
problemas.push(`${ref}: una tabla con desglose de m\xE9tricas del cat\xE1logo lleva la fila TOTAL que devuelve calcular.mjs (primera celda \xABTOTAL\xBB)`);
|
|
41897
|
+
}
|
|
41898
|
+
salida.push({ tipo, titulo, columnas: cols, filas: celdasTabla, ancho: a, ...calculo ? { calculo } : {}, origen });
|
|
41610
41899
|
break;
|
|
41611
41900
|
}
|
|
41612
41901
|
case "texto": {
|
|
@@ -41932,14 +42221,21 @@ async function manejarApi(ctx, input2) {
|
|
|
41932
42221
|
return respuesta(200, await registrarCargaManual(ctx, { companyId, casoId: input2.params.casoId ?? "", valorCargado: body.valorCargado, comentario: str2(body.comentario), persona: decididoPor, idempotencyKey: str2(body.idempotencyKey), actor }));
|
|
41933
42222
|
case "respuesta-paciente":
|
|
41934
42223
|
return respuesta(200, await registrarRespuestaPaciente(ctx, { companyId, casoId: input2.params.casoId ?? "", acepto: body.acepto === true, comentario: str2(body.comentario), persona: decididoPor, idempotencyKey: str2(body.idempotencyKey), actor }));
|
|
42224
|
+
case "no-corresponde": {
|
|
42225
|
+
if (actor.tipo !== "humano") return respuesta(403, { error: "actor_no_permitido", motivo: "no_corresponde lo decide una persona del hospital" });
|
|
42226
|
+
if (!str2(body.motivo)) return respuesta(422, { error: "validacion", motivo: "motivo_presente: no_corresponde requiere motivo" });
|
|
42227
|
+
return respuesta(200, await transicionar(ctx, { companyId, casoId: input2.params.casoId ?? "", via: "no_corresponde", motivo: motivoConPersona(str2(body.motivo)), datos: conPersona(obj(body.datos)), idempotencyKey: str2(body.idempotencyKey), actor }));
|
|
42228
|
+
}
|
|
41935
42229
|
case "version":
|
|
41936
|
-
return respuesta(200, { version: manifest_default.version, capacidades: ["carga-manual", "recupero-turnos", "ofrecer-primero"], workflows: listarWorkflows().map((w) => w.clave) });
|
|
42230
|
+
return respuesta(200, { version: manifest_default.version, capacidades: ["carga-manual", "recupero-turnos", "ofrecer-primero", "cuenta-completa", "metricas-certificadas"], workflows: listarWorkflows().map((w) => w.clave) });
|
|
41937
42231
|
case "politicas":
|
|
41938
42232
|
return respuesta(200, { politicas: await listarPoliticas(ctx, companyId) });
|
|
41939
42233
|
case "serie":
|
|
41940
42234
|
return respuesta(200, await serie(ctx, companyId, numOrNull(qs("dias"))));
|
|
41941
42235
|
case "historial-politicas":
|
|
41942
42236
|
return respuesta(200, { cambios: await historialPoliticas(ctx, companyId, numOrNull(qs("limit"))) });
|
|
42237
|
+
case "metricas":
|
|
42238
|
+
return respuesta(200, { version: CATALOGO_VERSION, metricas: METRICAS });
|
|
41943
42239
|
case "tableros":
|
|
41944
42240
|
return respuesta(200, { tableros: await listarTableros(ctx, companyId) });
|
|
41945
42241
|
case "tablero-publicado": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "paperclip-plugin-autopilot-ops",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Autopilot Care · operación autónoma gobernada para instituciones de salud: workflows determinísticos, validaciones, registro de negocio, políticas (observa/propone/ejecuta/corte) y tablero del director, como plugin de Paperclip.",
|
|
6
6
|
"files": [
|