paperclip-plugin-autopilot-ops 0.1.8 → 0.1.10
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 +13 -5
- package/dist/ui/index.js +13 -5
- package/dist/worker.js +270 -40
- 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.10";
|
|
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, control de cuenta completa antes de facturar, 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, prestaciones sin facturar con su plazo de presentaci\xF3n, 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"), `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.',
|
|
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), `facturacion_pendiente` (una prestaci\xF3n sin facturar; misma referencia `bono:<n\xFAmero>` y mismos datos; el plugin calcula plazo_dias, vence_el y plazo_confirmado con los plazos por obra social de la instituci\xF3n). `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: '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.',
|
|
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. En `facturacion_pendiente` (solo lee): `observar` con bono_id, bono_estado, fecha_prestacion, obra_social, lectura_id (conector "base") y fuente; `comprobar` con bono_estado_leido y lectura_id (facturado/refacturado \u2192 facturada; prefacturado \u2192 en prefactura; si no, sigue por presentar); `descartar` con motivo.',
|
|
105
105
|
parametersSchema: {
|
|
106
106
|
type: "object",
|
|
107
107
|
properties: {
|
|
@@ -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: {
|
|
@@ -277,6 +277,14 @@ var manifest = {
|
|
|
277
277
|
capability: "api.routes.register",
|
|
278
278
|
companyResolution: { from: "body", key: "companyId" }
|
|
279
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
|
+
},
|
|
280
288
|
{
|
|
281
289
|
routeKey: "tableros",
|
|
282
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.10";
|
|
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, control de cuenta completa antes de facturar, 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, prestaciones sin facturar con su plazo de presentaci\xF3n, 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"), `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.',
|
|
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), `facturacion_pendiente` (una prestaci\xF3n sin facturar; misma referencia `bono:<n\xFAmero>` y mismos datos; el plugin calcula plazo_dias, vence_el y plazo_confirmado con los plazos por obra social de la instituci\xF3n). `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: '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.',
|
|
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. En `facturacion_pendiente` (solo lee): `observar` con bono_id, bono_estado, fecha_prestacion, obra_social, lectura_id (conector "base") y fuente; `comprobar` con bono_estado_leido y lectura_id (facturado/refacturado \u2192 facturada; prefacturado \u2192 en prefactura; si no, sigue por presentar); `descartar` con motivo.',
|
|
113
113
|
parametersSchema: {
|
|
114
114
|
type: "object",
|
|
115
115
|
properties: {
|
|
@@ -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: {
|
|
@@ -285,6 +285,14 @@ var manifest = {
|
|
|
285
285
|
capability: "api.routes.register",
|
|
286
286
|
companyResolution: { from: "body", key: "companyId" }
|
|
287
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
|
+
},
|
|
288
296
|
{
|
|
289
297
|
routeKey: "tableros",
|
|
290
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.10";
|
|
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, control de cuenta completa antes de facturar, 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, prestaciones sin facturar con su plazo de presentaci\xF3n, 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"), `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.',
|
|
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), `facturacion_pendiente` (una prestaci\xF3n sin facturar; misma referencia `bono:<n\xFAmero>` y mismos datos; el plugin calcula plazo_dias, vence_el y plazo_confirmado con los plazos por obra social de la instituci\xF3n). `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: '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.',
|
|
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. En `facturacion_pendiente` (solo lee): `observar` con bono_id, bono_estado, fecha_prestacion, obra_social, lectura_id (conector "base") y fuente; `comprobar` con bono_estado_leido y lectura_id (facturado/refacturado \u2192 facturada; prefacturado \u2192 en prefactura; si no, sigue por presentar); `descartar` con motivo.',
|
|
40037
40037
|
parametersSchema: {
|
|
40038
40038
|
type: "object",
|
|
40039
40039
|
properties: {
|
|
@@ -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: {
|
|
@@ -40209,6 +40209,14 @@ var manifest = {
|
|
|
40209
40209
|
capability: "api.routes.register",
|
|
40210
40210
|
companyResolution: { from: "body", key: "companyId" }
|
|
40211
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
|
+
},
|
|
40212
40220
|
{
|
|
40213
40221
|
routeKey: "tableros",
|
|
40214
40222
|
method: "GET",
|
|
@@ -40850,11 +40858,107 @@ var cuentaCompleta = {
|
|
|
40850
40858
|
}
|
|
40851
40859
|
]
|
|
40852
40860
|
};
|
|
40861
|
+
var PLAZO_PRESENTACION_POR_DEFECTO = 60;
|
|
40862
|
+
function sumarDias(fecha, dias) {
|
|
40863
|
+
const d = /* @__PURE__ */ new Date(`${fecha}T00:00:00Z`);
|
|
40864
|
+
d.setUTCDate(d.getUTCDate() + dias);
|
|
40865
|
+
return d.toISOString().slice(0, 10);
|
|
40866
|
+
}
|
|
40867
|
+
function calcularVencimiento(fechaPrestacion, obraSocial, contexto) {
|
|
40868
|
+
const nombre = normalizarNombre(obraSocial);
|
|
40869
|
+
const fijado = nombre ? Object.entries(contexto.plazosPresentacion ?? {}).find(([k]) => normalizarNombre(k) === nombre)?.[1] : void 0;
|
|
40870
|
+
const plazo = typeof fijado === "number" && Number.isInteger(fijado) && fijado > 0 && fijado <= 730 ? fijado : PLAZO_PRESENTACION_POR_DEFECTO;
|
|
40871
|
+
return { plazo_dias: plazo, vence_el: sumarDias(fechaPrestacion, plazo), plazo_confirmado: typeof fijado === "number" };
|
|
40872
|
+
}
|
|
40873
|
+
var facturacionPendiente = {
|
|
40874
|
+
clave: "facturacion_pendiente",
|
|
40875
|
+
nombre: "Prestaciones sin facturar",
|
|
40876
|
+
puesto: "Admisi\xF3n y cuenta",
|
|
40877
|
+
descripcion: "Lee en el sistema del hospital las prestaciones (bonos) que todav\xEDa no se facturaron, calcula cu\xE1ndo vence el plazo de presentaci\xF3n al financiador y avisa al equipo, primero lo que vence antes. El equipo las presenta y el puesto lo comprueba volviendo a leer. El puesto no escribe en el sistema.",
|
|
40878
|
+
inicial: "detectado",
|
|
40879
|
+
estados: ["detectado", "por_presentar", "en_prefactura", "presentado", "facturada", "no_corresponde", "descartado"],
|
|
40880
|
+
finalesOk: ["facturada"],
|
|
40881
|
+
finalesNoOk: ["no_corresponde", "descartado"],
|
|
40882
|
+
minutosAbsorbidos: 10,
|
|
40883
|
+
transiciones: [
|
|
40884
|
+
{
|
|
40885
|
+
via: "observar",
|
|
40886
|
+
de: ["detectado"],
|
|
40887
|
+
a: (_caso, datos) => estadoBono(datos.bono_estado) === "prefacturado" ? "en_prefactura" : "por_presentar",
|
|
40888
|
+
actor: ["agente", "humano"],
|
|
40889
|
+
nivelRequerido: "observa",
|
|
40890
|
+
descripcion: "Informar una prestaci\xF3n sin facturar con su fecha y obra social; el plugin calcula el plazo y el vencimiento con los plazos de la instituci\xF3n.",
|
|
40891
|
+
validar: (caso, datos, contexto) => {
|
|
40892
|
+
const l = { ...caso.datos, ...datos };
|
|
40893
|
+
const estado = estadoBono(datos.bono_estado);
|
|
40894
|
+
const fecha = fechaISO(l.fecha_prestacion) ? String(l.fecha_prestacion) : null;
|
|
40895
|
+
return [
|
|
40896
|
+
refExterna(l.bono_id) ? ok("bono_presente") : falla("bono_presente", "observar requiere bono_id: el n\xFAmero de bono en el sistema del hospital"),
|
|
40897
|
+
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"}`),
|
|
40898
|
+
fecha && (!contexto.hoy || fecha <= contexto.hoy) ? ok("fecha_prestacion_valida", fecha) : falla("fecha_prestacion_valida", "fecha_prestacion (AAAA-MM-DD) es obligatoria y no puede ser futura"),
|
|
40899
|
+
texto(l.obra_social) ? ok("obra_social_presente") : falla("obra_social_presente", "obra_social es obligatoria: con ella se busca el plazo de presentaci\xF3n"),
|
|
40900
|
+
texto(datos.lectura_id) ? ok("lectura_registrada") : falla("lectura_registrada", "observar requiere lectura_id: la lectura de la base registrada con ops_registrar_accion"),
|
|
40901
|
+
texto(datos.fuente) ? ok("fuente_presente") : falla("fuente_presente", "fuente (base y consulta de donde sale la prestaci\xF3n) es obligatoria")
|
|
40902
|
+
];
|
|
40903
|
+
}
|
|
40904
|
+
},
|
|
40905
|
+
{
|
|
40906
|
+
via: "presentar",
|
|
40907
|
+
de: ["por_presentar", "en_prefactura"],
|
|
40908
|
+
a: "presentado",
|
|
40909
|
+
actor: ["humano"],
|
|
40910
|
+
cargaManual: true,
|
|
40911
|
+
descripcion: "Una persona del hospital present\xF3 la prestaci\xF3n (la prefactur\xF3 y factur\xF3). El puesto lo comprueba volviendo a leer.",
|
|
40912
|
+
validar: (_caso, datos) => [
|
|
40913
|
+
texto(datos.accion_id) ? ok("accion_registrada") : falla("accion_registrada", "presentar requiere accion_id de la carga registrada")
|
|
40914
|
+
]
|
|
40915
|
+
},
|
|
40916
|
+
{
|
|
40917
|
+
// Lo decide el plugin con lo que se volvió a leer: facturada, todavía en una prefactura, o sigue sin presentar.
|
|
40918
|
+
via: "comprobar",
|
|
40919
|
+
de: ["por_presentar", "en_prefactura", "presentado"],
|
|
40920
|
+
a: (_caso, datos) => {
|
|
40921
|
+
const estado = estadoBono(datos.bono_estado_leido) ?? "";
|
|
40922
|
+
if (BONO_FACTURADO.includes(estado)) return "facturada";
|
|
40923
|
+
return estado === "prefacturado" ? "en_prefactura" : "por_presentar";
|
|
40924
|
+
},
|
|
40925
|
+
actor: ["agente", "humano", "sistema"],
|
|
40926
|
+
nivelRequerido: "observa",
|
|
40927
|
+
descripcion: "Volver a leer el bono en el sistema e informar su estado. El plugin decide: facturada, en una prefactura o todav\xEDa por presentar.",
|
|
40928
|
+
validar: (_caso, datos) => {
|
|
40929
|
+
const estado = estadoBono(datos.bono_estado_leido);
|
|
40930
|
+
return [
|
|
40931
|
+
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)`),
|
|
40932
|
+
texto(datos.lectura_id) ? ok("lectura_registrada") : falla("lectura_registrada", "comprobar requiere lectura_id: la lectura de la base registrada con ops_registrar_accion")
|
|
40933
|
+
];
|
|
40934
|
+
}
|
|
40935
|
+
},
|
|
40936
|
+
{
|
|
40937
|
+
via: "no_corresponde",
|
|
40938
|
+
de: ["por_presentar", "en_prefactura", "presentado"],
|
|
40939
|
+
a: "no_corresponde",
|
|
40940
|
+
actor: ["humano"],
|
|
40941
|
+
descripcion: "El equipo indica que esta prestaci\xF3n no se presenta al financiador (por ejemplo, la pag\xF3 el paciente o no es facturable). Queda registrado con el motivo.",
|
|
40942
|
+
validar: (_caso, datos) => [texto(datos.motivo) ? ok("motivo_presente") : falla("motivo_presente", "no_corresponde requiere motivo")]
|
|
40943
|
+
},
|
|
40944
|
+
{
|
|
40945
|
+
via: "descartar",
|
|
40946
|
+
de: ["detectado", "por_presentar", "en_prefactura", "presentado"],
|
|
40947
|
+
a: "descartado",
|
|
40948
|
+
actor: ["agente", "humano"],
|
|
40949
|
+
nivelRequerido: "observa",
|
|
40950
|
+
descripcion: "El bono se elimin\xF3, se anul\xF3 o qued\xF3 como no cobrable: ya no hay nada que presentar.",
|
|
40951
|
+
validar: (_caso, datos) => [texto(datos.motivo) ? ok("motivo_presente") : falla("motivo_presente", "descartar requiere motivo")]
|
|
40952
|
+
}
|
|
40953
|
+
]
|
|
40954
|
+
};
|
|
40955
|
+
var WORKFLOWS_SOLO_LECTURA = /* @__PURE__ */ new Set([cuentaCompleta.clave, facturacionPendiente.clave]);
|
|
40853
40956
|
var WORKFLOWS = {
|
|
40854
40957
|
[actualizacionArancel.clave]: actualizacionArancel,
|
|
40855
40958
|
[tareaOperativa.clave]: tareaOperativa,
|
|
40856
40959
|
[recuperoTurno.clave]: recuperoTurno,
|
|
40857
|
-
[cuentaCompleta.clave]: cuentaCompleta
|
|
40960
|
+
[cuentaCompleta.clave]: cuentaCompleta,
|
|
40961
|
+
[facturacionPendiente.clave]: facturacionPendiente
|
|
40858
40962
|
};
|
|
40859
40963
|
function getWorkflow(clave) {
|
|
40860
40964
|
return WORKFLOWS[clave] ?? null;
|
|
@@ -41067,14 +41171,30 @@ async function listarPoliticas(ctx, companyId) {
|
|
|
41067
41171
|
function describirOpciones(antes, despues) {
|
|
41068
41172
|
const partes = [];
|
|
41069
41173
|
if (Boolean(antes.ofrecer_primero) !== Boolean(despues.ofrecer_primero)) partes.push(`consultar al paciente antes de asignar: ${despues.ofrecer_primero ? "s\xED" : "no"}`);
|
|
41174
|
+
const plazosAntes = JSON.stringify(leerPlazos(antes.plazos));
|
|
41175
|
+
const plazosDespues = leerPlazos(despues.plazos);
|
|
41176
|
+
if (plazosAntes !== JSON.stringify(plazosDespues)) {
|
|
41177
|
+
const lista = Object.entries(plazosDespues).map(([os, d]) => `${os} ${d} d\xEDas`);
|
|
41178
|
+
partes.push(`plazos de presentaci\xF3n: ${lista.length ? lista.join(", ") : "ninguno fijado (se usa el habitual)"}`);
|
|
41179
|
+
}
|
|
41070
41180
|
return partes.length ? partes.join(" \xB7 ") : null;
|
|
41071
41181
|
}
|
|
41182
|
+
function leerPlazos(v) {
|
|
41183
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return {};
|
|
41184
|
+
const out = {};
|
|
41185
|
+
for (const [k, d] of Object.entries(v)) {
|
|
41186
|
+
const dias = typeof d === "number" ? d : typeof d === "string" ? Number(d) : NaN;
|
|
41187
|
+
if (k.trim() && Number.isInteger(dias) && dias > 0 && dias <= 730) out[k.trim().slice(0, 80)] = dias;
|
|
41188
|
+
}
|
|
41189
|
+
return out;
|
|
41190
|
+
}
|
|
41072
41191
|
async function cambiarPolitica(ctx, input2) {
|
|
41073
41192
|
if (!getWorkflow(input2.workflow)) throw new OpsError("workflow_desconocido", `workflow desconocido: ${input2.workflow}`);
|
|
41074
41193
|
if (input2.actor.tipo !== "humano") throw new OpsError("actor_no_permitido", "solo un humano puede cambiar la pol\xEDtica de un workflow");
|
|
41075
41194
|
const anterior = await obtenerModo(ctx, input2.companyId, input2.workflow);
|
|
41076
41195
|
const opcionesAntes = await obtenerOpciones(ctx, input2.companyId, input2.workflow);
|
|
41077
41196
|
const opciones = { ...opcionesAntes, ...input2.opciones ?? {} };
|
|
41197
|
+
if (opciones.plazos !== void 0) opciones.plazos = leerPlazos(opciones.plazos);
|
|
41078
41198
|
const cambioOpciones = describirOpciones(opcionesAntes, opciones);
|
|
41079
41199
|
const motivo = [input2.motivo ?? null, cambioOpciones].filter(Boolean).join(" \xB7 ") || null;
|
|
41080
41200
|
const existe = await ctx.db.query(
|
|
@@ -41174,9 +41294,9 @@ async function abrirCaso(ctx, input2) {
|
|
|
41174
41294
|
if (prev[0]) return { caso: mapCaso(prev[0]), creado: false };
|
|
41175
41295
|
}
|
|
41176
41296
|
const referencia = str(input2.referencia);
|
|
41177
|
-
if (wf.clave === "cuenta_completa") {
|
|
41297
|
+
if (wf.clave === "cuenta_completa" || wf.clave === "facturacion_pendiente") {
|
|
41178
41298
|
const bono = refExterna(input2.datos?.bono_id);
|
|
41179
|
-
if (!referencia || !/^bono:\S+$/.test(referencia)) throw new OpsError("referencia_requerida",
|
|
41299
|
+
if (!referencia || !/^bono:\S+$/.test(referencia)) throw new OpsError("referencia_requerida", `en ${wf.clave} la referencia es obligatoria: bono:<n\xFAmero de bono>`);
|
|
41180
41300
|
if (bono && referencia !== `bono:${bono}`) throw new OpsError("referencia_requerida", `la referencia ${referencia} no coincide con el bono_id ${bono}`);
|
|
41181
41301
|
}
|
|
41182
41302
|
if (referencia) {
|
|
@@ -41224,6 +41344,10 @@ function ahoraEnHospital(ahora = /* @__PURE__ */ new Date()) {
|
|
|
41224
41344
|
async function contextoDe(ctx, companyId, caso, via, datos) {
|
|
41225
41345
|
const { hoy, hora, inicioDelDia } = ahoraEnHospital();
|
|
41226
41346
|
const contexto = { hoy, horaActual: hora };
|
|
41347
|
+
if (caso.workflow === "facturacion_pendiente") {
|
|
41348
|
+
contexto.plazosPresentacion = leerPlazos((await obtenerOpciones(ctx, companyId, caso.workflow)).plazos);
|
|
41349
|
+
return contexto;
|
|
41350
|
+
}
|
|
41227
41351
|
if (caso.workflow !== "recupero_turno") return contexto;
|
|
41228
41352
|
contexto.ofrecerPrimero = (await obtenerOpciones(ctx, companyId, caso.workflow)).ofrecer_primero === true;
|
|
41229
41353
|
if (via !== "proponer") return contexto;
|
|
@@ -41302,6 +41426,10 @@ async function transicionar(ctx, input2) {
|
|
|
41302
41426
|
datos.faltantes = leerFaltantes(datos.faltantes_leidos) ?? [];
|
|
41303
41427
|
delete datos.faltantes_leidos;
|
|
41304
41428
|
}
|
|
41429
|
+
if (caso.workflow === "facturacion_pendiente" && resultado.via === "observar") {
|
|
41430
|
+
const fecha = String(datos.fecha_prestacion ?? caso.datos.fecha_prestacion ?? "");
|
|
41431
|
+
Object.assign(datos, calcularVencimiento(fecha, datos.obra_social ?? caso.datos.obra_social, contexto));
|
|
41432
|
+
}
|
|
41305
41433
|
if (caso.workflow === "recupero_turno" && resultado.via === "no_acepta") {
|
|
41306
41434
|
const previas = Array.isArray(caso.datos.declinaron) ? caso.datos.declinaron.map((v) => refExterna(v)).filter((v) => v !== null) : [];
|
|
41307
41435
|
const esta = refExterna(caso.datos.solicitud_id);
|
|
@@ -41367,8 +41495,8 @@ async function registrarAccion(ctx, input2) {
|
|
|
41367
41495
|
if (!RESULTADOS_ACCION.includes(input2.resultado)) throw new OpsError("resultado_invalido", `resultado debe ser: ${RESULTADOS_ACCION.join(", ")}`);
|
|
41368
41496
|
const objetivo = str(input2.objetivo);
|
|
41369
41497
|
if (!objetivo) throw new OpsError("objetivo_requerido", "objetivo es obligatorio (qu\xE9 pantalla, endpoint o archivo se toc\xF3)");
|
|
41370
|
-
if (caso.workflow
|
|
41371
|
-
throw new OpsError("politica",
|
|
41498
|
+
if (WORKFLOWS_SOLO_LECTURA.has(caso.workflow) && input2.tipo === "escritura" && input2.actor.tipo === "agente") {
|
|
41499
|
+
throw new OpsError("politica", `en ${caso.workflow} el puesto solo lee: no registra escrituras sobre el sistema del hospital; eso lo hace el equipo`);
|
|
41372
41500
|
}
|
|
41373
41501
|
if (input2.tipo === "escritura" && input2.actor.tipo === "agente") {
|
|
41374
41502
|
const modo = await obtenerModo(ctx, input2.companyId, caso.workflow);
|
|
@@ -41457,6 +41585,7 @@ async function registrarCargaManual(ctx, input2) {
|
|
|
41457
41585
|
...caso.datos.valor_nuevo !== void 0 ? { valor_aprobado: caso.datos.valor_nuevo } : {},
|
|
41458
41586
|
...caso.datos.vigencia_desde !== void 0 ? { vigencia_desde: caso.datos.vigencia_desde } : {},
|
|
41459
41587
|
...caso.workflow === "cuenta_completa" ? { faltantes_que_completo: caso.datos.faltantes ?? [] } : {},
|
|
41588
|
+
...caso.workflow === "facturacion_pendiente" ? { bono: caso.datos.bono_numero ?? caso.datos.bono_id, vence_el: caso.datos.vence_el } : {},
|
|
41460
41589
|
...comentario ? { nota: comentario } : {}
|
|
41461
41590
|
},
|
|
41462
41591
|
detalle: comentario,
|
|
@@ -41669,6 +41798,32 @@ function errorParaAgente(err) {
|
|
|
41669
41798
|
return { error: `error_interno: ${message2}`, data: { codigo: "error_interno" } };
|
|
41670
41799
|
}
|
|
41671
41800
|
|
|
41801
|
+
// src/metricas.ts
|
|
41802
|
+
var CATALOGO_VERSION = "2026-10-15.2";
|
|
41803
|
+
var METRICAS = [
|
|
41804
|
+
{ 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)." },
|
|
41805
|
+
{ 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." },
|
|
41806
|
+
{ id: "ausencias", version: 1, estado: "validada", nombre: "Ausencias", unidad: "turnos", definicion: "Turnos marcados como \xABno asisti\xF3\xBB." },
|
|
41807
|
+
{ 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." },
|
|
41808
|
+
{ id: "turnos_sin_cerrar", version: 1, estado: "validada", 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." },
|
|
41809
|
+
{ id: "cancelaciones", version: 1, estado: "validada", nombre: "Cancelaciones", unidad: "turnos", definicion: "Turnos cancelados del per\xEDodo." },
|
|
41810
|
+
{ 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." },
|
|
41811
|
+
{ id: "tasa_cancelacion", version: 1, estado: "validada", nombre: "Tasa de cancelaci\xF3n", unidad: "%", definicion: "Cancelaciones sobre lo agendado (turnos dados m\xE1s cancelados)." },
|
|
41812
|
+
{ 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." },
|
|
41813
|
+
{ 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)." },
|
|
41814
|
+
{ id: "facturas_emitidas", version: 1, estado: "validada", nombre: "Facturas emitidas", unidad: "facturas", definicion: "Cantidad de facturas emitidas en el per\xEDodo, sin las anuladas." },
|
|
41815
|
+
{ 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." },
|
|
41816
|
+
{ 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." },
|
|
41817
|
+
{ 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." },
|
|
41818
|
+
{ 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)." },
|
|
41819
|
+
{ 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." },
|
|
41820
|
+
{ 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." },
|
|
41821
|
+
{ id: "debitos_financiador", version: 1, estado: "a_definir", nombre: "D\xE9bitos de financiadores", unidad: "$", definicion: "No se puede calcular desde Alephoo: una nota de cr\xE9dito puede responder a un d\xE9bito del financiador o a un error de facturaci\xF3n, y Alephoo no guarda cu\xE1l es (Federico Calle, 15/10/2026). Los tableros muestran \xABnotas de cr\xE9dito\xBB, que s\xED es certificada; este n\xFAmero no se publica como oficial." }
|
|
41822
|
+
];
|
|
41823
|
+
function metricaPorId(id) {
|
|
41824
|
+
return METRICAS.find((m) => m.id === id) ?? null;
|
|
41825
|
+
}
|
|
41826
|
+
|
|
41672
41827
|
// src/tableros.ts
|
|
41673
41828
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
41674
41829
|
var TIPOS = ["indicador", "barras", "linea", "tabla", "texto"];
|
|
@@ -41715,6 +41870,79 @@ function numero2(v, campo, problemas) {
|
|
|
41715
41870
|
function ancho(v) {
|
|
41716
41871
|
return v === 1 || v === 2 || v === 3 ? v : void 0;
|
|
41717
41872
|
}
|
|
41873
|
+
function aNumero(v) {
|
|
41874
|
+
if (typeof v === "number") return Number.isFinite(v) ? v : null;
|
|
41875
|
+
if (typeof v !== "string") return null;
|
|
41876
|
+
let s = v.trim().replace(/[$\s%]/g, "");
|
|
41877
|
+
if (s === "") return null;
|
|
41878
|
+
if (s.includes(",")) s = s.replace(/\./g, "").replace(",", ".");
|
|
41879
|
+
else if (/^-?\d{1,3}(\.\d{3})+$/.test(s)) s = s.replace(/\./g, "");
|
|
41880
|
+
const n = Number(s);
|
|
41881
|
+
return Number.isFinite(n) ? n : null;
|
|
41882
|
+
}
|
|
41883
|
+
var ES_VARIACION = /varia|cambio|crec|difer|dif\.|Δ|meta|cumpl|\bvs\b|versus/i;
|
|
41884
|
+
function controlarPorcentaje(valor, donde, problemas) {
|
|
41885
|
+
const n = aNumero(valor);
|
|
41886
|
+
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)`);
|
|
41887
|
+
}
|
|
41888
|
+
var MAX_CONSULTA = 8e3;
|
|
41889
|
+
function leerCalculo(v, ref, problemas) {
|
|
41890
|
+
if (v === void 0 || v === null) return { origen: "exploratorio" };
|
|
41891
|
+
if (typeof v !== "object" || Array.isArray(v)) {
|
|
41892
|
+
problemas.push(`${ref}.calculo tiene que ser el bloque \xABcalculo\xBB que devuelve calcular.mjs`);
|
|
41893
|
+
return { origen: "exploratorio" };
|
|
41894
|
+
}
|
|
41895
|
+
const o = v;
|
|
41896
|
+
const lista = arr(o.metricas);
|
|
41897
|
+
if (lista.length === 0) problemas.push(`${ref}.calculo.metricas est\xE1 vac\xEDo`);
|
|
41898
|
+
let propuesta = false;
|
|
41899
|
+
const metricas = [];
|
|
41900
|
+
lista.slice(0, 12).forEach((m, j) => {
|
|
41901
|
+
const mo = m && typeof m === "object" ? m : {};
|
|
41902
|
+
const id = typeof mo.id === "string" ? mo.id : "";
|
|
41903
|
+
const version2 = Number(mo.version);
|
|
41904
|
+
const def2 = metricaPorId(id);
|
|
41905
|
+
if (!def2) {
|
|
41906
|
+
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)`);
|
|
41907
|
+
return;
|
|
41908
|
+
}
|
|
41909
|
+
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`);
|
|
41910
|
+
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`);
|
|
41911
|
+
if (def2.estado === "propuesta") propuesta = true;
|
|
41912
|
+
metricas.push({ id, version: def2.version });
|
|
41913
|
+
});
|
|
41914
|
+
const huella = typeof o.huella === "string" && /^[0-9a-f]{16}$/.test(o.huella) ? o.huella : "";
|
|
41915
|
+
if (!huella) problemas.push(`${ref}.calculo.huella falta o no tiene la forma de calcular.mjs (16 caracteres hexadecimales)`);
|
|
41916
|
+
const parametros = o.parametros && typeof o.parametros === "object" && !Array.isArray(o.parametros) ? o.parametros : {};
|
|
41917
|
+
if (JSON.stringify(parametros).length > 2e3) problemas.push(`${ref}.calculo.parametros es demasiado largo`);
|
|
41918
|
+
const consulta = typeof o.consulta === "string" && o.consulta.trim() ? o.consulta.slice(0, MAX_CONSULTA) : void 0;
|
|
41919
|
+
const catalogo = typeof o.catalogo === "string" ? o.catalogo.slice(0, 40) : "";
|
|
41920
|
+
return { calculo: { catalogo, metricas, parametros, huella, ...consulta ? { consulta } : {} }, origen: propuesta ? "propuesta" : "certificado" };
|
|
41921
|
+
}
|
|
41922
|
+
var esTotal = (c) => typeof c === "string" && /^total\b/i.test(c.trim());
|
|
41923
|
+
var esSubtotal = (c) => typeof c === "string" && /^subtotal\b/i.test(c.trim());
|
|
41924
|
+
function controlarTabla(ref, columnas, filas, problemas) {
|
|
41925
|
+
columnas.forEach((col, j) => {
|
|
41926
|
+
if (!col.includes("%") || ES_VARIACION.test(col)) return;
|
|
41927
|
+
filas.forEach((f, i) => controlarPorcentaje(f[j], `${ref}.filas[${i}] \xAB${col}\xBB`, problemas));
|
|
41928
|
+
});
|
|
41929
|
+
const totales = filas.filter((f) => esTotal(f[0]));
|
|
41930
|
+
if (totales.length !== 1) return;
|
|
41931
|
+
const total = totales[0];
|
|
41932
|
+
const detalle = filas.filter((f) => !esTotal(f[0]) && !esSubtotal(f[0]));
|
|
41933
|
+
if (detalle.length === 0) return;
|
|
41934
|
+
for (let j = 1; j < columnas.length; j += 1) {
|
|
41935
|
+
if (columnas[j].includes("%")) continue;
|
|
41936
|
+
const esperado = aNumero(total[j]);
|
|
41937
|
+
if (esperado === null) continue;
|
|
41938
|
+
const valores = detalle.map((f) => f[j]);
|
|
41939
|
+
if (valores.some((x) => x !== null && x !== "" && aNumero(x) === null)) continue;
|
|
41940
|
+
const suma = valores.reduce((acc, x) => acc + (aNumero(x) ?? 0), 0);
|
|
41941
|
+
if (Math.abs(suma - esperado) > Math.max(0.011, Math.abs(esperado) * 1e-6)) {
|
|
41942
|
+
problemas.push(`${ref}: la columna \xAB${columnas[j]}\xBB suma ${Math.round(suma * 100) / 100} y la fila TOTAL dice ${esperado}`);
|
|
41943
|
+
}
|
|
41944
|
+
}
|
|
41945
|
+
}
|
|
41718
41946
|
function validarWidgets(entrada) {
|
|
41719
41947
|
const problemas = [];
|
|
41720
41948
|
const lista = Array.isArray(entrada) ? entrada : [];
|
|
@@ -41736,39 +41964,38 @@ function validarWidgets(entrada) {
|
|
|
41736
41964
|
const valor = typeof o.valor === "number" && Number.isFinite(o.valor) ? o.valor : texto2(o.valor, 40, `${ref}.valor`, problemas, true) ?? "";
|
|
41737
41965
|
const tono = typeof o.tono === "string" && TONOS.includes(o.tono) ? o.tono : void 0;
|
|
41738
41966
|
if (o.tono !== void 0 && !tono) problemas.push(`${ref}.tono debe ser uno de ${TONOS.join(", ")}`);
|
|
41739
|
-
|
|
41967
|
+
const unidad = texto2(o.unidad, 16, `${ref}.unidad`, problemas);
|
|
41968
|
+
if (unidad === "%" && !ES_VARIACION.test(etiqueta)) controlarPorcentaje(valor, `${ref}.valor`, problemas);
|
|
41969
|
+
const { calculo, origen } = leerCalculo(o.calculo, ref, problemas);
|
|
41970
|
+
salida.push({ tipo, etiqueta, valor, unidad, contexto: texto2(o.contexto, 160, `${ref}.contexto`, problemas), tono, ancho: a, ...calculo ? { calculo } : {}, origen });
|
|
41740
41971
|
break;
|
|
41741
41972
|
}
|
|
41742
41973
|
case "barras": {
|
|
41743
41974
|
const titulo = texto2(o.titulo, 120, `${ref}.titulo`, problemas, true) ?? "";
|
|
41744
41975
|
const series = arr(o.series);
|
|
41745
41976
|
if (series.length === 0 || series.length > 40) problemas.push(`${ref}.series debe tener entre 1 y 40 barras`);
|
|
41746
|
-
|
|
41747
|
-
|
|
41748
|
-
|
|
41749
|
-
|
|
41750
|
-
series: series.slice(0, 40).map((s, j) => {
|
|
41751
|
-
const so = s && typeof s === "object" ? s : {};
|
|
41752
|
-
return { etiqueta: texto2(so.etiqueta, 60, `${ref}.series[${j}].etiqueta`, problemas, true) ?? "", valor: numero2(so.valor, `${ref}.series[${j}].valor`, problemas) };
|
|
41753
|
-
}),
|
|
41754
|
-
ancho: a
|
|
41977
|
+
const unidad = texto2(o.unidad, 16, `${ref}.unidad`, problemas);
|
|
41978
|
+
const barras = series.slice(0, 40).map((s, j) => {
|
|
41979
|
+
const so = s && typeof s === "object" ? s : {};
|
|
41980
|
+
return { etiqueta: texto2(so.etiqueta, 60, `${ref}.series[${j}].etiqueta`, problemas, true) ?? "", valor: numero2(so.valor, `${ref}.series[${j}].valor`, problemas) };
|
|
41755
41981
|
});
|
|
41982
|
+
if (unidad === "%" && !ES_VARIACION.test(titulo)) barras.forEach((b, j) => controlarPorcentaje(b.valor, `${ref}.series[${j}].valor`, problemas));
|
|
41983
|
+
const { calculo, origen } = leerCalculo(o.calculo, ref, problemas);
|
|
41984
|
+
salida.push({ tipo, titulo, unidad, series: barras, ancho: a, ...calculo ? { calculo } : {}, origen });
|
|
41756
41985
|
break;
|
|
41757
41986
|
}
|
|
41758
41987
|
case "linea": {
|
|
41759
41988
|
const titulo = texto2(o.titulo, 120, `${ref}.titulo`, problemas, true) ?? "";
|
|
41760
41989
|
const puntos = arr(o.puntos);
|
|
41761
41990
|
if (puntos.length === 0 || puntos.length > 400) problemas.push(`${ref}.puntos debe tener entre 1 y 400 puntos`);
|
|
41762
|
-
|
|
41763
|
-
|
|
41764
|
-
|
|
41765
|
-
|
|
41766
|
-
puntos: puntos.slice(0, 400).map((p, j) => {
|
|
41767
|
-
const po = p && typeof p === "object" ? p : {};
|
|
41768
|
-
return { x: texto2(po.x, 40, `${ref}.puntos[${j}].x`, problemas, true) ?? "", y: numero2(po.y, `${ref}.puntos[${j}].y`, problemas) };
|
|
41769
|
-
}),
|
|
41770
|
-
ancho: a
|
|
41991
|
+
const unidad = texto2(o.unidad, 16, `${ref}.unidad`, problemas);
|
|
41992
|
+
const serie2 = puntos.slice(0, 400).map((p, j) => {
|
|
41993
|
+
const po = p && typeof p === "object" ? p : {};
|
|
41994
|
+
return { x: texto2(po.x, 40, `${ref}.puntos[${j}].x`, problemas, true) ?? "", y: numero2(po.y, `${ref}.puntos[${j}].y`, problemas) };
|
|
41771
41995
|
});
|
|
41996
|
+
if (unidad === "%" && !ES_VARIACION.test(titulo)) serie2.forEach((p, j) => controlarPorcentaje(p.y, `${ref}.puntos[${j}].y`, problemas));
|
|
41997
|
+
const { calculo, origen } = leerCalculo(o.calculo, ref, problemas);
|
|
41998
|
+
salida.push({ tipo, titulo, unidad, puntos: serie2, ancho: a, ...calculo ? { calculo } : {}, origen });
|
|
41772
41999
|
break;
|
|
41773
42000
|
}
|
|
41774
42001
|
case "tabla": {
|
|
@@ -41778,17 +42005,18 @@ function validarWidgets(entrada) {
|
|
|
41778
42005
|
const filas = arr(o.filas);
|
|
41779
42006
|
if (filas.length > 200) problemas.push(`${ref}.filas admite hasta 200 filas`);
|
|
41780
42007
|
const cols = columnas.slice(0, 12).map((c, j) => texto2(c, 60, `${ref}.columnas[${j}]`, problemas, true) ?? "");
|
|
41781
|
-
|
|
41782
|
-
|
|
41783
|
-
|
|
41784
|
-
|
|
41785
|
-
filas: filas.slice(0, 200).map((f, j) => {
|
|
41786
|
-
const celdas = arr(f);
|
|
41787
|
-
if (celdas.length !== cols.length) problemas.push(`${ref}.filas[${j}] tiene ${celdas.length} celdas y la tabla ${cols.length} columnas`);
|
|
41788
|
-
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));
|
|
41789
|
-
}),
|
|
41790
|
-
ancho: a
|
|
42008
|
+
const celdasTabla = filas.slice(0, 200).map((f, j) => {
|
|
42009
|
+
const celdas = arr(f);
|
|
42010
|
+
if (celdas.length !== cols.length) problemas.push(`${ref}.filas[${j}] tiene ${celdas.length} celdas y la tabla ${cols.length} columnas`);
|
|
42011
|
+
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));
|
|
41791
42012
|
});
|
|
42013
|
+
controlarTabla(ref, cols, celdasTabla, problemas);
|
|
42014
|
+
const { calculo, origen } = leerCalculo(o.calculo, ref, problemas);
|
|
42015
|
+
const por = calculo && Array.isArray(calculo.parametros.por) ? calculo.parametros.por : [];
|
|
42016
|
+
if (calculo && por.length > 0 && !celdasTabla.some((f) => esTotal(f[0]))) {
|
|
42017
|
+
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)`);
|
|
42018
|
+
}
|
|
42019
|
+
salida.push({ tipo, titulo, columnas: cols, filas: celdasTabla, ancho: a, ...calculo ? { calculo } : {}, origen });
|
|
41792
42020
|
break;
|
|
41793
42021
|
}
|
|
41794
42022
|
case "texto": {
|
|
@@ -42120,13 +42348,15 @@ async function manejarApi(ctx, input2) {
|
|
|
42120
42348
|
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 }));
|
|
42121
42349
|
}
|
|
42122
42350
|
case "version":
|
|
42123
|
-
return respuesta(200, { version: manifest_default.version, capacidades: ["carga-manual", "recupero-turnos", "ofrecer-primero", "cuenta-completa"], workflows: listarWorkflows().map((w) => w.clave) });
|
|
42351
|
+
return respuesta(200, { version: manifest_default.version, capacidades: ["carga-manual", "recupero-turnos", "ofrecer-primero", "cuenta-completa", "metricas-certificadas", "facturacion-pendiente"], workflows: listarWorkflows().map((w) => w.clave) });
|
|
42124
42352
|
case "politicas":
|
|
42125
42353
|
return respuesta(200, { politicas: await listarPoliticas(ctx, companyId) });
|
|
42126
42354
|
case "serie":
|
|
42127
42355
|
return respuesta(200, await serie(ctx, companyId, numOrNull(qs("dias"))));
|
|
42128
42356
|
case "historial-politicas":
|
|
42129
42357
|
return respuesta(200, { cambios: await historialPoliticas(ctx, companyId, numOrNull(qs("limit"))) });
|
|
42358
|
+
case "metricas":
|
|
42359
|
+
return respuesta(200, { version: CATALOGO_VERSION, metricas: METRICAS });
|
|
42130
42360
|
case "tableros":
|
|
42131
42361
|
return respuesta(200, { tableros: await listarTableros(ctx, companyId) });
|
|
42132
42362
|
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.10",
|
|
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": [
|