paperclip-plugin-autopilot-ops 0.1.6 → 0.1.8
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 +21 -5
- package/dist/ui/index.js +21 -5
- package/dist/worker.js +319 -35
- package/migrations/003_opciones_politica.sql +6 -0
- 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.8";
|
|
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" },
|
|
@@ -221,6 +221,22 @@ var manifest = {
|
|
|
221
221
|
capability: "api.routes.register",
|
|
222
222
|
companyResolution: { from: "body", key: "companyId" }
|
|
223
223
|
},
|
|
224
|
+
{
|
|
225
|
+
routeKey: "respuesta-paciente",
|
|
226
|
+
method: "POST",
|
|
227
|
+
path: "/casos/:casoId/respuesta",
|
|
228
|
+
auth: "board",
|
|
229
|
+
capability: "api.routes.register",
|
|
230
|
+
companyResolution: { from: "body", key: "companyId" }
|
|
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
|
+
},
|
|
224
240
|
{
|
|
225
241
|
routeKey: "version",
|
|
226
242
|
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.8";
|
|
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" },
|
|
@@ -229,6 +229,22 @@ var manifest = {
|
|
|
229
229
|
capability: "api.routes.register",
|
|
230
230
|
companyResolution: { from: "body", key: "companyId" }
|
|
231
231
|
},
|
|
232
|
+
{
|
|
233
|
+
routeKey: "respuesta-paciente",
|
|
234
|
+
method: "POST",
|
|
235
|
+
path: "/casos/:casoId/respuesta",
|
|
236
|
+
auth: "board",
|
|
237
|
+
capability: "api.routes.register",
|
|
238
|
+
companyResolution: { from: "body", key: "companyId" }
|
|
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
|
+
},
|
|
232
248
|
{
|
|
233
249
|
routeKey: "version",
|
|
234
250
|
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.8";
|
|
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" },
|
|
@@ -40153,6 +40153,22 @@ var manifest = {
|
|
|
40153
40153
|
capability: "api.routes.register",
|
|
40154
40154
|
companyResolution: { from: "body", key: "companyId" }
|
|
40155
40155
|
},
|
|
40156
|
+
{
|
|
40157
|
+
routeKey: "respuesta-paciente",
|
|
40158
|
+
method: "POST",
|
|
40159
|
+
path: "/casos/:casoId/respuesta",
|
|
40160
|
+
auth: "board",
|
|
40161
|
+
capability: "api.routes.register",
|
|
40162
|
+
companyResolution: { from: "body", key: "companyId" }
|
|
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
|
+
},
|
|
40156
40172
|
{
|
|
40157
40173
|
routeKey: "version",
|
|
40158
40174
|
method: "GET",
|
|
@@ -40470,6 +40486,26 @@ function fueraDePreferencia(preferencia, hora) {
|
|
|
40470
40486
|
if (p.includes("tarde")) return hora < "13:00";
|
|
40471
40487
|
return false;
|
|
40472
40488
|
}
|
|
40489
|
+
var MARGEN_MINUTOS_ASIGNAR = 60;
|
|
40490
|
+
function minutosHastaLaCita(l, contexto) {
|
|
40491
|
+
const hora = horaHM(l.hora);
|
|
40492
|
+
if (!fechaISO(l.fecha) || !hora || !contexto.hoy || !contexto.horaActual) return null;
|
|
40493
|
+
const cita = Date.parse(`${String(l.fecha)}T${hora}:00Z`);
|
|
40494
|
+
const ahora = Date.parse(`${contexto.hoy}T${contexto.horaActual}:00Z`);
|
|
40495
|
+
return Number.isNaN(cita) || Number.isNaN(ahora) ? null : Math.round((cita - ahora) / 6e4);
|
|
40496
|
+
}
|
|
40497
|
+
function validarMargen(caso, contexto) {
|
|
40498
|
+
const minutos = minutosHastaLaCita(caso.datos, contexto);
|
|
40499
|
+
if (minutos === null) return ok("margen_para_avisar", "sin fecha y hora para medir");
|
|
40500
|
+
return minutos >= MARGEN_MINUTOS_ASIGNAR ? ok("margen_para_avisar", `faltan ${minutos} min`) : falla("margen_para_avisar", minutos <= 0 ? "la cita ya pas\xF3" : `la cita es en ${minutos} min: hacen falta al menos ${MARGEN_MINUTOS_ASIGNAR} para que el paciente se entere`);
|
|
40501
|
+
}
|
|
40502
|
+
function validarPacienteAcepto(caso, contexto) {
|
|
40503
|
+
if (!contexto.ofrecerPrimero) return ok("paciente_acepto", "la instituci\xF3n no pide consulta previa");
|
|
40504
|
+
return caso.estado === "aceptado" ? ok("paciente_acepto", "el paciente acept\xF3") : falla("paciente_acepto", "en esta instituci\xF3n el paciente tiene que aceptar la cita antes de asignarla: primero ofrecer y esperar que acepte");
|
|
40505
|
+
}
|
|
40506
|
+
function declinadas(caso) {
|
|
40507
|
+
return Array.isArray(caso.datos.declinaron) ? caso.datos.declinaron.map((v) => refExterna(v)).filter((v) => v !== null) : [];
|
|
40508
|
+
}
|
|
40473
40509
|
function validarLugar(caso, datos, contexto) {
|
|
40474
40510
|
const l = { ...caso.datos, ...datos };
|
|
40475
40511
|
const completo = fechaISO(l.fecha) && horaHM(l.hora) !== null && texto(l.profesional) !== null && texto(l.especialidad) !== null;
|
|
@@ -40484,7 +40520,7 @@ function validarLugar(caso, datos, contexto) {
|
|
|
40484
40520
|
}
|
|
40485
40521
|
var recuperoTurno = {
|
|
40486
40522
|
clave: "recupero_turno",
|
|
40487
|
-
nombre: "Recupero de
|
|
40523
|
+
nombre: "Recupero de citas",
|
|
40488
40524
|
puesto: "Pacientes y agenda",
|
|
40489
40525
|
descripcion: "Detecta un turno que se liber\xF3 en la agenda, elige de la lista de espera (solicitudes de turno) a quien corresponde por especialidad, prioridad y antig\xFCedad, le asigna el turno en el sistema de gesti\xF3n y comprueba reley\xE9ndolo que qued\xF3 asignado.",
|
|
40490
40526
|
inicial: "detectado",
|
|
@@ -40494,6 +40530,7 @@ var recuperoTurno = {
|
|
|
40494
40530
|
"aprobado",
|
|
40495
40531
|
"ofrecido",
|
|
40496
40532
|
"aceptado",
|
|
40533
|
+
"asignando",
|
|
40497
40534
|
"asignado",
|
|
40498
40535
|
"verificado",
|
|
40499
40536
|
"con_diferencias",
|
|
@@ -40522,7 +40559,10 @@ var recuperoTurno = {
|
|
|
40522
40559
|
const espSolicitud = normalizarNombre(datos.solicitud_especialidad);
|
|
40523
40560
|
out.push(esp && espSolicitud && esp === espSolicitud ? ok("especialidad_coincide") : falla("especialidad_coincide", `la solicitud es de ${texto(datos.solicitud_especialidad) ?? "(sin especialidad)"} y el turno es de ${texto(l.especialidad) ?? "(sin especialidad)"}`));
|
|
40524
40561
|
out.push(contexto.solicitudEnOtroCaso ? falla("solicitud_libre", `esa solicitud ya tiene otro recupero en curso o asignado (${contexto.solicitudEnOtroCaso})`) : ok("solicitud_libre"));
|
|
40525
|
-
const
|
|
40562
|
+
const yaNo = declinadas(caso);
|
|
40563
|
+
const elegida = refExterna(datos.solicitud_id);
|
|
40564
|
+
out.push(elegida && yaNo.includes(elegida) ? falla("no_la_rechazo", "el paciente de esa solicitud ya no acept\xF3 esta cita: proponer a la siguiente") : ok("no_la_rechazo"));
|
|
40565
|
+
const { posicion, total } = posicionEnCola(datos, [...contexto.solicitudesEnCurso ?? [], ...yaNo]);
|
|
40526
40566
|
if (!leerCola(datos.cola)) out.push(falla("orden_de_la_cola", "cola es obligatoria: la lista de espera pendiente de esa especialidad tal como se vio, [{solicitud_id, prioridad, alta}]"));
|
|
40527
40567
|
else if (posicion === null) out.push(falla("orden_de_la_cola", "la solicitud elegida no est\xE1 en la cola informada"));
|
|
40528
40568
|
else if (posicion > 1 && !texto(datos.justificacion)) out.push(falla("orden_de_la_cola", `es la ${posicion}.\xAA de ${total} en la cola: pasarla adelante de las anteriores requiere justificacion`));
|
|
@@ -40534,7 +40574,7 @@ var recuperoTurno = {
|
|
|
40534
40574
|
aSinExcepcion: "aprobado",
|
|
40535
40575
|
excepciones: (caso, datos, contexto) => {
|
|
40536
40576
|
const l = { ...caso.datos, ...datos };
|
|
40537
|
-
const { posicion, total } = posicionEnCola(datos, contexto.solicitudesEnCurso);
|
|
40577
|
+
const { posicion, total } = posicionEnCola(datos, [...contexto.solicitudesEnCurso ?? [], ...declinadas(caso)]);
|
|
40538
40578
|
const pedido = normalizarNombre(datos.solicitud_profesional);
|
|
40539
40579
|
const profesional = normalizarNombre(l.profesional);
|
|
40540
40580
|
const paraHoy = Boolean(contexto.hoy && l.fecha === contexto.hoy);
|
|
@@ -40574,8 +40614,11 @@ var recuperoTurno = {
|
|
|
40574
40614
|
a: "ofrecido",
|
|
40575
40615
|
actor: ["agente", "humano"],
|
|
40576
40616
|
nivelRequerido: "ejecuta",
|
|
40577
|
-
descripcion: "Ofrecerle
|
|
40578
|
-
validar: (
|
|
40617
|
+
descripcion: "Ofrecerle la cita al paciente antes de asignarla (obligatorio donde la instituci\xF3n pide consulta previa).",
|
|
40618
|
+
validar: (caso, datos, contexto) => [
|
|
40619
|
+
texto(datos.canal) ? ok("canal_informado") : falla("canal_informado", "ofrecer requiere canal (tel\xE9fono, WhatsApp, correo)"),
|
|
40620
|
+
validarMargen(caso, contexto)
|
|
40621
|
+
]
|
|
40579
40622
|
},
|
|
40580
40623
|
{
|
|
40581
40624
|
via: "aceptar",
|
|
@@ -40596,16 +40639,34 @@ var recuperoTurno = {
|
|
|
40596
40639
|
validar: (_caso, datos) => [texto(datos.motivo) ? ok("motivo_presente") : falla("motivo_presente", "no_acepta requiere motivo (no contest\xF3, no puede ese d\xEDa\u2026)")]
|
|
40597
40640
|
},
|
|
40598
40641
|
{
|
|
40599
|
-
|
|
40642
|
+
// El plugin autoriza ANTES de que se escriba en el sistema del hospital: la cita está a tiempo
|
|
40643
|
+
// y, donde la institución lo pide, el paciente aceptó. Sin esta autorización el agente no escribe.
|
|
40644
|
+
via: "autorizar",
|
|
40600
40645
|
de: ["aprobado", "aceptado"],
|
|
40646
|
+
a: "asignando",
|
|
40647
|
+
actor: ["agente", "humano"],
|
|
40648
|
+
nivelRequerido: "ejecuta",
|
|
40649
|
+
descripcion: "Pedir autorizaci\xF3n para asignar la cita en el sistema de gesti\xF3n. Va siempre antes de escribir.",
|
|
40650
|
+
validar: (caso, _datos, contexto) => [
|
|
40651
|
+
...validarLugar(caso, {}, contexto),
|
|
40652
|
+
validarMargen(caso, contexto),
|
|
40653
|
+
validarPacienteAcepto(caso, contexto)
|
|
40654
|
+
]
|
|
40655
|
+
},
|
|
40656
|
+
{
|
|
40657
|
+
// Registra lo que ya se escribió: no se rechaza por la hora (la escritura ocurrió). Un agente solo
|
|
40658
|
+
// llega acá con la autorización; una persona que lo hizo a mano, desde aprobado o aceptado.
|
|
40659
|
+
via: "asignar",
|
|
40660
|
+
de: ["asignando", "aprobado", "aceptado"],
|
|
40601
40661
|
a: "asignado",
|
|
40602
40662
|
actor: ["agente", "humano"],
|
|
40603
40663
|
nivelRequerido: "ejecuta",
|
|
40604
40664
|
cargaManual: true,
|
|
40605
|
-
descripcion: "
|
|
40665
|
+
descripcion: "Registrar la cita asignada en el sistema de gesti\xF3n, con la acci\xF3n de escritura.",
|
|
40606
40666
|
validar: (caso, datos, contexto) => [
|
|
40607
|
-
|
|
40608
|
-
|
|
40667
|
+
texto(datos.accion_id) ? ok("accion_registrada") : falla("accion_registrada", "asignar requiere accion_id de la escritura registrada con ops_registrar_accion"),
|
|
40668
|
+
contexto.actor === "agente" && caso.estado !== "asignando" ? falla("escritura_autorizada", "un agente tiene que pedir autorizar antes de escribir en el sistema") : ok("escritura_autorizada"),
|
|
40669
|
+
...caso.estado === "asignando" ? [] : [validarPacienteAcepto(caso, contexto)]
|
|
40609
40670
|
]
|
|
40610
40671
|
},
|
|
40611
40672
|
{
|
|
@@ -40645,7 +40706,7 @@ var recuperoTurno = {
|
|
|
40645
40706
|
},
|
|
40646
40707
|
{
|
|
40647
40708
|
via: "descartar",
|
|
40648
|
-
de: ["detectado", "pendiente_aprobacion", "aprobado", "ofrecido", "aceptado"],
|
|
40709
|
+
de: ["detectado", "pendiente_aprobacion", "aprobado", "ofrecido", "aceptado", "asignando"],
|
|
40649
40710
|
a: "cancelado",
|
|
40650
40711
|
actor: ["agente", "humano"],
|
|
40651
40712
|
nivelRequerido: "observa",
|
|
@@ -40654,10 +40715,146 @@ var recuperoTurno = {
|
|
|
40654
40715
|
}
|
|
40655
40716
|
]
|
|
40656
40717
|
};
|
|
40718
|
+
var REGLAS_CUENTA = {
|
|
40719
|
+
autorizacion_faltante: {
|
|
40720
|
+
nombre: "Falta el n\xFAmero de autorizaci\xF3n",
|
|
40721
|
+
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)"
|
|
40722
|
+
},
|
|
40723
|
+
validacion_faltante: {
|
|
40724
|
+
nombre: "Falta la validaci\xF3n online del financiador",
|
|
40725
|
+
dondeSeCorrige: "Alephoo \u203A Facturaci\xF3n \u203A Preparar Informaci\xF3n \u203A Editar bono: \xABValidar con broker\xBB en la prestaci\xF3n"
|
|
40726
|
+
},
|
|
40727
|
+
afiliado_faltante: {
|
|
40728
|
+
nombre: "Falta el n\xFAmero de afiliado",
|
|
40729
|
+
dondeSeCorrige: "Alephoo \u203A Facturaci\xF3n \u203A Preparar Informaci\xF3n \u203A Editar bono: n\xFAmero de afiliado (tambi\xE9n actualiza el padr\xF3n del paciente)"
|
|
40730
|
+
},
|
|
40731
|
+
orden_faltante: {
|
|
40732
|
+
nombre: "Faltan los datos de la orden m\xE9dica",
|
|
40733
|
+
dondeSeCorrige: "Alephoo \u203A Facturaci\xF3n \u203A Preparar Informaci\xF3n \u203A Editar bono: m\xE9dico solicitante y fecha de la receta de la prestaci\xF3n"
|
|
40734
|
+
},
|
|
40735
|
+
prestacion_sin_valor: {
|
|
40736
|
+
nombre: "Prestaci\xF3n valorizada en $0",
|
|
40737
|
+
dondeSeCorrige: "Alephoo \u203A Facturaci\xF3n \u203A Configuraci\xF3n de convenios: cargar el valor de la pr\xE1ctica; despu\xE9s Facturaci\xF3n \u203A Actualizar Bonos"
|
|
40738
|
+
},
|
|
40739
|
+
epicrisis_faltante: {
|
|
40740
|
+
nombre: "Internaci\xF3n con egreso y sin epicrisis",
|
|
40741
|
+
dondeSeCorrige: "Alephoo \u203A historia cl\xEDnica del paciente internado \u203A Epicrisis"
|
|
40742
|
+
}
|
|
40743
|
+
};
|
|
40744
|
+
var BONO_POR_FACTURAR = ["no_confirmado", "confirmado", "prefacturado"];
|
|
40745
|
+
var BONO_FACTURADO = ["facturado", "refacturado"];
|
|
40746
|
+
function leerFaltantes(v) {
|
|
40747
|
+
if (!Array.isArray(v)) return null;
|
|
40748
|
+
const out = [];
|
|
40749
|
+
for (const e of v) {
|
|
40750
|
+
if (!e || typeof e !== "object") return null;
|
|
40751
|
+
const r = e;
|
|
40752
|
+
const regla = texto(r.regla);
|
|
40753
|
+
const detalle = texto(r.detalle);
|
|
40754
|
+
if (!regla || !REGLAS_CUENTA[regla] || !detalle) return null;
|
|
40755
|
+
out.push({ regla, detalle, item: refExterna(r.item) });
|
|
40756
|
+
}
|
|
40757
|
+
return out;
|
|
40758
|
+
}
|
|
40759
|
+
function validarFaltantes(campo, v, alMenosUno) {
|
|
40760
|
+
const lista = leerFaltantes(v);
|
|
40761
|
+
if (!lista) {
|
|
40762
|
+
return falla("faltantes_validos", `${campo} tiene que ser una lista [{regla, detalle, item?}] con reglas del cat\xE1logo: ${Object.keys(REGLAS_CUENTA).join(", ")}`);
|
|
40763
|
+
}
|
|
40764
|
+
if (alMenosUno && lista.length === 0) return falla("faltantes_validos", `${campo} est\xE1 vac\xEDo: una cuenta sin faltantes no se abre`);
|
|
40765
|
+
return ok("faltantes_validos", lista.length === 0 ? "sin faltantes" : lista.length === 1 ? "1 faltante" : `${lista.length} faltantes`);
|
|
40766
|
+
}
|
|
40767
|
+
function estadoBono(v) {
|
|
40768
|
+
const s = texto(v);
|
|
40769
|
+
return s ? s.toLowerCase().replace(/[\s-]+/g, "_") : null;
|
|
40770
|
+
}
|
|
40771
|
+
var cuentaCompleta = {
|
|
40772
|
+
clave: "cuenta_completa",
|
|
40773
|
+
nombre: "Cuenta completa antes de facturar",
|
|
40774
|
+
puesto: "Admisi\xF3n y cuenta",
|
|
40775
|
+
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.",
|
|
40776
|
+
inicial: "detectado",
|
|
40777
|
+
estados: ["detectado", "para_completar", "completado", "con_faltantes", "completa", "facturado_incompleto", "no_corresponde", "descartado"],
|
|
40778
|
+
finalesOk: ["completa"],
|
|
40779
|
+
finalesNoOk: ["facturado_incompleto", "no_corresponde", "descartado"],
|
|
40780
|
+
minutosAbsorbidos: 10,
|
|
40781
|
+
transiciones: [
|
|
40782
|
+
{
|
|
40783
|
+
via: "observar",
|
|
40784
|
+
de: ["detectado"],
|
|
40785
|
+
a: "para_completar",
|
|
40786
|
+
actor: ["agente", "humano"],
|
|
40787
|
+
nivelRequerido: "observa",
|
|
40788
|
+
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.",
|
|
40789
|
+
validar: (caso, datos) => {
|
|
40790
|
+
const l = { ...caso.datos, ...datos };
|
|
40791
|
+
const estado = estadoBono(datos.bono_estado);
|
|
40792
|
+
return [
|
|
40793
|
+
refExterna(l.bono_id) ? ok("bono_presente") : falla("bono_presente", "observar requiere bono_id: el n\xFAmero de bono en el sistema del hospital"),
|
|
40794
|
+
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"}`),
|
|
40795
|
+
validarFaltantes("faltantes", datos.faltantes, true),
|
|
40796
|
+
texto(datos.lectura_id) ? ok("lectura_registrada") : falla("lectura_registrada", "observar requiere lectura_id: la lectura de la base registrada con ops_registrar_accion"),
|
|
40797
|
+
texto(datos.fuente) ? ok("fuente_presente") : falla("fuente_presente", "fuente (base y consulta de donde salen los faltantes) es obligatoria")
|
|
40798
|
+
];
|
|
40799
|
+
}
|
|
40800
|
+
},
|
|
40801
|
+
{
|
|
40802
|
+
via: "completar",
|
|
40803
|
+
de: ["para_completar", "con_faltantes"],
|
|
40804
|
+
a: "completado",
|
|
40805
|
+
actor: ["humano"],
|
|
40806
|
+
cargaManual: true,
|
|
40807
|
+
descripcion: "Una persona del hospital complet\xF3 en el sistema lo que faltaba. El puesto lo comprueba volviendo a leer.",
|
|
40808
|
+
validar: (_caso, datos) => [
|
|
40809
|
+
texto(datos.accion_id) ? ok("accion_registrada") : falla("accion_registrada", "completar requiere accion_id de la carga registrada")
|
|
40810
|
+
]
|
|
40811
|
+
},
|
|
40812
|
+
{
|
|
40813
|
+
// Lo decide el plugin con lo que se volvió a leer: sin faltantes, completa; facturada con faltantes, sale con riesgo de débito.
|
|
40814
|
+
via: "comprobar",
|
|
40815
|
+
de: ["para_completar", "completado", "con_faltantes"],
|
|
40816
|
+
a: (caso, datos) => {
|
|
40817
|
+
const faltan = leerFaltantes(datos.faltantes_leidos) ?? [];
|
|
40818
|
+
if (faltan.length === 0) return "completa";
|
|
40819
|
+
if (BONO_FACTURADO.includes(estadoBono(datos.bono_estado_leido) ?? "")) return "facturado_incompleto";
|
|
40820
|
+
return caso.estado === "completado" ? "con_faltantes" : caso.estado;
|
|
40821
|
+
},
|
|
40822
|
+
actor: ["agente", "humano", "sistema"],
|
|
40823
|
+
nivelRequerido: "observa",
|
|
40824
|
+
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.",
|
|
40825
|
+
validar: (_caso, datos) => {
|
|
40826
|
+
const estado = estadoBono(datos.bono_estado_leido);
|
|
40827
|
+
return [
|
|
40828
|
+
validarFaltantes("faltantes_leidos", datos.faltantes_leidos, false),
|
|
40829
|
+
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)`),
|
|
40830
|
+
texto(datos.lectura_id) ? ok("lectura_registrada") : falla("lectura_registrada", "comprobar requiere lectura_id: la lectura de la base registrada con ops_registrar_accion")
|
|
40831
|
+
];
|
|
40832
|
+
}
|
|
40833
|
+
},
|
|
40834
|
+
{
|
|
40835
|
+
via: "no_corresponde",
|
|
40836
|
+
de: ["para_completar", "completado", "con_faltantes"],
|
|
40837
|
+
a: "no_corresponde",
|
|
40838
|
+
actor: ["humano"],
|
|
40839
|
+
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.",
|
|
40840
|
+
validar: (_caso, datos) => [texto(datos.motivo) ? ok("motivo_presente") : falla("motivo_presente", "no_corresponde requiere motivo")]
|
|
40841
|
+
},
|
|
40842
|
+
{
|
|
40843
|
+
via: "descartar",
|
|
40844
|
+
de: ["detectado", "para_completar", "completado", "con_faltantes"],
|
|
40845
|
+
a: "descartado",
|
|
40846
|
+
actor: ["agente", "humano"],
|
|
40847
|
+
nivelRequerido: "observa",
|
|
40848
|
+
descripcion: "El bono se elimin\xF3, se anul\xF3 o qued\xF3 como no cobrable: ya no hay nada que completar.",
|
|
40849
|
+
validar: (_caso, datos) => [texto(datos.motivo) ? ok("motivo_presente") : falla("motivo_presente", "descartar requiere motivo")]
|
|
40850
|
+
}
|
|
40851
|
+
]
|
|
40852
|
+
};
|
|
40657
40853
|
var WORKFLOWS = {
|
|
40658
40854
|
[actualizacionArancel.clave]: actualizacionArancel,
|
|
40659
40855
|
[tareaOperativa.clave]: tareaOperativa,
|
|
40660
|
-
[recuperoTurno.clave]: recuperoTurno
|
|
40856
|
+
[recuperoTurno.clave]: recuperoTurno,
|
|
40857
|
+
[cuentaCompleta.clave]: cuentaCompleta
|
|
40661
40858
|
};
|
|
40662
40859
|
function getWorkflow(clave) {
|
|
40663
40860
|
return WORKFLOWS[clave] ?? null;
|
|
@@ -40674,7 +40871,7 @@ function modoHabilita(modo, nivelRequerido) {
|
|
|
40674
40871
|
function evaluarTransicion(input2) {
|
|
40675
40872
|
const { caso, via, actor } = input2;
|
|
40676
40873
|
const datos = input2.datos ?? {};
|
|
40677
|
-
const contexto = input2.contexto ?? {};
|
|
40874
|
+
const contexto = { ...input2.contexto ?? {}, actor };
|
|
40678
40875
|
const wf = getWorkflow(caso.workflow);
|
|
40679
40876
|
if (!wf) {
|
|
40680
40877
|
return { ok: false, codigo: "workflow_desconocido", motivo: `workflow desconocido: ${caso.workflow}`, validaciones: [] };
|
|
@@ -40840,9 +41037,16 @@ async function obtenerModo(ctx, companyId, workflow) {
|
|
|
40840
41037
|
const modo = rows[0]?.modo;
|
|
40841
41038
|
return typeof modo === "string" ? modo : MODO_POR_DEFECTO;
|
|
40842
41039
|
}
|
|
41040
|
+
async function obtenerOpciones(ctx, companyId, workflow) {
|
|
41041
|
+
const rows = await ctx.db.query(
|
|
41042
|
+
`SELECT opciones FROM ${t(ctx, "politica")} WHERE company_id = $1 AND workflow = $2 LIMIT 1`,
|
|
41043
|
+
[companyId, workflow]
|
|
41044
|
+
);
|
|
41045
|
+
return jsonb(rows[0]?.opciones);
|
|
41046
|
+
}
|
|
40843
41047
|
async function listarPoliticas(ctx, companyId) {
|
|
40844
41048
|
const rows = await ctx.db.query(
|
|
40845
|
-
`SELECT workflow, modo, motivo, updated_at FROM ${t(ctx, "politica")} WHERE company_id = $1`,
|
|
41049
|
+
`SELECT workflow, modo, motivo, opciones, updated_at FROM ${t(ctx, "politica")} WHERE company_id = $1`,
|
|
40846
41050
|
[companyId]
|
|
40847
41051
|
);
|
|
40848
41052
|
const porWorkflow = new Map(rows.map((r) => [String(r.workflow), r]));
|
|
@@ -40855,40 +41059,50 @@ async function listarPoliticas(ctx, companyId) {
|
|
|
40855
41059
|
modo: r ? String(r.modo) : MODO_POR_DEFECTO,
|
|
40856
41060
|
motivo: r ? r.motivo ?? null : null,
|
|
40857
41061
|
actualizadaEn: r ? iso(r.updated_at) : null,
|
|
40858
|
-
porDefecto: !r
|
|
41062
|
+
porDefecto: !r,
|
|
41063
|
+
opciones: r ? jsonb(r.opciones) : {}
|
|
40859
41064
|
};
|
|
40860
41065
|
});
|
|
40861
41066
|
}
|
|
41067
|
+
function describirOpciones(antes, despues) {
|
|
41068
|
+
const partes = [];
|
|
41069
|
+
if (Boolean(antes.ofrecer_primero) !== Boolean(despues.ofrecer_primero)) partes.push(`consultar al paciente antes de asignar: ${despues.ofrecer_primero ? "s\xED" : "no"}`);
|
|
41070
|
+
return partes.length ? partes.join(" \xB7 ") : null;
|
|
41071
|
+
}
|
|
40862
41072
|
async function cambiarPolitica(ctx, input2) {
|
|
40863
41073
|
if (!getWorkflow(input2.workflow)) throw new OpsError("workflow_desconocido", `workflow desconocido: ${input2.workflow}`);
|
|
40864
41074
|
if (input2.actor.tipo !== "humano") throw new OpsError("actor_no_permitido", "solo un humano puede cambiar la pol\xEDtica de un workflow");
|
|
40865
41075
|
const anterior = await obtenerModo(ctx, input2.companyId, input2.workflow);
|
|
41076
|
+
const opcionesAntes = await obtenerOpciones(ctx, input2.companyId, input2.workflow);
|
|
41077
|
+
const opciones = { ...opcionesAntes, ...input2.opciones ?? {} };
|
|
41078
|
+
const cambioOpciones = describirOpciones(opcionesAntes, opciones);
|
|
41079
|
+
const motivo = [input2.motivo ?? null, cambioOpciones].filter(Boolean).join(" \xB7 ") || null;
|
|
40866
41080
|
const existe = await ctx.db.query(
|
|
40867
41081
|
`SELECT id FROM ${t(ctx, "politica")} WHERE company_id = $1 AND workflow = $2 LIMIT 1`,
|
|
40868
41082
|
[input2.companyId, input2.workflow]
|
|
40869
41083
|
);
|
|
40870
41084
|
if (existe.length > 0) {
|
|
40871
41085
|
await ctx.db.execute(
|
|
40872
|
-
`UPDATE ${t(ctx, "politica")} SET modo = $3, motivo = $4, actor_tipo = $5, actor_id = $6, updated_at = now() WHERE company_id = $1 AND workflow = $2`,
|
|
40873
|
-
[input2.companyId, input2.workflow, input2.modo,
|
|
41086
|
+
`UPDATE ${t(ctx, "politica")} SET modo = $3, motivo = $4, actor_tipo = $5, actor_id = $6, opciones = $7::jsonb, updated_at = now() WHERE company_id = $1 AND workflow = $2`,
|
|
41087
|
+
[input2.companyId, input2.workflow, input2.modo, motivo, input2.actor.tipo, input2.actor.id, JSON.stringify(opciones)]
|
|
40874
41088
|
);
|
|
40875
41089
|
} else {
|
|
40876
41090
|
await ctx.db.execute(
|
|
40877
|
-
`INSERT INTO ${t(ctx, "politica")} (id, company_id, workflow, modo, motivo, actor_tipo, actor_id) VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
|
40878
|
-
[randomUUID(), input2.companyId, input2.workflow, input2.modo,
|
|
41091
|
+
`INSERT INTO ${t(ctx, "politica")} (id, company_id, workflow, modo, motivo, actor_tipo, actor_id, opciones) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb)`,
|
|
41092
|
+
[randomUUID(), input2.companyId, input2.workflow, input2.modo, motivo, input2.actor.tipo, input2.actor.id, JSON.stringify(opciones)]
|
|
40879
41093
|
);
|
|
40880
41094
|
}
|
|
40881
41095
|
await ctx.db.execute(
|
|
40882
41096
|
`INSERT INTO ${t(ctx, "politica_historial")} (id, company_id, workflow, modo_anterior, modo_nuevo, motivo, actor_tipo, actor_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
|
|
40883
|
-
[randomUUID(), input2.companyId, input2.workflow, anterior, input2.modo,
|
|
41097
|
+
[randomUUID(), input2.companyId, input2.workflow, anterior, input2.modo, motivo, input2.actor.tipo, input2.actor.id]
|
|
40884
41098
|
);
|
|
40885
41099
|
await ctx.activity.log({
|
|
40886
41100
|
companyId: input2.companyId,
|
|
40887
|
-
message: `Autopilot Care: pol\xEDtica de ${input2.workflow} cambiada de ${anterior} a ${input2.modo}${
|
|
41101
|
+
message: `Autopilot Care: pol\xEDtica de ${input2.workflow} cambiada de ${anterior} a ${input2.modo}${motivo ? ` (${motivo})` : ""}`,
|
|
40888
41102
|
metadata: { workflow: input2.workflow, modoAnterior: anterior, modoNuevo: input2.modo, actorId: input2.actor.id }
|
|
40889
41103
|
});
|
|
40890
41104
|
const wf = getWorkflow(input2.workflow);
|
|
40891
|
-
return { workflow: wf.clave, nombre: wf.nombre, puesto: wf.puesto, modo: input2.modo, motivo
|
|
41105
|
+
return { workflow: wf.clave, nombre: wf.nombre, puesto: wf.puesto, modo: input2.modo, motivo, actualizadaEn: (/* @__PURE__ */ new Date()).toISOString(), porDefecto: false, opciones };
|
|
40892
41106
|
}
|
|
40893
41107
|
async function leerCaso(ctx, companyId, casoId) {
|
|
40894
41108
|
const rows = await ctx.db.query(
|
|
@@ -40960,6 +41174,11 @@ async function abrirCaso(ctx, input2) {
|
|
|
40960
41174
|
if (prev[0]) return { caso: mapCaso(prev[0]), creado: false };
|
|
40961
41175
|
}
|
|
40962
41176
|
const referencia = str(input2.referencia);
|
|
41177
|
+
if (wf.clave === "cuenta_completa") {
|
|
41178
|
+
const bono = refExterna(input2.datos?.bono_id);
|
|
41179
|
+
if (!referencia || !/^bono:\S+$/.test(referencia)) throw new OpsError("referencia_requerida", "en cuenta_completa la referencia es obligatoria: bono:<n\xFAmero de bono>");
|
|
41180
|
+
if (bono && referencia !== `bono:${bono}`) throw new OpsError("referencia_requerida", `la referencia ${referencia} no coincide con el bono_id ${bono}`);
|
|
41181
|
+
}
|
|
40963
41182
|
if (referencia) {
|
|
40964
41183
|
const abiertos = await ctx.db.query(
|
|
40965
41184
|
`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`,
|
|
@@ -41005,7 +41224,9 @@ function ahoraEnHospital(ahora = /* @__PURE__ */ new Date()) {
|
|
|
41005
41224
|
async function contextoDe(ctx, companyId, caso, via, datos) {
|
|
41006
41225
|
const { hoy, hora, inicioDelDia } = ahoraEnHospital();
|
|
41007
41226
|
const contexto = { hoy, horaActual: hora };
|
|
41008
|
-
if (caso.workflow !== "recupero_turno"
|
|
41227
|
+
if (caso.workflow !== "recupero_turno") return contexto;
|
|
41228
|
+
contexto.ofrecerPrimero = (await obtenerOpciones(ctx, companyId, caso.workflow)).ofrecer_primero === true;
|
|
41229
|
+
if (via !== "proponer") return contexto;
|
|
41009
41230
|
const otros = await ctx.db.query(
|
|
41010
41231
|
`SELECT id, datos->>'solicitud_id' AS solicitud_id FROM ${t(ctx, "caso")} WHERE company_id = $1 AND workflow = $2 AND id <> $3 AND datos->>'solicitud_id' IS NOT NULL AND estado NOT IN ($4, $5, $6) LIMIT 1000`,
|
|
41011
41232
|
[companyId, "recupero_turno", caso.id, "sin_candidato", "rechazado", "cancelado"]
|
|
@@ -41052,6 +41273,40 @@ async function transicionar(ctx, input2) {
|
|
|
41052
41273
|
throw new OpsError("validacion", `accion_registrada: ${problema}`, { validaciones: [{ regla: "accion_registrada", ok: false, detalle: problema }], estado: caso.estado });
|
|
41053
41274
|
}
|
|
41054
41275
|
}
|
|
41276
|
+
const lecturaId = str(datos.lectura_id);
|
|
41277
|
+
if (lecturaId) {
|
|
41278
|
+
const rows = await ctx.db.query(
|
|
41279
|
+
`SELECT id, caso_id, tipo, resultado, created_at FROM ${t(ctx, "accion")} WHERE company_id = $1 AND id = $2 LIMIT 1`,
|
|
41280
|
+
[input2.companyId, lecturaId]
|
|
41281
|
+
);
|
|
41282
|
+
const lectura = rows[0];
|
|
41283
|
+
let vieja = false;
|
|
41284
|
+
if (lectura && resultado.via === "comprobar") {
|
|
41285
|
+
const ultima = await ctx.db.query(
|
|
41286
|
+
`SELECT max(created_at) AS ultima FROM ${t(ctx, "transicion")} WHERE company_id = $1 AND caso_id = $2`,
|
|
41287
|
+
[input2.companyId, caso.id]
|
|
41288
|
+
);
|
|
41289
|
+
const desde = ultima[0]?.ultima ? Date.parse(iso(ultima[0].ultima)) : NaN;
|
|
41290
|
+
vieja = Number.isFinite(desde) && Date.parse(iso(lectura.created_at)) <= desde;
|
|
41291
|
+
}
|
|
41292
|
+
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;
|
|
41293
|
+
if (problema) {
|
|
41294
|
+
throw new OpsError("validacion", `lectura_registrada: ${problema}`, { validaciones: [{ regla: "lectura_registrada", ok: false, detalle: problema }], estado: caso.estado });
|
|
41295
|
+
}
|
|
41296
|
+
}
|
|
41297
|
+
if (caso.workflow === "cuenta_completa" && resultado.via === "observar") {
|
|
41298
|
+
datos.faltantes = leerFaltantes(datos.faltantes) ?? [];
|
|
41299
|
+
datos.faltantes_iniciales = datos.faltantes;
|
|
41300
|
+
}
|
|
41301
|
+
if (caso.workflow === "cuenta_completa" && resultado.via === "comprobar") {
|
|
41302
|
+
datos.faltantes = leerFaltantes(datos.faltantes_leidos) ?? [];
|
|
41303
|
+
delete datos.faltantes_leidos;
|
|
41304
|
+
}
|
|
41305
|
+
if (caso.workflow === "recupero_turno" && resultado.via === "no_acepta") {
|
|
41306
|
+
const previas = Array.isArray(caso.datos.declinaron) ? caso.datos.declinaron.map((v) => refExterna(v)).filter((v) => v !== null) : [];
|
|
41307
|
+
const esta = refExterna(caso.datos.solicitud_id);
|
|
41308
|
+
datos.declinaron = esta && !previas.includes(esta) ? [...previas, esta] : previas;
|
|
41309
|
+
}
|
|
41055
41310
|
const excepcionesEvaluadas = resultado.validaciones.filter((v) => v.excepcion);
|
|
41056
41311
|
if (excepcionesEvaluadas.length > 0) datos.excepciones = excepcionesEvaluadas.filter((v) => !v.ok).map((v) => v.regla);
|
|
41057
41312
|
else if (resultado.via === "proponer") datos.excepciones = [];
|
|
@@ -41102,7 +41357,7 @@ async function transicionar(ctx, input2) {
|
|
|
41102
41357
|
};
|
|
41103
41358
|
}
|
|
41104
41359
|
var TIPOS_ACCION = ["lectura", "escritura", "verificacion"];
|
|
41105
|
-
var CONECTORES = ["api", "archivo", "pantalla", "manual"];
|
|
41360
|
+
var CONECTORES = ["api", "base", "archivo", "pantalla", "manual"];
|
|
41106
41361
|
var RESULTADOS_ACCION = ["ok", "error", "pendiente"];
|
|
41107
41362
|
async function registrarAccion(ctx, input2) {
|
|
41108
41363
|
const caso = await leerCaso(ctx, input2.companyId, input2.casoId);
|
|
@@ -41112,12 +41367,19 @@ async function registrarAccion(ctx, input2) {
|
|
|
41112
41367
|
if (!RESULTADOS_ACCION.includes(input2.resultado)) throw new OpsError("resultado_invalido", `resultado debe ser: ${RESULTADOS_ACCION.join(", ")}`);
|
|
41113
41368
|
const objetivo = str(input2.objetivo);
|
|
41114
41369
|
if (!objetivo) throw new OpsError("objetivo_requerido", "objetivo es obligatorio (qu\xE9 pantalla, endpoint o archivo se toc\xF3)");
|
|
41370
|
+
if (caso.workflow === "cuenta_completa" && input2.tipo === "escritura" && input2.actor.tipo === "agente") {
|
|
41371
|
+
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");
|
|
41372
|
+
}
|
|
41115
41373
|
if (input2.tipo === "escritura" && input2.actor.tipo === "agente") {
|
|
41116
41374
|
const modo = await obtenerModo(ctx, input2.companyId, caso.workflow);
|
|
41117
41375
|
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`);
|
|
41118
41376
|
}
|
|
41119
41377
|
const id = randomUUID();
|
|
41120
|
-
const evidencia = input2.evidencia ?? {};
|
|
41378
|
+
const evidencia = { ...input2.evidencia ?? {} };
|
|
41379
|
+
delete evidencia.fuera_de_flujo;
|
|
41380
|
+
if (caso.workflow === "recupero_turno" && input2.tipo === "escritura" && input2.actor.tipo === "agente" && caso.estado !== "asignando") {
|
|
41381
|
+
evidencia.fuera_de_flujo = true;
|
|
41382
|
+
}
|
|
41121
41383
|
await ctx.db.execute(
|
|
41122
41384
|
`INSERT INTO ${t(ctx, "accion")} (id, company_id, caso_id, tipo, conector, objetivo, resultado, evidencia, detalle, actor_tipo, actor_id, run_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9, $10, $11, $12)`,
|
|
41123
41385
|
[id, input2.companyId, caso.id, input2.tipo, input2.conector, objetivo, input2.resultado, JSON.stringify(evidencia), input2.detalle ?? null, input2.actor.tipo, input2.actor.id, input2.actor.runId ?? null]
|
|
@@ -41177,8 +41439,8 @@ async function registrarCargaManual(ctx, input2) {
|
|
|
41177
41439
|
if (!sinValor && valorCargado === null) {
|
|
41178
41440
|
throw new OpsError("validacion", "valor_cargado: debe ser un n\xFAmero", { validaciones: [{ regla: "valor_cargado_numerico", ok: false, detalle: "debe ser un n\xFAmero" }], estado: caso.estado });
|
|
41179
41441
|
}
|
|
41180
|
-
const
|
|
41181
|
-
const previa = evaluarTransicion({ caso, via: def2.via, actor: "humano", datos: { accion_id: "(por registrar)" }, modo: "ejecuta", contexto:
|
|
41442
|
+
const contextoPrevio = await contextoDe(ctx, input2.companyId, caso, def2.via, {});
|
|
41443
|
+
const previa = evaluarTransicion({ caso, via: def2.via, actor: "humano", datos: { accion_id: "(por registrar)" }, modo: "ejecuta", contexto: contextoPrevio });
|
|
41182
41444
|
if (!previa.ok) throw new OpsError(previa.codigo, previa.motivo, { validaciones: previa.validaciones, estado: caso.estado });
|
|
41183
41445
|
const persona = str(input2.persona);
|
|
41184
41446
|
const comentario = str(input2.comentario);
|
|
@@ -41194,6 +41456,7 @@ async function registrarCargaManual(ctx, input2) {
|
|
|
41194
41456
|
...valorCargado !== null ? { valor_cargado: valorCargado } : {},
|
|
41195
41457
|
...caso.datos.valor_nuevo !== void 0 ? { valor_aprobado: caso.datos.valor_nuevo } : {},
|
|
41196
41458
|
...caso.datos.vigencia_desde !== void 0 ? { vigencia_desde: caso.datos.vigencia_desde } : {},
|
|
41459
|
+
...caso.workflow === "cuenta_completa" ? { faltantes_que_completo: caso.datos.faltantes ?? [] } : {},
|
|
41197
41460
|
...comentario ? { nota: comentario } : {}
|
|
41198
41461
|
},
|
|
41199
41462
|
detalle: comentario,
|
|
@@ -41223,6 +41486,16 @@ async function registrarCargaManual(ctx, input2) {
|
|
|
41223
41486
|
throw err;
|
|
41224
41487
|
}
|
|
41225
41488
|
}
|
|
41489
|
+
async function registrarRespuestaPaciente(ctx, input2) {
|
|
41490
|
+
if (input2.actor.tipo !== "humano") throw new OpsError("actor_no_permitido", "la respuesta del paciente la registra una persona del hospital; un agente usa ops_transicionar");
|
|
41491
|
+
const caso = await leerCaso(ctx, input2.companyId, input2.casoId);
|
|
41492
|
+
if (!caso) throw new OpsError("caso_inexistente", `no existe el caso ${input2.casoId} en esta instituci\xF3n`);
|
|
41493
|
+
if (caso.workflow !== "recupero_turno") throw new OpsError("estado_invalido", "solo las citas ofrecidas al paciente llevan respuesta");
|
|
41494
|
+
const persona = str(input2.persona);
|
|
41495
|
+
const comentario = str(input2.comentario);
|
|
41496
|
+
const datos = input2.acepto ? { respuesta: comentario ?? "Acept\xF3 la cita", ...persona ? { registrado_por: persona } : {} } : { motivo: comentario ?? "No acept\xF3 la cita", ...persona ? { registrado_por: persona } : {} };
|
|
41497
|
+
return transicionar(ctx, { companyId: input2.companyId, casoId: caso.id, via: input2.acepto ? "aceptar" : "no_acepta", datos, motivo: comentario, idempotencyKey: input2.idempotencyKey, actor: input2.actor });
|
|
41498
|
+
}
|
|
41226
41499
|
function finalesOk() {
|
|
41227
41500
|
return [...new Set(listarWorkflows().flatMap((w) => w.finalesOk))];
|
|
41228
41501
|
}
|
|
@@ -41647,11 +41920,14 @@ function registrarHerramientas(ctx) {
|
|
|
41647
41920
|
const detalle = await leerCasoCompleto(ctx, runCtx.companyId, casoId);
|
|
41648
41921
|
if (!detalle) throw new OpsError("caso_inexistente", `no existe el caso ${casoId} en esta instituci\xF3n`);
|
|
41649
41922
|
const disponibles = detalle.disponibles.filter((d) => d.actor.includes("agente")).map((d) => d.via);
|
|
41923
|
+
const opciones = await obtenerOpciones(ctx, runCtx.companyId, detalle.caso.workflow);
|
|
41924
|
+
const consulta = detalle.caso.workflow === "recupero_turno" ? `
|
|
41925
|
+
Consulta previa al paciente: ${opciones.ofrecer_primero === true ? "S\xCD (ofrecer y esperar que acepte antes de autorizar)" : "no"}` : "";
|
|
41650
41926
|
return {
|
|
41651
41927
|
content: `${resumenCaso(detalle.caso)}
|
|
41652
41928
|
Transiciones disponibles para agente: ${disponibles.join(", ") || "ninguna"}
|
|
41653
|
-
Transiciones registradas: ${detalle.transiciones.length}; acciones registradas: ${detalle.acciones.length}`,
|
|
41654
|
-
data: detalle
|
|
41929
|
+
Transiciones registradas: ${detalle.transiciones.length}; acciones registradas: ${detalle.acciones.length}${consulta}`,
|
|
41930
|
+
data: { ...detalle, opciones }
|
|
41655
41931
|
};
|
|
41656
41932
|
}));
|
|
41657
41933
|
ctx.tools.register(TOOL_NAMES.abrirCaso, toolSchema(TOOL_NAMES.abrirCaso), async (params, runCtx) => conErrores(async () => {
|
|
@@ -41836,8 +42112,15 @@ async function manejarApi(ctx, input2) {
|
|
|
41836
42112
|
return respuesta(200, await transicionar(ctx, { companyId, casoId: input2.params.casoId ?? "", via: "rechazar", motivo: str2(body.motivo), datos: conPersona(obj(body.datos)), actor }));
|
|
41837
42113
|
case "marcar-cargado":
|
|
41838
42114
|
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 }));
|
|
42115
|
+
case "respuesta-paciente":
|
|
42116
|
+
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 }));
|
|
42117
|
+
case "no-corresponde": {
|
|
42118
|
+
if (actor.tipo !== "humano") return respuesta(403, { error: "actor_no_permitido", motivo: "no_corresponde lo decide una persona del hospital" });
|
|
42119
|
+
if (!str2(body.motivo)) return respuesta(422, { error: "validacion", motivo: "motivo_presente: no_corresponde requiere motivo" });
|
|
42120
|
+
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
|
+
}
|
|
41839
42122
|
case "version":
|
|
41840
|
-
return respuesta(200, { version: manifest_default.version, capacidades: ["carga-manual", "recupero-turnos"], workflows: listarWorkflows().map((w) => w.clave) });
|
|
42123
|
+
return respuesta(200, { version: manifest_default.version, capacidades: ["carga-manual", "recupero-turnos", "ofrecer-primero", "cuenta-completa"], workflows: listarWorkflows().map((w) => w.clave) });
|
|
41841
42124
|
case "politicas":
|
|
41842
42125
|
return respuesta(200, { politicas: await listarPoliticas(ctx, companyId) });
|
|
41843
42126
|
case "serie":
|
|
@@ -41854,7 +42137,8 @@ async function manejarApi(ctx, input2) {
|
|
|
41854
42137
|
const workflow = str2(body.workflow);
|
|
41855
42138
|
if (!workflow) return respuesta(400, { error: "workflow_requerido" });
|
|
41856
42139
|
if (!esModo(body.modo)) return respuesta(400, { error: "modo_invalido", detalle: "modo debe ser corte, observa, propone, ejecuta o autonomo" });
|
|
41857
|
-
|
|
42140
|
+
const opciones = body.opciones && typeof body.opciones === "object" && !Array.isArray(body.opciones) ? body.opciones : null;
|
|
42141
|
+
return respuesta(200, await cambiarPolitica(ctx, { companyId, workflow, modo: body.modo, motivo: motivoConPersona(str2(body.motivo)), opciones, actor }));
|
|
41858
42142
|
}
|
|
41859
42143
|
default:
|
|
41860
42144
|
return respuesta(404, { error: "ruta_desconocida", routeKey: input2.routeKey });
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
-- Opciones de cada función por institución, además del nivel de autonomía.
|
|
2
|
+
-- recupero_turno: {"ofrecer_primero": true} → el paciente tiene que aceptar la cita
|
|
3
|
+
-- antes de que se asigne (el plugin no autoriza la escritura hasta que acepta).
|
|
4
|
+
|
|
5
|
+
ALTER TABLE plugin_autopilot_ops_1a68482ede.politica
|
|
6
|
+
ADD COLUMN opciones jsonb NOT NULL DEFAULT '{}'::jsonb;
|
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.8",
|
|
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": [
|