paperclip-plugin-autopilot-ops 0.1.20 → 0.1.22
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 +3 -3
- package/dist/ui/index.js +3 -3
- package/dist/worker.js +502 -15
- 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.22";
|
|
4
4
|
var DB_NAMESPACE_SLUG = "autopilot_ops";
|
|
5
5
|
var PAGE_ROUTE = "autopilot";
|
|
6
6
|
var TOOL_NAMES = {
|
|
@@ -113,7 +113,7 @@ var manifest = {
|
|
|
113
113
|
{
|
|
114
114
|
name: TOOL_NAMES.abrirCaso,
|
|
115
115
|
displayName: "Abrir caso",
|
|
116
|
-
description: 'Abre un caso nuevo en un workflow, en su estado inicial. Workflows: `demanda_pendiente` (referencia obligatoria `orden:<id>`, `solicitud:<id>` o `control:<persona_id>:<regla_id>`; datos: regla_id obligatorio y la regla tiene que estar activa, persona_id, tipo de origen, estudio o especialidad o prestacion, fecha_origen AAAA-MM-DD, dias; el plugin fija regla_version, canal, intentos, vence_el; nunca nombre, documento ni tel\xE9fono del paciente),`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, internacion_id opcional; en faltantes, `responsable` {tipo profesional, id, nombre} opcional para que el plugin avise a quien resuelve), `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), `credencial_profesional` (un profesional con agenda; referencia obligatoria `profesional:<id>`; datos: profesional_id, profesional (apellido y nombre, es personal de la instituci\xF3n), matricula_nacional, matricula_provincial, codigo_provincia, especialidades). `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.',
|
|
116
|
+
description: 'Abre un caso nuevo en un workflow, en su estado inicial. Workflows: `circular_aranceles` (una circular de aranceles entera; referencia obligatoria `circular:<obra social>:<hash corto del archivo>`; datos: obra_social, convenio (nombre en el HIS), archivo {nombre, hash, paginas}, vigencia_desde AAAA-MM-DD, porcentaje_declarado y filas_declaradas si la circular los dice), `demanda_pendiente` (referencia obligatoria `orden:<id>`, `solicitud:<id>` o `control:<persona_id>:<regla_id>`; datos: regla_id obligatorio y la regla tiene que estar activa, persona_id, tipo de origen, estudio o especialidad o prestacion, fecha_origen AAAA-MM-DD, dias; el plugin fija regla_version, canal, intentos, vence_el; nunca nombre, documento ni tel\xE9fono del paciente),`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, internacion_id opcional; en faltantes, `responsable` {tipo profesional, id, nombre} opcional para que el plugin avise a quien resuelve), `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), `credencial_profesional` (un profesional con agenda; referencia obligatoria `profesional:<id>`; datos: profesional_id, profesional (apellido y nombre, es personal de la instituci\xF3n), matricula_nacional, matricula_provincial, codigo_provincia, especialidades). `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.',
|
|
117
117
|
parametersSchema: {
|
|
118
118
|
type: "object",
|
|
119
119
|
properties: {
|
|
@@ -130,7 +130,7 @@ var manifest = {
|
|
|
130
130
|
{
|
|
131
131
|
name: TOOL_NAMES.transicionar,
|
|
132
132
|
displayName: "Transicionar caso",
|
|
133
|
-
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 de la instituci\xF3n 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. En `demanda_pendiente`: `contactar` (nivel ejecuta; datos: canal igual al de la regla y accion_id de la escritura registrada; el plugin controla regla activa, intentos, espera entre intentos y horario), `responder` (respuesta: acepta|rechaza|no_contesta), `comprobar` (lectura_id de la relectura del HIS, turno_id y turno_estado del turno encontrado), `cerrar` (motivo; solo con los intentos agotados o el plazo vencido), `descartar` (motivo). `credencial_profesional` (solo lee): `verificar` con profesional_id, matricula y tipo_matricula (nacional|provincial) si hay matr\xEDcula, resultado = el objeto que devuelve verificar-matriculas.mjs tal cual ({sin_matricula, encontrada, coincide_nombre, situacion, sancion, profesion, provincia, emisor}), lectura_id (conector "api", la consulta al registro) y fuente; el plugin decide vigente u observada y fija la observaci\xF3n (sin_matricula, no_figura, otra_persona, inhabilitada, sancion); `descartar` con motivo si el profesional ya no est\xE1 activo.',
|
|
133
|
+
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 de la instituci\xF3n 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. En `circular_aranceles`: `extraer` con extraccion_a (lo que ley\xF3 el agente) y extraccion_b (la salida de leer-circular.mjs, tal cual) como [{codigo, nombre, valor_nuevo}], actual (la salida de leer-convenio.mjs: [{codigo, nombre, componentes:[{arancel, monto}], copago, tipo_copago}]), lectura_id (lectura del convenio, conector base o api), lectura_circular_id (lectura de la circular, conector archivo) y fuente; el plugin cruza las tres fuentes y decide qu\xE9 se propone, qu\xE9 se revisa y qu\xE9 es excepci\xF3n (el agente no decide eso); `proponer` sin datos; despu\xE9s de la carga, `verificar` con leidos (la salida de leer-convenio.mjs tras la carga) y lectura_id; `cancelar` con motivo. En `demanda_pendiente`: `contactar` (nivel ejecuta; datos: canal igual al de la regla y accion_id de la escritura registrada; el plugin controla regla activa, intentos, espera entre intentos y horario), `responder` (respuesta: acepta|rechaza|no_contesta), `comprobar` (lectura_id de la relectura del HIS, turno_id y turno_estado del turno encontrado), `cerrar` (motivo; solo con los intentos agotados o el plazo vencido), `descartar` (motivo). `credencial_profesional` (solo lee): `verificar` con profesional_id, matricula y tipo_matricula (nacional|provincial) si hay matr\xEDcula, resultado = el objeto que devuelve verificar-matriculas.mjs tal cual ({sin_matricula, encontrada, coincide_nombre, situacion, sancion, profesion, provincia, emisor}), lectura_id (conector "api", la consulta al registro) y fuente; el plugin decide vigente u observada y fija la observaci\xF3n (sin_matricula, no_figura, otra_persona, inhabilitada, sancion); `descartar` con motivo si el profesional ya no est\xE1 activo.',
|
|
134
134
|
parametersSchema: {
|
|
135
135
|
type: "object",
|
|
136
136
|
properties: {
|
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.22";
|
|
12
12
|
var DB_NAMESPACE_SLUG = "autopilot_ops";
|
|
13
13
|
var PAGE_ROUTE = "autopilot";
|
|
14
14
|
var TOOL_NAMES = {
|
|
@@ -121,7 +121,7 @@ var manifest = {
|
|
|
121
121
|
{
|
|
122
122
|
name: TOOL_NAMES.abrirCaso,
|
|
123
123
|
displayName: "Abrir caso",
|
|
124
|
-
description: 'Abre un caso nuevo en un workflow, en su estado inicial. Workflows: `demanda_pendiente` (referencia obligatoria `orden:<id>`, `solicitud:<id>` o `control:<persona_id>:<regla_id>`; datos: regla_id obligatorio y la regla tiene que estar activa, persona_id, tipo de origen, estudio o especialidad o prestacion, fecha_origen AAAA-MM-DD, dias; el plugin fija regla_version, canal, intentos, vence_el; nunca nombre, documento ni tel\xE9fono del paciente),`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, internacion_id opcional; en faltantes, `responsable` {tipo profesional, id, nombre} opcional para que el plugin avise a quien resuelve), `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), `credencial_profesional` (un profesional con agenda; referencia obligatoria `profesional:<id>`; datos: profesional_id, profesional (apellido y nombre, es personal de la instituci\xF3n), matricula_nacional, matricula_provincial, codigo_provincia, especialidades). `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.',
|
|
124
|
+
description: 'Abre un caso nuevo en un workflow, en su estado inicial. Workflows: `circular_aranceles` (una circular de aranceles entera; referencia obligatoria `circular:<obra social>:<hash corto del archivo>`; datos: obra_social, convenio (nombre en el HIS), archivo {nombre, hash, paginas}, vigencia_desde AAAA-MM-DD, porcentaje_declarado y filas_declaradas si la circular los dice), `demanda_pendiente` (referencia obligatoria `orden:<id>`, `solicitud:<id>` o `control:<persona_id>:<regla_id>`; datos: regla_id obligatorio y la regla tiene que estar activa, persona_id, tipo de origen, estudio o especialidad o prestacion, fecha_origen AAAA-MM-DD, dias; el plugin fija regla_version, canal, intentos, vence_el; nunca nombre, documento ni tel\xE9fono del paciente),`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, internacion_id opcional; en faltantes, `responsable` {tipo profesional, id, nombre} opcional para que el plugin avise a quien resuelve), `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), `credencial_profesional` (un profesional con agenda; referencia obligatoria `profesional:<id>`; datos: profesional_id, profesional (apellido y nombre, es personal de la instituci\xF3n), matricula_nacional, matricula_provincial, codigo_provincia, especialidades). `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.',
|
|
125
125
|
parametersSchema: {
|
|
126
126
|
type: "object",
|
|
127
127
|
properties: {
|
|
@@ -138,7 +138,7 @@ var manifest = {
|
|
|
138
138
|
{
|
|
139
139
|
name: TOOL_NAMES.transicionar,
|
|
140
140
|
displayName: "Transicionar caso",
|
|
141
|
-
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 de la instituci\xF3n 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. En `demanda_pendiente`: `contactar` (nivel ejecuta; datos: canal igual al de la regla y accion_id de la escritura registrada; el plugin controla regla activa, intentos, espera entre intentos y horario), `responder` (respuesta: acepta|rechaza|no_contesta), `comprobar` (lectura_id de la relectura del HIS, turno_id y turno_estado del turno encontrado), `cerrar` (motivo; solo con los intentos agotados o el plazo vencido), `descartar` (motivo). `credencial_profesional` (solo lee): `verificar` con profesional_id, matricula y tipo_matricula (nacional|provincial) si hay matr\xEDcula, resultado = el objeto que devuelve verificar-matriculas.mjs tal cual ({sin_matricula, encontrada, coincide_nombre, situacion, sancion, profesion, provincia, emisor}), lectura_id (conector "api", la consulta al registro) y fuente; el plugin decide vigente u observada y fija la observaci\xF3n (sin_matricula, no_figura, otra_persona, inhabilitada, sancion); `descartar` con motivo si el profesional ya no est\xE1 activo.',
|
|
141
|
+
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 de la instituci\xF3n 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. En `circular_aranceles`: `extraer` con extraccion_a (lo que ley\xF3 el agente) y extraccion_b (la salida de leer-circular.mjs, tal cual) como [{codigo, nombre, valor_nuevo}], actual (la salida de leer-convenio.mjs: [{codigo, nombre, componentes:[{arancel, monto}], copago, tipo_copago}]), lectura_id (lectura del convenio, conector base o api), lectura_circular_id (lectura de la circular, conector archivo) y fuente; el plugin cruza las tres fuentes y decide qu\xE9 se propone, qu\xE9 se revisa y qu\xE9 es excepci\xF3n (el agente no decide eso); `proponer` sin datos; despu\xE9s de la carga, `verificar` con leidos (la salida de leer-convenio.mjs tras la carga) y lectura_id; `cancelar` con motivo. En `demanda_pendiente`: `contactar` (nivel ejecuta; datos: canal igual al de la regla y accion_id de la escritura registrada; el plugin controla regla activa, intentos, espera entre intentos y horario), `responder` (respuesta: acepta|rechaza|no_contesta), `comprobar` (lectura_id de la relectura del HIS, turno_id y turno_estado del turno encontrado), `cerrar` (motivo; solo con los intentos agotados o el plazo vencido), `descartar` (motivo). `credencial_profesional` (solo lee): `verificar` con profesional_id, matricula y tipo_matricula (nacional|provincial) si hay matr\xEDcula, resultado = el objeto que devuelve verificar-matriculas.mjs tal cual ({sin_matricula, encontrada, coincide_nombre, situacion, sancion, profesion, provincia, emisor}), lectura_id (conector "api", la consulta al registro) y fuente; el plugin decide vigente u observada y fija la observaci\xF3n (sin_matricula, no_figura, otra_persona, inhabilitada, sancion); `descartar` con motivo si el profesional ya no est\xE1 activo.',
|
|
142
142
|
parametersSchema: {
|
|
143
143
|
type: "object",
|
|
144
144
|
properties: {
|
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.22";
|
|
39936
39936
|
var DB_NAMESPACE_SLUG = "autopilot_ops";
|
|
39937
39937
|
var PAGE_ROUTE = "autopilot";
|
|
39938
39938
|
var TOOL_NAMES = {
|
|
@@ -40045,7 +40045,7 @@ var manifest = {
|
|
|
40045
40045
|
{
|
|
40046
40046
|
name: TOOL_NAMES.abrirCaso,
|
|
40047
40047
|
displayName: "Abrir caso",
|
|
40048
|
-
description: 'Abre un caso nuevo en un workflow, en su estado inicial. Workflows: `demanda_pendiente` (referencia obligatoria `orden:<id>`, `solicitud:<id>` o `control:<persona_id>:<regla_id>`; datos: regla_id obligatorio y la regla tiene que estar activa, persona_id, tipo de origen, estudio o especialidad o prestacion, fecha_origen AAAA-MM-DD, dias; el plugin fija regla_version, canal, intentos, vence_el; nunca nombre, documento ni tel\xE9fono del paciente),`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, internacion_id opcional; en faltantes, `responsable` {tipo profesional, id, nombre} opcional para que el plugin avise a quien resuelve), `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), `credencial_profesional` (un profesional con agenda; referencia obligatoria `profesional:<id>`; datos: profesional_id, profesional (apellido y nombre, es personal de la instituci\xF3n), matricula_nacional, matricula_provincial, codigo_provincia, especialidades). `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.',
|
|
40048
|
+
description: 'Abre un caso nuevo en un workflow, en su estado inicial. Workflows: `circular_aranceles` (una circular de aranceles entera; referencia obligatoria `circular:<obra social>:<hash corto del archivo>`; datos: obra_social, convenio (nombre en el HIS), archivo {nombre, hash, paginas}, vigencia_desde AAAA-MM-DD, porcentaje_declarado y filas_declaradas si la circular los dice), `demanda_pendiente` (referencia obligatoria `orden:<id>`, `solicitud:<id>` o `control:<persona_id>:<regla_id>`; datos: regla_id obligatorio y la regla tiene que estar activa, persona_id, tipo de origen, estudio o especialidad o prestacion, fecha_origen AAAA-MM-DD, dias; el plugin fija regla_version, canal, intentos, vence_el; nunca nombre, documento ni tel\xE9fono del paciente),`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, internacion_id opcional; en faltantes, `responsable` {tipo profesional, id, nombre} opcional para que el plugin avise a quien resuelve), `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), `credencial_profesional` (un profesional con agenda; referencia obligatoria `profesional:<id>`; datos: profesional_id, profesional (apellido y nombre, es personal de la instituci\xF3n), matricula_nacional, matricula_provincial, codigo_provincia, especialidades). `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.',
|
|
40049
40049
|
parametersSchema: {
|
|
40050
40050
|
type: "object",
|
|
40051
40051
|
properties: {
|
|
@@ -40062,7 +40062,7 @@ var manifest = {
|
|
|
40062
40062
|
{
|
|
40063
40063
|
name: TOOL_NAMES.transicionar,
|
|
40064
40064
|
displayName: "Transicionar caso",
|
|
40065
|
-
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 de la instituci\xF3n 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. En `demanda_pendiente`: `contactar` (nivel ejecuta; datos: canal igual al de la regla y accion_id de la escritura registrada; el plugin controla regla activa, intentos, espera entre intentos y horario), `responder` (respuesta: acepta|rechaza|no_contesta), `comprobar` (lectura_id de la relectura del HIS, turno_id y turno_estado del turno encontrado), `cerrar` (motivo; solo con los intentos agotados o el plazo vencido), `descartar` (motivo). `credencial_profesional` (solo lee): `verificar` con profesional_id, matricula y tipo_matricula (nacional|provincial) si hay matr\xEDcula, resultado = el objeto que devuelve verificar-matriculas.mjs tal cual ({sin_matricula, encontrada, coincide_nombre, situacion, sancion, profesion, provincia, emisor}), lectura_id (conector "api", la consulta al registro) y fuente; el plugin decide vigente u observada y fija la observaci\xF3n (sin_matricula, no_figura, otra_persona, inhabilitada, sancion); `descartar` con motivo si el profesional ya no est\xE1 activo.',
|
|
40065
|
+
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 de la instituci\xF3n 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. En `circular_aranceles`: `extraer` con extraccion_a (lo que ley\xF3 el agente) y extraccion_b (la salida de leer-circular.mjs, tal cual) como [{codigo, nombre, valor_nuevo}], actual (la salida de leer-convenio.mjs: [{codigo, nombre, componentes:[{arancel, monto}], copago, tipo_copago}]), lectura_id (lectura del convenio, conector base o api), lectura_circular_id (lectura de la circular, conector archivo) y fuente; el plugin cruza las tres fuentes y decide qu\xE9 se propone, qu\xE9 se revisa y qu\xE9 es excepci\xF3n (el agente no decide eso); `proponer` sin datos; despu\xE9s de la carga, `verificar` con leidos (la salida de leer-convenio.mjs tras la carga) y lectura_id; `cancelar` con motivo. En `demanda_pendiente`: `contactar` (nivel ejecuta; datos: canal igual al de la regla y accion_id de la escritura registrada; el plugin controla regla activa, intentos, espera entre intentos y horario), `responder` (respuesta: acepta|rechaza|no_contesta), `comprobar` (lectura_id de la relectura del HIS, turno_id y turno_estado del turno encontrado), `cerrar` (motivo; solo con los intentos agotados o el plazo vencido), `descartar` (motivo). `credencial_profesional` (solo lee): `verificar` con profesional_id, matricula y tipo_matricula (nacional|provincial) si hay matr\xEDcula, resultado = el objeto que devuelve verificar-matriculas.mjs tal cual ({sin_matricula, encontrada, coincide_nombre, situacion, sancion, profesion, provincia, emisor}), lectura_id (conector "api", la consulta al registro) y fuente; el plugin decide vigente u observada y fija la observaci\xF3n (sin_matricula, no_figura, otra_persona, inhabilitada, sancion); `descartar` con motivo si el profesional ya no est\xE1 activo.',
|
|
40066
40066
|
parametersSchema: {
|
|
40067
40067
|
type: "object",
|
|
40068
40068
|
properties: {
|
|
@@ -40375,6 +40375,276 @@ var manifest_default = manifest;
|
|
|
40375
40375
|
// src/ops.ts
|
|
40376
40376
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
40377
40377
|
|
|
40378
|
+
// src/aranceles.ts
|
|
40379
|
+
import { createHash } from "node:crypto";
|
|
40380
|
+
var TOLERANCIA_EXTRACCIONES = 5e-3;
|
|
40381
|
+
var TOLERANCIA_FILAS = 0.1;
|
|
40382
|
+
var DESVIO_COHERENCIA = 15;
|
|
40383
|
+
var PROPORCION_INCOHERENTE = 0.1;
|
|
40384
|
+
var TOPE_VARIACION_POR_DEFECTO = 50;
|
|
40385
|
+
var LIMITES_TOPE = [10, 200];
|
|
40386
|
+
var VIGENCIA_DIAS_ATRAS = 60;
|
|
40387
|
+
var VIGENCIA_DIAS_ADELANTE = 120;
|
|
40388
|
+
var TOLERANCIA_RELECTURA = 0.01;
|
|
40389
|
+
var redondear = (n, dec = 2) => Math.round(n * 10 ** dec) / 10 ** dec;
|
|
40390
|
+
var pesos = (n) => `$ ${n.toLocaleString("es-AR", { maximumFractionDigits: 2 })}`;
|
|
40391
|
+
var porc = (n) => `${n.toLocaleString("es-AR", { maximumFractionDigits: 1 })} %`;
|
|
40392
|
+
function normalizarCodigo(v) {
|
|
40393
|
+
const s = typeof v === "number" ? String(v) : typeof v === "string" ? v : "";
|
|
40394
|
+
return s.toUpperCase().replace(/[^A-Z0-9]/g, "").replace(/^0+(?=.)/, "");
|
|
40395
|
+
}
|
|
40396
|
+
function leerMonto(v) {
|
|
40397
|
+
if (typeof v === "number") return Number.isFinite(v) ? v : null;
|
|
40398
|
+
if (typeof v !== "string") return null;
|
|
40399
|
+
let s = v.replace(/[$\s]/g, "").replace(/ARS/i, "");
|
|
40400
|
+
if (!s || !/^-?[\d.,]+$/.test(s)) return null;
|
|
40401
|
+
const coma = s.lastIndexOf(",");
|
|
40402
|
+
const punto = s.lastIndexOf(".");
|
|
40403
|
+
if (coma > -1 && punto > -1) s = coma > punto ? s.replace(/\./g, "").replace(",", ".") : s.replace(/,/g, "");
|
|
40404
|
+
else if (coma > -1) s = /,\d{3}$/.test(s) && s.split(",").length > 2 ? s.replace(/,/g, "") : s.replace(",", ".");
|
|
40405
|
+
else if (punto > -1 && s.split(".").length > 2) s = s.replace(/\./g, "");
|
|
40406
|
+
else if (punto > -1 && /\.\d{3}$/.test(s)) s = s.replace(".", "");
|
|
40407
|
+
const n = Number(s);
|
|
40408
|
+
return Number.isFinite(n) ? n : null;
|
|
40409
|
+
}
|
|
40410
|
+
function leerFilas(v, nombre) {
|
|
40411
|
+
if (!Array.isArray(v)) return { error: `${nombre} tiene que ser una lista [{codigo, nombre, valor_nuevo}]` };
|
|
40412
|
+
const filas = [];
|
|
40413
|
+
const vistos = /* @__PURE__ */ new Set();
|
|
40414
|
+
for (const [i, e] of v.entries()) {
|
|
40415
|
+
if (!e || typeof e !== "object") return { error: `${nombre}[${i}] no es una fila` };
|
|
40416
|
+
const r = e;
|
|
40417
|
+
const codigo = normalizarCodigo(r.codigo);
|
|
40418
|
+
if (!codigo) return { error: `${nombre}[${i}] no tiene c\xF3digo` };
|
|
40419
|
+
if (vistos.has(codigo)) return { error: `${nombre} tiene el c\xF3digo ${codigo} repetido` };
|
|
40420
|
+
vistos.add(codigo);
|
|
40421
|
+
const valor = leerMonto(r.valor_nuevo);
|
|
40422
|
+
if (valor === null || valor <= 0) return { error: `${nombre}[${i}] (c\xF3digo ${codigo}): valor_nuevo tiene que ser un n\xFAmero mayor a 0` };
|
|
40423
|
+
filas.push({ codigo, codigo_original: String(r.codigo).trim(), nombre: typeof r.nombre === "string" && r.nombre.trim() ? r.nombre.trim().slice(0, 200) : null, valor_nuevo: redondear(valor) });
|
|
40424
|
+
}
|
|
40425
|
+
return { filas };
|
|
40426
|
+
}
|
|
40427
|
+
function leerActual(v) {
|
|
40428
|
+
if (!Array.isArray(v)) return { error: "actual tiene que ser una lista [{codigo, nombre, componentes: [{arancel, monto}], copago, tipo_copago}]" };
|
|
40429
|
+
const practicas = [];
|
|
40430
|
+
const vistos = /* @__PURE__ */ new Set();
|
|
40431
|
+
for (const [i, e] of v.entries()) {
|
|
40432
|
+
if (!e || typeof e !== "object") return { error: `actual[${i}] no es una pr\xE1ctica` };
|
|
40433
|
+
const r = e;
|
|
40434
|
+
const codigo = normalizarCodigo(r.codigo);
|
|
40435
|
+
if (!codigo) return { error: `actual[${i}] no tiene c\xF3digo` };
|
|
40436
|
+
if (vistos.has(codigo)) continue;
|
|
40437
|
+
vistos.add(codigo);
|
|
40438
|
+
const comps = Array.isArray(r.componentes) ? r.componentes : [];
|
|
40439
|
+
const componentes = [];
|
|
40440
|
+
for (const c of comps) {
|
|
40441
|
+
const cc = c;
|
|
40442
|
+
const arancel = typeof cc.arancel === "string" ? cc.arancel.trim() : "";
|
|
40443
|
+
const monto = leerMonto(cc.monto);
|
|
40444
|
+
if (!arancel || monto === null || monto < 0) return { error: `actual[${i}] (c\xF3digo ${codigo}): cada componente necesita arancel y monto` };
|
|
40445
|
+
componentes.push({ arancel, monto: redondear(monto) });
|
|
40446
|
+
}
|
|
40447
|
+
if (componentes.length === 0) return { error: `actual[${i}] (c\xF3digo ${codigo}): no tiene componentes de arancel` };
|
|
40448
|
+
practicas.push({
|
|
40449
|
+
codigo,
|
|
40450
|
+
codigo_his: typeof r.codigo_his === "string" && r.codigo_his.trim() ? r.codigo_his.trim() : String(r.codigo).trim(),
|
|
40451
|
+
nombre: typeof r.nombre === "string" && r.nombre.trim() ? r.nombre.trim() : codigo,
|
|
40452
|
+
componentes,
|
|
40453
|
+
copago: r.copago === null || r.copago === void 0 ? null : String(r.copago),
|
|
40454
|
+
tipo_copago: r.tipo_copago === null || r.tipo_copago === void 0 ? null : String(r.tipo_copago)
|
|
40455
|
+
});
|
|
40456
|
+
}
|
|
40457
|
+
return { practicas };
|
|
40458
|
+
}
|
|
40459
|
+
function repartir(componentes, nuevo) {
|
|
40460
|
+
const total = componentes.reduce((a, c) => a + c.monto, 0);
|
|
40461
|
+
if (componentes.length === 1) return [{ arancel: componentes[0].arancel, monto: redondear(nuevo) }];
|
|
40462
|
+
if (total <= 0) return null;
|
|
40463
|
+
const out = componentes.map((c) => ({ arancel: c.arancel, monto: redondear(c.monto / total * nuevo) }));
|
|
40464
|
+
const resto = redondear(nuevo - out.reduce((a, c) => a + c.monto, 0));
|
|
40465
|
+
if (resto !== 0) {
|
|
40466
|
+
const mayor = out.reduce((m, c, i) => c.monto > out[m].monto ? i : m, 0);
|
|
40467
|
+
out[mayor].monto = redondear(out[mayor].monto + resto);
|
|
40468
|
+
}
|
|
40469
|
+
return out;
|
|
40470
|
+
}
|
|
40471
|
+
function mediana(v) {
|
|
40472
|
+
if (v.length === 0) return null;
|
|
40473
|
+
const s = [...v].sort((a, b) => a - b);
|
|
40474
|
+
const m = Math.floor(s.length / 2);
|
|
40475
|
+
return s.length % 2 ? s[m] : (s[m - 1] + s[m]) / 2;
|
|
40476
|
+
}
|
|
40477
|
+
function sumarDias(fecha, dias) {
|
|
40478
|
+
const d = /* @__PURE__ */ new Date(`${fecha}T00:00:00Z`);
|
|
40479
|
+
d.setUTCDate(d.getUTCDate() + dias);
|
|
40480
|
+
return d.toISOString().slice(0, 10);
|
|
40481
|
+
}
|
|
40482
|
+
function cruzarCircular(input2) {
|
|
40483
|
+
const errores = [];
|
|
40484
|
+
const a = leerFilas(input2.extraccionA, "extraccion_a");
|
|
40485
|
+
const b = leerFilas(input2.extraccionB, "extraccion_b");
|
|
40486
|
+
const act = leerActual(input2.actual);
|
|
40487
|
+
if ("error" in a) errores.push(a.error);
|
|
40488
|
+
if ("error" in b) errores.push(b.error);
|
|
40489
|
+
if ("error" in act) errores.push(act.error);
|
|
40490
|
+
const vigencia = typeof input2.vigenciaDesde === "string" && /^\d{4}-\d{2}-\d{2}$/.test(input2.vigenciaDesde) ? input2.vigenciaDesde : null;
|
|
40491
|
+
if (!vigencia) errores.push("vigencia_desde: la circular tiene que decir desde cu\xE1ndo rige (AAAA-MM-DD)");
|
|
40492
|
+
else if (vigencia < sumarDias(input2.hoy, -VIGENCIA_DIAS_ATRAS) || vigencia > sumarDias(input2.hoy, VIGENCIA_DIAS_ADELANTE)) {
|
|
40493
|
+
errores.push(`vigencia_desde ${vigencia} fuera de rango: se aceptan circulares que rigen desde ${VIGENCIA_DIAS_ATRAS} d\xEDas atr\xE1s hasta ${VIGENCIA_DIAS_ADELANTE} d\xEDas adelante`);
|
|
40494
|
+
}
|
|
40495
|
+
if ("error" in a || "error" in b || "error" in act) return { ok: false, errores };
|
|
40496
|
+
const filasA = a.filas;
|
|
40497
|
+
const filasB = b.filas;
|
|
40498
|
+
const nA = filasA.length;
|
|
40499
|
+
const nB = filasB.length;
|
|
40500
|
+
if (nA === 0 || nB === 0) errores.push("las dos extracciones tienen que traer al menos una fila");
|
|
40501
|
+
else if (Math.abs(nA - nB) / Math.max(nA, nB) > TOLERANCIA_FILAS) {
|
|
40502
|
+
errores.push(`las dos extracciones no coinciden en cantidad de filas (A ${nA}, B ${nB}): hay que volver a leer la circular`);
|
|
40503
|
+
}
|
|
40504
|
+
const declaradas = typeof input2.filasDeclaradas === "number" && input2.filasDeclaradas > 0 ? Math.round(input2.filasDeclaradas) : null;
|
|
40505
|
+
if (declaradas !== null) {
|
|
40506
|
+
for (const [n, cual] of [[nA, "A"], [nB, "B"]]) {
|
|
40507
|
+
if (n > 0 && Math.abs(n - declaradas) / declaradas > TOLERANCIA_FILAS) errores.push(`la extracci\xF3n ${cual} tiene ${n} filas y la circular declara ${declaradas}`);
|
|
40508
|
+
}
|
|
40509
|
+
}
|
|
40510
|
+
if (errores.length) return { ok: false, errores };
|
|
40511
|
+
const tope = typeof input2.topeVariacion === "number" && input2.topeVariacion >= LIMITES_TOPE[0] && input2.topeVariacion <= LIMITES_TOPE[1] ? input2.topeVariacion : TOPE_VARIACION_POR_DEFECTO;
|
|
40512
|
+
const porA = new Map(filasA.map((f) => [f.codigo, f]));
|
|
40513
|
+
const porB = new Map(filasB.map((f) => [f.codigo, f]));
|
|
40514
|
+
const porHis = new Map(act.practicas.map((p) => [p.codigo, p]));
|
|
40515
|
+
const porBase = /* @__PURE__ */ new Map();
|
|
40516
|
+
for (const p of act.practicas) {
|
|
40517
|
+
const raiz = p.codigo.replace(/[A-Z]$/, "");
|
|
40518
|
+
if (raiz !== p.codigo) porBase.set(raiz, [...porBase.get(raiz) ?? [], p]);
|
|
40519
|
+
}
|
|
40520
|
+
const usadas = /* @__PURE__ */ new Set();
|
|
40521
|
+
const codigos = [.../* @__PURE__ */ new Set([...filasA.map((f) => f.codigo), ...filasB.map((f) => f.codigo)])];
|
|
40522
|
+
const items = [];
|
|
40523
|
+
for (const codigoCircular of codigos) {
|
|
40524
|
+
const fa = porA.get(codigoCircular);
|
|
40525
|
+
const fb = porB.get(codigoCircular);
|
|
40526
|
+
let his = porHis.get(codigoCircular);
|
|
40527
|
+
const variantes = his ? [] : porBase.get(codigoCircular) ?? [];
|
|
40528
|
+
if (!his && variantes.length === 1) his = variantes[0];
|
|
40529
|
+
const codigo = his?.codigo ?? codigoCircular;
|
|
40530
|
+
const nombre = his?.nombre ?? fb?.nombre ?? fa?.nombre ?? codigo;
|
|
40531
|
+
const valorActual = his ? redondear(his.componentes.reduce((s, c) => s + c.monto, 0)) : null;
|
|
40532
|
+
const base = { codigo, codigo_his: his?.codigo_his ?? fb?.codigo_original ?? fa?.codigo_original ?? codigo, nombre, valor_a: fa?.valor_nuevo ?? null, valor_b: fb?.valor_nuevo ?? null, valor_actual: valorActual, valor_nuevo: null, variacion: null, estado: "revisar", motivo: null, componentes_nuevos: null };
|
|
40533
|
+
if (variantes.length > 1) {
|
|
40534
|
+
items.push({ ...base, motivo: `el convenio del HIS tiene ${variantes.length} variantes de este c\xF3digo (${variantes.map((v) => v.codigo_his).join(", ")}): no se carga hasta decidir a cu\xE1l corresponde` });
|
|
40535
|
+
continue;
|
|
40536
|
+
}
|
|
40537
|
+
if (his && usadas.has(his.codigo)) {
|
|
40538
|
+
items.push({ ...base, motivo: `otra fila de la circular ya corresponde a la pr\xE1ctica ${his.codigo_his} del HIS` });
|
|
40539
|
+
continue;
|
|
40540
|
+
}
|
|
40541
|
+
if (his) usadas.add(his.codigo);
|
|
40542
|
+
if (!fa || !fb) {
|
|
40543
|
+
items.push({ ...base, motivo: fa ? "solo la ley\xF3 el agente: el programa no la encontr\xF3 en la circular" : "solo la ley\xF3 el programa: el agente no la inform\xF3" });
|
|
40544
|
+
continue;
|
|
40545
|
+
}
|
|
40546
|
+
if (Math.abs(fa.valor_nuevo - fb.valor_nuevo) / Math.max(fa.valor_nuevo, fb.valor_nuevo) > TOLERANCIA_EXTRACCIONES) {
|
|
40547
|
+
items.push({ ...base, motivo: `las dos lecturas no coinciden: el agente ley\xF3 ${pesos(fa.valor_nuevo)} y el programa ${pesos(fb.valor_nuevo)}` });
|
|
40548
|
+
continue;
|
|
40549
|
+
}
|
|
40550
|
+
const nuevo = fb.valor_nuevo;
|
|
40551
|
+
if (!his) {
|
|
40552
|
+
items.push({ ...base, valor_nuevo: nuevo, estado: "sin_convenio", motivo: "la circular la trae y el convenio del HIS no la tiene: no se carga" });
|
|
40553
|
+
continue;
|
|
40554
|
+
}
|
|
40555
|
+
const componentes = repartir(his.componentes, nuevo);
|
|
40556
|
+
if (!componentes) {
|
|
40557
|
+
items.push({ ...base, valor_nuevo: nuevo, motivo: "tiene varios componentes de arancel en $0: no se puede repartir el valor nuevo" });
|
|
40558
|
+
continue;
|
|
40559
|
+
}
|
|
40560
|
+
const variacion = valorActual && valorActual > 0 ? redondear((nuevo - valorActual) / valorActual * 100, 1) : null;
|
|
40561
|
+
items.push({ ...base, valor_nuevo: nuevo, variacion, estado: "propuesta", componentes_nuevos: componentes });
|
|
40562
|
+
}
|
|
40563
|
+
const conVariacion = items.filter((i) => i.estado === "propuesta" && i.variacion !== null);
|
|
40564
|
+
const declarado = typeof input2.porcentajeDeclarado === "number" && Number.isFinite(input2.porcentajeDeclarado) ? input2.porcentajeDeclarado : null;
|
|
40565
|
+
const referencia = declarado ?? mediana(conVariacion.map((i) => i.variacion));
|
|
40566
|
+
let desviadas = 0;
|
|
40567
|
+
for (const i of items) {
|
|
40568
|
+
if (i.estado !== "propuesta") continue;
|
|
40569
|
+
const motivos = [];
|
|
40570
|
+
if (i.valor_actual === 0 || i.variacion === null) motivos.push("el HIS tiene hoy $0: no hay contra qu\xE9 comparar");
|
|
40571
|
+
else {
|
|
40572
|
+
if (i.variacion < 0) motivos.push(`baja el valor (${porc(i.variacion)})`);
|
|
40573
|
+
if (i.variacion > tope) motivos.push(`sube ${porc(i.variacion)}, m\xE1s que el tope de ${porc(tope)}`);
|
|
40574
|
+
if (referencia !== null && Math.abs(i.variacion - referencia) > DESVIO_COHERENCIA) {
|
|
40575
|
+
desviadas += 1;
|
|
40576
|
+
motivos.push(`se aparta ${redondear(Math.abs(i.variacion - referencia), 1).toLocaleString("es-AR")} puntos de la variaci\xF3n ${declarado !== null ? "que declara la circular" : "del resto"} (${porc(redondear(referencia, 1))})`);
|
|
40577
|
+
}
|
|
40578
|
+
}
|
|
40579
|
+
if (motivos.length) {
|
|
40580
|
+
i.estado = "excepcion";
|
|
40581
|
+
i.motivo = motivos.join("; ");
|
|
40582
|
+
}
|
|
40583
|
+
}
|
|
40584
|
+
const variaciones = items.filter((i) => i.variacion !== null && (i.estado === "propuesta" || i.estado === "excepcion")).map((i) => i.variacion);
|
|
40585
|
+
const resumen = {
|
|
40586
|
+
filas_a: nA,
|
|
40587
|
+
filas_b: nB,
|
|
40588
|
+
filas_declaradas: declaradas,
|
|
40589
|
+
coinciden: items.filter((i) => i.valor_a !== null && i.valor_b !== null && (i.estado !== "revisar" || i.valor_nuevo !== null)).length,
|
|
40590
|
+
propuestas: items.filter((i) => i.estado === "propuesta").length,
|
|
40591
|
+
revisar: items.filter((i) => i.estado === "revisar").length,
|
|
40592
|
+
sin_convenio: items.filter((i) => i.estado === "sin_convenio").length,
|
|
40593
|
+
excepciones: items.filter((i) => i.estado === "excepcion").length,
|
|
40594
|
+
bajas: items.filter((i) => i.variacion !== null && i.variacion < 0).length,
|
|
40595
|
+
supera_tope: items.filter((i) => i.variacion !== null && i.variacion > tope).length,
|
|
40596
|
+
referencia: declarado !== null ? "declarada" : referencia !== null ? "mediana" : null,
|
|
40597
|
+
variacion_referencia: referencia === null ? null : redondear(referencia, 1),
|
|
40598
|
+
variacion_min: variaciones.length ? Math.min(...variaciones) : null,
|
|
40599
|
+
variacion_max: variaciones.length ? Math.max(...variaciones) : null,
|
|
40600
|
+
coherencia_rota: conVariacion.length > 0 && desviadas / conVariacion.length > PROPORCION_INCOHERENTE,
|
|
40601
|
+
tope_variacion: tope
|
|
40602
|
+
};
|
|
40603
|
+
return { ok: true, items, resumen };
|
|
40604
|
+
}
|
|
40605
|
+
function leerItems(v) {
|
|
40606
|
+
return Array.isArray(v) ? v.filter((x) => x && typeof x === "object" && typeof x.codigo === "string") : [];
|
|
40607
|
+
}
|
|
40608
|
+
function itemsACargar(items, excluir) {
|
|
40609
|
+
const fuera = new Set(Array.isArray(excluir) ? excluir.map(normalizarCodigo) : []);
|
|
40610
|
+
return items.filter((i) => (i.estado === "propuesta" || i.estado === "excepcion") && i.componentes_nuevos && !fuera.has(i.codigo));
|
|
40611
|
+
}
|
|
40612
|
+
var csvCampo = (v) => /[;"\n\r]/.test(v) ? `"${v.replace(/"/g, '""')}"` : v;
|
|
40613
|
+
function archivoActualizacion(items, actual) {
|
|
40614
|
+
const porHis = new Map(actual.map((p) => [p.codigo, p]));
|
|
40615
|
+
const aranceles = [];
|
|
40616
|
+
for (const i of items) for (const c of i.componentes_nuevos ?? []) if (!aranceles.includes(c.arancel)) aranceles.push(c.arancel);
|
|
40617
|
+
const lineas = [["Prestacion", "Codigo", "Copago", "Tipo Copago", ...aranceles].map(csvCampo).join(";")];
|
|
40618
|
+
for (const i of items) {
|
|
40619
|
+
const his = porHis.get(i.codigo);
|
|
40620
|
+
const montos = aranceles.map((a) => {
|
|
40621
|
+
const c = (i.componentes_nuevos ?? []).find((x) => x.arancel === a);
|
|
40622
|
+
return c ? c.monto.toFixed(2) : "";
|
|
40623
|
+
});
|
|
40624
|
+
lineas.push([his?.nombre ?? i.nombre, ` ${his ? his.codigo_his : i.codigo}`, his?.copago ?? "", his?.tipo_copago ?? "", ...montos].map(csvCampo).join(";"));
|
|
40625
|
+
}
|
|
40626
|
+
const contenido = `\uFEFF${lineas.join("\r\n")}\r
|
|
40627
|
+
`;
|
|
40628
|
+
return { contenido, hash: hashArchivo(contenido), filas: items.length, aranceles };
|
|
40629
|
+
}
|
|
40630
|
+
function hashArchivo(contenido) {
|
|
40631
|
+
return createHash("sha256").update(contenido, "utf8").digest("hex");
|
|
40632
|
+
}
|
|
40633
|
+
function compararRelectura(aCargar, leidos) {
|
|
40634
|
+
const lectura = leerActual(leidos);
|
|
40635
|
+
if ("error" in lectura) return { error: `leidos: ${lectura.error}` };
|
|
40636
|
+
const porHis = new Map(lectura.practicas.map((p) => [p.codigo, p]));
|
|
40637
|
+
const diferencias = [];
|
|
40638
|
+
for (const i of aCargar) {
|
|
40639
|
+
const his = porHis.get(i.codigo);
|
|
40640
|
+
for (const c of i.componentes_nuevos ?? []) {
|
|
40641
|
+
const leido = his?.componentes.find((x) => x.arancel === c.arancel)?.monto ?? null;
|
|
40642
|
+
if (leido === null || Math.abs(leido - c.monto) > TOLERANCIA_RELECTURA) diferencias.push({ codigo: i.codigo, nombre: i.nombre, arancel: c.arancel, aprobado: c.monto, leido });
|
|
40643
|
+
}
|
|
40644
|
+
}
|
|
40645
|
+
return { diferencias, comprobadas: aCargar.length };
|
|
40646
|
+
}
|
|
40647
|
+
|
|
40378
40648
|
// src/workflows.ts
|
|
40379
40649
|
var MODOS = ["corte", "observa", "propone", "ejecuta", "autonomo"];
|
|
40380
40650
|
var MODO_POR_DEFECTO = "propone";
|
|
@@ -40988,7 +41258,7 @@ var cuentaCompleta = {
|
|
|
40988
41258
|
]
|
|
40989
41259
|
};
|
|
40990
41260
|
var PLAZO_PRESENTACION_POR_DEFECTO = 60;
|
|
40991
|
-
function
|
|
41261
|
+
function sumarDias2(fecha, dias) {
|
|
40992
41262
|
const d = /* @__PURE__ */ new Date(`${fecha}T00:00:00Z`);
|
|
40993
41263
|
d.setUTCDate(d.getUTCDate() + dias);
|
|
40994
41264
|
return d.toISOString().slice(0, 10);
|
|
@@ -40997,7 +41267,7 @@ function calcularVencimiento(fechaPrestacion, obraSocial, contexto) {
|
|
|
40997
41267
|
const nombre = normalizarNombre(obraSocial);
|
|
40998
41268
|
const fijado = nombre ? Object.entries(contexto.plazosPresentacion ?? {}).find(([k]) => normalizarNombre(k) === nombre)?.[1] : void 0;
|
|
40999
41269
|
const plazo = typeof fijado === "number" && Number.isInteger(fijado) && fijado > 0 && fijado <= 730 ? fijado : PLAZO_PRESENTACION_POR_DEFECTO;
|
|
41000
|
-
return { plazo_dias: plazo, vence_el:
|
|
41270
|
+
return { plazo_dias: plazo, vence_el: sumarDias2(fechaPrestacion, plazo), plazo_confirmado: typeof fijado === "number" };
|
|
41001
41271
|
}
|
|
41002
41272
|
var facturacionPendiente = {
|
|
41003
41273
|
clave: "facturacion_pendiente",
|
|
@@ -41317,6 +41587,156 @@ var demandaPendiente = {
|
|
|
41317
41587
|
}
|
|
41318
41588
|
]
|
|
41319
41589
|
};
|
|
41590
|
+
function resumenDe(caso) {
|
|
41591
|
+
const r = caso.datos.resumen;
|
|
41592
|
+
return r && typeof r === "object" ? r : null;
|
|
41593
|
+
}
|
|
41594
|
+
var circularAranceles = {
|
|
41595
|
+
clave: "circular_aranceles",
|
|
41596
|
+
nombre: "Circulares de aranceles",
|
|
41597
|
+
puesto: "Convenios y facturaci\xF3n",
|
|
41598
|
+
descripcion: "Lee la circular de aranceles de una obra social dos veces por caminos distintos, la cruza con el convenio del HIS y arma la actualizaci\xF3n completa; la direcci\xF3n la aprueba en un solo pedido, se carga con el archivo que genera el plugin y el puesto comprueba releyendo cada valor.",
|
|
41599
|
+
inicial: "recibida",
|
|
41600
|
+
estados: ["recibida", "extraida", "pendiente_aprobacion", "aprobada", "aplicada", "verificada", "con_diferencias", "rechazada", "cancelada"],
|
|
41601
|
+
finalesOk: ["verificada"],
|
|
41602
|
+
finalesNoOk: ["rechazada", "cancelada"],
|
|
41603
|
+
minutosAbsorbidos: 180,
|
|
41604
|
+
transiciones: [
|
|
41605
|
+
{
|
|
41606
|
+
via: "extraer",
|
|
41607
|
+
de: ["recibida", "extraida"],
|
|
41608
|
+
a: "extraida",
|
|
41609
|
+
actor: ["agente", "humano"],
|
|
41610
|
+
nivelRequerido: "observa",
|
|
41611
|
+
descripcion: "Informar las dos lecturas de la circular (la del agente y la de leer-circular.mjs) y lo que tiene hoy el convenio en el HIS. El plugin las cruza y decide pr\xE1ctica por pr\xE1ctica.",
|
|
41612
|
+
validar: (caso, datos, contexto) => {
|
|
41613
|
+
const l = { ...caso.datos, ...datos };
|
|
41614
|
+
const cruce = cruzarCircular({
|
|
41615
|
+
extraccionA: datos.extraccion_a,
|
|
41616
|
+
extraccionB: datos.extraccion_b,
|
|
41617
|
+
actual: datos.actual,
|
|
41618
|
+
filasDeclaradas: num(l.filas_declaradas),
|
|
41619
|
+
porcentajeDeclarado: num(l.porcentaje_declarado),
|
|
41620
|
+
vigenciaDesde: l.vigencia_desde,
|
|
41621
|
+
hoy: contexto.hoy ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
41622
|
+
topeVariacion: contexto.topeVariacion ?? null
|
|
41623
|
+
});
|
|
41624
|
+
return [
|
|
41625
|
+
texto(datos.lectura_id) ? ok("lectura_registrada") : falla("lectura_registrada", "extraer requiere lectura_id: la lectura del convenio en el HIS registrada con ops_registrar_accion"),
|
|
41626
|
+
texto(datos.lectura_circular_id) ? ok("lectura_circular_registrada") : falla("lectura_circular_registrada", "extraer requiere lectura_circular_id: la lectura de la circular (conector archivo) registrada con ops_registrar_accion"),
|
|
41627
|
+
texto(datos.fuente) ? ok("fuente_presente") : falla("fuente_presente", "fuente (la circular: nombre del archivo y de d\xF3nde vino) es obligatoria"),
|
|
41628
|
+
cruce.ok ? ok("cruce_circular", `${cruce.resumen.propuestas} propuestas, ${cruce.resumen.excepciones} excepciones, ${cruce.resumen.revisar} a revisar, ${cruce.resumen.sin_convenio} sin convenio`) : falla("cruce_circular", cruce.errores.join(" \xB7 "))
|
|
41629
|
+
];
|
|
41630
|
+
}
|
|
41631
|
+
},
|
|
41632
|
+
{
|
|
41633
|
+
via: "proponer",
|
|
41634
|
+
de: ["extraida"],
|
|
41635
|
+
a: "pendiente_aprobacion",
|
|
41636
|
+
actor: ["agente", "humano"],
|
|
41637
|
+
nivelRequerido: "propone",
|
|
41638
|
+
descripcion: "Pedir la aprobaci\xF3n de la circular con lo que calcul\xF3 el plugin.",
|
|
41639
|
+
validar: (caso) => {
|
|
41640
|
+
const r = resumenDe(caso);
|
|
41641
|
+
return [r && r.propuestas + r.excepciones > 0 ? ok("hay_que_cargar", `${r.propuestas + r.excepciones} pr\xE1cticas`) : falla("hay_que_cargar", "no hay ninguna pr\xE1ctica para cargar: revisar la lectura o cancelar")];
|
|
41642
|
+
},
|
|
41643
|
+
autonomia: {
|
|
41644
|
+
aSinExcepcion: "aprobada",
|
|
41645
|
+
excepciones: (caso, _datos, contexto) => {
|
|
41646
|
+
const r = resumenDe(caso);
|
|
41647
|
+
const vigencia = texto(caso.datos.vigencia_desde);
|
|
41648
|
+
return [
|
|
41649
|
+
excepcion("practicas_con_excepcion", (r?.excepciones ?? 0) > 0, `${r?.excepciones ?? 0} pr\xE1cticas con baja, suba sobre el tope o desv\xEDo`),
|
|
41650
|
+
excepcion("lecturas_que_no_coinciden", (r?.revisar ?? 0) > 0, `${r?.revisar ?? 0} pr\xE1cticas a revisar`),
|
|
41651
|
+
excepcion("sin_convenio", (r?.sin_convenio ?? 0) > 0, `${r?.sin_convenio ?? 0} pr\xE1cticas que el convenio no tiene`),
|
|
41652
|
+
excepcion("coherencia_rota", r?.coherencia_rota === true, r?.coherencia_rota ? "m\xE1s del 10 % de las pr\xE1cticas se aparta de la variaci\xF3n de la circular" : "coherente"),
|
|
41653
|
+
excepcion("vigencia_pasada", vigencia !== null && contexto.hoy !== void 0 && vigencia < contexto.hoy, vigencia ? `rige desde ${vigencia}` : "sin vigencia")
|
|
41654
|
+
];
|
|
41655
|
+
}
|
|
41656
|
+
}
|
|
41657
|
+
},
|
|
41658
|
+
{
|
|
41659
|
+
via: "aprobar",
|
|
41660
|
+
de: ["pendiente_aprobacion"],
|
|
41661
|
+
a: "aprobada",
|
|
41662
|
+
actor: ["humano"],
|
|
41663
|
+
descripcion: "Aprobaci\xF3n de la circular por la direcci\xF3n; puede excluir pr\xE1cticas (excluir: [c\xF3digos]).",
|
|
41664
|
+
validar: (caso, datos) => {
|
|
41665
|
+
const items = leerItems(caso.datos.items);
|
|
41666
|
+
const excluir = datos.excluir === void 0 ? [] : datos.excluir;
|
|
41667
|
+
if (!Array.isArray(excluir)) return [falla("excluir_valido", "excluir tiene que ser una lista de c\xF3digos")];
|
|
41668
|
+
const codigos = new Set(items.map((i) => i.codigo));
|
|
41669
|
+
const ajenos = excluir.map(normalizarCodigo).filter((c) => !codigos.has(c));
|
|
41670
|
+
const quedan = itemsACargar(items, excluir).length;
|
|
41671
|
+
return [
|
|
41672
|
+
ajenos.length === 0 ? ok("excluir_valido") : falla("excluir_valido", `c\xF3digos que no est\xE1n en la circular: ${ajenos.join(", ")}`),
|
|
41673
|
+
quedan > 0 ? ok("hay_que_cargar", `${quedan} pr\xE1cticas`) : falla("hay_que_cargar", "excluyendo esas no queda nada para cargar: rechazar la circular")
|
|
41674
|
+
];
|
|
41675
|
+
}
|
|
41676
|
+
},
|
|
41677
|
+
{
|
|
41678
|
+
via: "rechazar",
|
|
41679
|
+
de: ["pendiente_aprobacion"],
|
|
41680
|
+
a: "rechazada",
|
|
41681
|
+
actor: ["humano"],
|
|
41682
|
+
descripcion: "Rechazo con motivo.",
|
|
41683
|
+
validar: (_caso, datos) => [texto(datos.motivo) ? ok("motivo_presente") : falla("motivo_presente", "el rechazo requiere motivo")]
|
|
41684
|
+
},
|
|
41685
|
+
{
|
|
41686
|
+
via: "aplicar",
|
|
41687
|
+
de: ["aprobada"],
|
|
41688
|
+
a: "aplicada",
|
|
41689
|
+
actor: ["agente", "humano"],
|
|
41690
|
+
nivelRequerido: "ejecuta",
|
|
41691
|
+
cargaManual: true,
|
|
41692
|
+
descripcion: "Cargar en el HIS el archivo que gener\xF3 el plugin (Actualizaci\xF3n con archivo) y registrarlo, con el hash del archivo cargado.",
|
|
41693
|
+
validar: (caso, datos) => {
|
|
41694
|
+
const esperado = texto(caso.datos.archivo_csv?.hash);
|
|
41695
|
+
const informado = texto(datos.archivo_hash);
|
|
41696
|
+
return [
|
|
41697
|
+
texto(datos.accion_id) ? ok("accion_registrada") : falla("accion_registrada", "aplicar requiere accion_id de la carga registrada"),
|
|
41698
|
+
esperado && informado === esperado ? ok("archivo_aprobado") : falla("archivo_aprobado", esperado ? "el archivo cargado no es el que gener\xF3 el plugin para esta aprobaci\xF3n (archivo_hash distinto): descargarlo de nuevo y cargar ese" : "la circular no tiene archivo generado")
|
|
41699
|
+
];
|
|
41700
|
+
}
|
|
41701
|
+
},
|
|
41702
|
+
{
|
|
41703
|
+
via: "verificar",
|
|
41704
|
+
de: ["aplicada"],
|
|
41705
|
+
a: (caso, datos) => {
|
|
41706
|
+
const r = compararRelectura(itemsACargar(leerItems(caso.datos.items), caso.datos.excluir), datos.leidos);
|
|
41707
|
+
return "error" in r || r.diferencias.length > 0 ? "con_diferencias" : "verificada";
|
|
41708
|
+
},
|
|
41709
|
+
actor: ["agente", "humano", "sistema"],
|
|
41710
|
+
nivelRequerido: "observa",
|
|
41711
|
+
descripcion: "Releer el convenio en el HIS despu\xE9s de la carga e informar cada pr\xE1ctica con sus componentes (leidos). El plugin compara contra lo aprobado.",
|
|
41712
|
+
validar: (_caso, datos) => {
|
|
41713
|
+
const r = compararRelectura([], datos.leidos);
|
|
41714
|
+
return [
|
|
41715
|
+
texto(datos.lectura_id) ? ok("lectura_registrada") : falla("lectura_registrada", "verificar requiere lectura_id: la relectura del convenio registrada"),
|
|
41716
|
+
"error" in r ? falla("leidos_validos", r.error) : ok("leidos_validos")
|
|
41717
|
+
];
|
|
41718
|
+
}
|
|
41719
|
+
},
|
|
41720
|
+
{
|
|
41721
|
+
via: "corregir",
|
|
41722
|
+
de: ["con_diferencias"],
|
|
41723
|
+
a: "aplicada",
|
|
41724
|
+
actor: ["humano"],
|
|
41725
|
+
cargaManual: true,
|
|
41726
|
+
descripcion: "Corregir en el HIS las pr\xE1cticas que no coincidieron y volver a comprobar.",
|
|
41727
|
+
validar: (_caso, datos) => [texto(datos.accion_id) ? ok("accion_registrada") : falla("accion_registrada", "corregir requiere accion_id de la correcci\xF3n registrada")]
|
|
41728
|
+
},
|
|
41729
|
+
{
|
|
41730
|
+
via: "cancelar",
|
|
41731
|
+
de: ["recibida", "extraida", "pendiente_aprobacion", "aprobada", "con_diferencias"],
|
|
41732
|
+
a: "cancelada",
|
|
41733
|
+
actor: ["humano", "agente"],
|
|
41734
|
+
nivelRequerido: "observa",
|
|
41735
|
+
descripcion: "Cancelar la circular (repetida, de otra regi\xF3n, ilegible).",
|
|
41736
|
+
validar: (_caso, datos) => [texto(datos.motivo) ? ok("motivo_presente") : falla("motivo_presente", "la cancelaci\xF3n requiere motivo")]
|
|
41737
|
+
}
|
|
41738
|
+
]
|
|
41739
|
+
};
|
|
41320
41740
|
var WORKFLOWS_SOLO_LECTURA = /* @__PURE__ */ new Set([cuentaCompleta.clave, facturacionPendiente.clave, credencialProfesional.clave]);
|
|
41321
41741
|
var WORKFLOWS = {
|
|
41322
41742
|
[actualizacionArancel.clave]: actualizacionArancel,
|
|
@@ -41325,7 +41745,8 @@ var WORKFLOWS = {
|
|
|
41325
41745
|
[cuentaCompleta.clave]: cuentaCompleta,
|
|
41326
41746
|
[facturacionPendiente.clave]: facturacionPendiente,
|
|
41327
41747
|
[credencialProfesional.clave]: credencialProfesional,
|
|
41328
|
-
[demandaPendiente.clave]: demandaPendiente
|
|
41748
|
+
[demandaPendiente.clave]: demandaPendiente,
|
|
41749
|
+
[circularAranceles.clave]: circularAranceles
|
|
41329
41750
|
};
|
|
41330
41751
|
function getWorkflow(clave) {
|
|
41331
41752
|
return WORKFLOWS[clave] ?? null;
|
|
@@ -41951,6 +42372,7 @@ async function listarPoliticas(ctx, companyId) {
|
|
|
41951
42372
|
}
|
|
41952
42373
|
function describirOpciones(antes, despues) {
|
|
41953
42374
|
const partes = [];
|
|
42375
|
+
if (antes.tope_variacion !== despues.tope_variacion && despues.tope_variacion !== void 0) partes.push(`tope de variaci\xF3n de las circulares: ${String(despues.tope_variacion)} %`);
|
|
41954
42376
|
if (Boolean(antes.ofrecer_primero) !== Boolean(despues.ofrecer_primero)) partes.push(`consultar al paciente antes de asignar: ${despues.ofrecer_primero ? "s\xED" : "no"}`);
|
|
41955
42377
|
const plazosAntes = JSON.stringify(leerPlazos(antes.plazos));
|
|
41956
42378
|
const plazosDespues = leerPlazos(despues.plazos);
|
|
@@ -41960,6 +42382,10 @@ function describirOpciones(antes, despues) {
|
|
|
41960
42382
|
}
|
|
41961
42383
|
return partes.length ? partes.join(" \xB7 ") : null;
|
|
41962
42384
|
}
|
|
42385
|
+
function leerTope(v) {
|
|
42386
|
+
const n = typeof v === "number" ? v : typeof v === "string" && v.trim() !== "" ? Number(v) : NaN;
|
|
42387
|
+
return Number.isFinite(n) && n >= LIMITES_TOPE[0] && n <= LIMITES_TOPE[1] ? Math.round(n) : null;
|
|
42388
|
+
}
|
|
41963
42389
|
function leerPlazos(v) {
|
|
41964
42390
|
if (!v || typeof v !== "object" || Array.isArray(v)) return {};
|
|
41965
42391
|
const out = {};
|
|
@@ -41976,6 +42402,11 @@ async function cambiarPolitica(ctx, input2) {
|
|
|
41976
42402
|
const opcionesAntes = await obtenerOpciones(ctx, input2.companyId, input2.workflow);
|
|
41977
42403
|
const opciones = { ...opcionesAntes, ...input2.opciones ?? {} };
|
|
41978
42404
|
if (opciones.plazos !== void 0) opciones.plazos = leerPlazos(opciones.plazos);
|
|
42405
|
+
if (opciones.tope_variacion !== void 0) {
|
|
42406
|
+
const tope = leerTope(opciones.tope_variacion);
|
|
42407
|
+
if (tope === null) throw new OpsError("validacion", `tope_variacion: tiene que ser un n\xFAmero entre ${LIMITES_TOPE[0]} y ${LIMITES_TOPE[1]}`);
|
|
42408
|
+
opciones.tope_variacion = tope;
|
|
42409
|
+
}
|
|
41979
42410
|
const cambioOpciones = describirOpciones(opcionesAntes, opciones);
|
|
41980
42411
|
const motivo = [input2.motivo ?? null, cambioOpciones].filter(Boolean).join(" \xB7 ") || null;
|
|
41981
42412
|
const existe = await ctx.db.query(
|
|
@@ -42085,6 +42516,13 @@ async function abrirCaso(ctx, input2) {
|
|
|
42085
42516
|
if (!referencia || !/^profesional:\S+$/.test(referencia)) throw new OpsError("referencia_requerida", "en credencial_profesional la referencia es obligatoria: profesional:<id del profesional>");
|
|
42086
42517
|
if (prof && referencia !== `profesional:${prof}`) throw new OpsError("referencia_requerida", `la referencia ${referencia} no coincide con el profesional_id ${prof}`);
|
|
42087
42518
|
}
|
|
42519
|
+
if (wf.clave === "circular_aranceles") {
|
|
42520
|
+
const d = input2.datos ?? {};
|
|
42521
|
+
if (!referencia || !/^circular:\S+$/.test(referencia)) throw new OpsError("referencia_requerida", "en circular_aranceles la referencia es obligatoria: circular:<obra social>:<hash corto del archivo>");
|
|
42522
|
+
const faltan = ["obra_social", "convenio", "vigencia_desde"].filter((k) => !str2(d[k]));
|
|
42523
|
+
if (faltan.length) throw new OpsError("validacion", `circular_aranceles necesita ${faltan.join(", ")}`);
|
|
42524
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(String(d.vigencia_desde))) throw new OpsError("validacion", "vigencia_desde: AAAA-MM-DD");
|
|
42525
|
+
}
|
|
42088
42526
|
const extra = {};
|
|
42089
42527
|
if (wf.clave === "demanda_pendiente") {
|
|
42090
42528
|
if (!referencia || !/^(orden|solicitud|control):\S+$/.test(referencia)) {
|
|
@@ -42101,7 +42539,7 @@ async function abrirCaso(ctx, input2) {
|
|
|
42101
42539
|
extra.tipo = regla.tipo;
|
|
42102
42540
|
extra.canal = regla.contacto.canal;
|
|
42103
42541
|
extra.intentos = 0;
|
|
42104
|
-
extra.vence_el =
|
|
42542
|
+
extra.vence_el = sumarDias2(hoy, regla.cierre.dias_para_cerrar);
|
|
42105
42543
|
extra.detectado_el = hoy;
|
|
42106
42544
|
}
|
|
42107
42545
|
if (referencia) {
|
|
@@ -42153,6 +42591,10 @@ async function contextoDe(ctx, companyId, caso, via, datos) {
|
|
|
42153
42591
|
contexto.plazosPresentacion = leerPlazos((await obtenerOpciones(ctx, companyId, caso.workflow)).plazos);
|
|
42154
42592
|
return contexto;
|
|
42155
42593
|
}
|
|
42594
|
+
if (caso.workflow === "circular_aranceles") {
|
|
42595
|
+
contexto.topeVariacion = leerTope((await obtenerOpciones(ctx, companyId, caso.workflow)).tope_variacion);
|
|
42596
|
+
return contexto;
|
|
42597
|
+
}
|
|
42156
42598
|
if (caso.workflow === "demanda_pendiente") {
|
|
42157
42599
|
const reglaId = str2(caso.datos.regla_id);
|
|
42158
42600
|
const regla = reglaId ? await leerRegla(ctx, companyId, reglaId) : null;
|
|
@@ -42223,7 +42665,7 @@ async function transicionar(ctx, input2) {
|
|
|
42223
42665
|
);
|
|
42224
42666
|
const lectura = rows[0];
|
|
42225
42667
|
let vieja = false;
|
|
42226
|
-
if (lectura && resultado.via === "comprobar") {
|
|
42668
|
+
if (lectura && (resultado.via === "comprobar" || caso.workflow === "circular_aranceles" && resultado.via === "verificar")) {
|
|
42227
42669
|
const ultima = await ctx.db.query(
|
|
42228
42670
|
`SELECT max(created_at) AS ultima FROM ${t2(ctx, "transicion")} WHERE company_id = $1 AND caso_id = $2`,
|
|
42229
42671
|
[input2.companyId, caso.id]
|
|
@@ -42236,6 +42678,48 @@ async function transicionar(ctx, input2) {
|
|
|
42236
42678
|
throw new OpsError("validacion", `lectura_registrada: ${problema}`, { validaciones: [{ regla: "lectura_registrada", ok: false, detalle: problema }], estado: caso.estado });
|
|
42237
42679
|
}
|
|
42238
42680
|
}
|
|
42681
|
+
const lecturaCircularId = str2(datos.lectura_circular_id);
|
|
42682
|
+
if (caso.workflow === "circular_aranceles" && lecturaCircularId) {
|
|
42683
|
+
const fila = (await ctx.db.query(`SELECT id, caso_id, tipo, conector, resultado FROM ${t2(ctx, "accion")} WHERE company_id = $1 AND id = $2 LIMIT 1`, [input2.companyId, lecturaCircularId]))[0];
|
|
42684
|
+
const problema = !fila ? `lectura_circular_id ${lecturaCircularId} no existe en esta instituci\xF3n` : String(fila.caso_id) !== caso.id ? `la lectura ${lecturaCircularId} pertenece a otro caso` : String(fila.tipo) !== "lectura" || String(fila.conector) !== "archivo" ? "la lectura de la circular tiene que ser una lectura con conector archivo" : String(fila.resultado) !== "ok" ? `la lectura ${lecturaCircularId} termin\xF3 con resultado ${String(fila.resultado)}` : null;
|
|
42685
|
+
if (problema) throw new OpsError("validacion", `lectura_circular_registrada: ${problema}`, { validaciones: [{ regla: "lectura_circular_registrada", ok: false, detalle: problema }], estado: caso.estado });
|
|
42686
|
+
}
|
|
42687
|
+
if (caso.workflow === "circular_aranceles") {
|
|
42688
|
+
delete datos.items;
|
|
42689
|
+
delete datos.resumen;
|
|
42690
|
+
delete datos.archivo_csv;
|
|
42691
|
+
delete datos.diferencias;
|
|
42692
|
+
if (resultado.via === "extraer") {
|
|
42693
|
+
const cruce = cruzarCircular({
|
|
42694
|
+
extraccionA: datos.extraccion_a,
|
|
42695
|
+
extraccionB: datos.extraccion_b,
|
|
42696
|
+
actual: datos.actual,
|
|
42697
|
+
filasDeclaradas: datos.filas_declaradas ?? caso.datos.filas_declaradas,
|
|
42698
|
+
porcentajeDeclarado: datos.porcentaje_declarado ?? caso.datos.porcentaje_declarado,
|
|
42699
|
+
vigenciaDesde: datos.vigencia_desde ?? caso.datos.vigencia_desde,
|
|
42700
|
+
hoy: contexto.hoy ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
42701
|
+
topeVariacion: contexto.topeVariacion ?? null
|
|
42702
|
+
});
|
|
42703
|
+
if (cruce.ok) {
|
|
42704
|
+
datos.items = cruce.items;
|
|
42705
|
+
datos.resumen = cruce.resumen;
|
|
42706
|
+
const act = leerActual(datos.actual);
|
|
42707
|
+
datos.actual = "practicas" in act ? act.practicas : [];
|
|
42708
|
+
}
|
|
42709
|
+
}
|
|
42710
|
+
if (resultado.via === "aprobar") datos.excluir = Array.isArray(datos.excluir) ? datos.excluir.map(normalizarCodigo) : [];
|
|
42711
|
+
if (resultado.a === "aprobada") {
|
|
42712
|
+
const aCargar = itemsACargar(leerItems(caso.datos.items), datos.excluir ?? caso.datos.excluir);
|
|
42713
|
+
const actual = leerActual(caso.datos.actual);
|
|
42714
|
+
const archivo = archivoActualizacion(aCargar, "practicas" in actual ? actual.practicas : []);
|
|
42715
|
+
datos.archivo_csv = { contenido: archivo.contenido, hash: archivo.hash, filas: archivo.filas, aranceles: archivo.aranceles, nombre: `actualizacion-${String(caso.datos.convenio ?? "convenio").toLowerCase().replace(/[^a-z0-9]+/g, "-")}-${String(caso.datos.vigencia_desde ?? "")}.csv` };
|
|
42716
|
+
}
|
|
42717
|
+
if (resultado.via === "verificar") {
|
|
42718
|
+
const r = compararRelectura(itemsACargar(leerItems(caso.datos.items), caso.datos.excluir), datos.leidos);
|
|
42719
|
+
datos.diferencias = "error" in r ? [] : r.diferencias;
|
|
42720
|
+
datos.verificada_el = contexto.hoy ?? null;
|
|
42721
|
+
}
|
|
42722
|
+
}
|
|
42239
42723
|
if (caso.workflow === "cuenta_completa" && resultado.via === "observar") {
|
|
42240
42724
|
datos.faltantes = leerFaltantes(datos.faltantes) ?? [];
|
|
42241
42725
|
datos.faltantes_iniciales = datos.faltantes;
|
|
@@ -42265,7 +42749,7 @@ async function transicionar(ctx, input2) {
|
|
|
42265
42749
|
datos.intentos = hechos;
|
|
42266
42750
|
datos.ultimo_contacto = contexto.hoy ?? null;
|
|
42267
42751
|
datos.canal = canalContacto(datos.canal);
|
|
42268
|
-
datos.proximo_contacto = regla && hechos < regla.contacto.max_intentos && contexto.hoy ?
|
|
42752
|
+
datos.proximo_contacto = regla && hechos < regla.contacto.max_intentos && contexto.hoy ? sumarDias2(contexto.hoy, regla.contacto.dias_entre_intentos) : null;
|
|
42269
42753
|
}
|
|
42270
42754
|
if (resultado.via === "responder") {
|
|
42271
42755
|
datos.respuesta = respuestaContacto(datos.respuesta);
|
|
@@ -42411,7 +42895,8 @@ async function registrarCargaManual(ctx, input2) {
|
|
|
42411
42895
|
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 });
|
|
42412
42896
|
}
|
|
42413
42897
|
const contextoPrevio = await contextoDe(ctx, input2.companyId, caso, def2.via, {});
|
|
42414
|
-
const
|
|
42898
|
+
const archivoHash = str2(input2.archivoHash);
|
|
42899
|
+
const previa = evaluarTransicion({ caso, via: def2.via, actor: "humano", datos: { accion_id: "(por registrar)", ...archivoHash ? { archivo_hash: archivoHash } : {} }, modo: "ejecuta", contexto: contextoPrevio });
|
|
42415
42900
|
if (!previa.ok) throw new OpsError(previa.codigo, previa.motivo, { validaciones: previa.validaciones, estado: caso.estado });
|
|
42416
42901
|
const persona = str2(input2.persona);
|
|
42417
42902
|
const comentario = str2(input2.comentario);
|
|
@@ -42429,6 +42914,7 @@ async function registrarCargaManual(ctx, input2) {
|
|
|
42429
42914
|
...caso.datos.vigencia_desde !== void 0 ? { vigencia_desde: caso.datos.vigencia_desde } : {},
|
|
42430
42915
|
...caso.workflow === "cuenta_completa" ? { faltantes_que_completo: caso.datos.faltantes ?? [] } : {},
|
|
42431
42916
|
...caso.workflow === "facturacion_pendiente" ? { bono: caso.datos.bono_numero ?? caso.datos.bono_id, vence_el: caso.datos.vence_el } : {},
|
|
42917
|
+
...caso.workflow === "circular_aranceles" ? { archivo_hash: archivoHash, archivo: caso.datos.archivo_csv?.nombre ?? null, practicas: caso.datos.archivo_csv?.filas ?? null, convenio: caso.datos.convenio ?? null } : {},
|
|
42432
42918
|
...caso.workflow === "credencial_profesional" ? { profesional: caso.datos.profesional ?? caso.datos.profesional_id, observacion_que_corrigio: caso.datos.observacion ?? null } : {},
|
|
42433
42919
|
...comentario ? { nota: comentario } : {}
|
|
42434
42920
|
},
|
|
@@ -42440,7 +42926,8 @@ async function registrarCargaManual(ctx, input2) {
|
|
|
42440
42926
|
...persona ? { cargado_por: persona } : {},
|
|
42441
42927
|
...valorCargado !== null ? { valor_cargado: valorCargado } : {},
|
|
42442
42928
|
// Una lectura anterior (la que dio diferencias) ya no describe lo que hay en el sistema.
|
|
42443
|
-
valor_leido: null
|
|
42929
|
+
valor_leido: null,
|
|
42930
|
+
...archivoHash ? { archivo_hash: archivoHash } : {}
|
|
42444
42931
|
};
|
|
42445
42932
|
try {
|
|
42446
42933
|
const r = await transicionar(ctx, { companyId: input2.companyId, casoId: caso.id, via: def2.via, datos, motivo: comentario, idempotencyKey: idem, actor: input2.actor });
|
|
@@ -42863,7 +43350,7 @@ function mapAviso(r) {
|
|
|
42863
43350
|
};
|
|
42864
43351
|
}
|
|
42865
43352
|
async function listarAvisos(ctx, companyId, opciones = {}) {
|
|
42866
|
-
const desde = opciones.desde ??
|
|
43353
|
+
const desde = opciones.desde ?? sumarDias2(ahoraEnHospital().hoy, -DIAS_VENTANA);
|
|
42867
43354
|
const rows = await ctx.db.query(`SELECT ${COLS_AVISO} FROM ${t3(ctx, "aviso")} WHERE company_id = $1 AND dia >= $2 ORDER BY created_at ASC`, [companyId, desde]);
|
|
42868
43355
|
return rows.map(mapAviso);
|
|
42869
43356
|
}
|
|
@@ -42945,7 +43432,7 @@ async function generarAvisos(ctx, input2) {
|
|
|
42945
43432
|
}
|
|
42946
43433
|
const h = historial.get(`${caso.id}|${contacto.clave}`) ?? { veces: 0, ultimo: "" };
|
|
42947
43434
|
if (h.veces >= MAX_AVISOS) continue;
|
|
42948
|
-
if (h.veces > 0 &&
|
|
43435
|
+
if (h.veces > 0 && sumarDias2(h.ultimo, DIAS_ENTRE_AVISOS) > hoy) continue;
|
|
42949
43436
|
if (yaHoy.has(contacto.clave)) continue;
|
|
42950
43437
|
const g = porDestinatario.get(contacto.clave) ?? { contacto, pendientes: [] };
|
|
42951
43438
|
g.pendientes.push({ casoId: caso.id, workflow: caso.workflow, que: p.que, intento: h.veces + 1, titulo: caso.titulo });
|
|
@@ -43576,7 +44063,7 @@ async function manejarApi(ctx, input2) {
|
|
|
43576
44063
|
case "rechazar":
|
|
43577
44064
|
return respuesta(200, await transicionar(ctx, { companyId, casoId: input2.params.casoId ?? "", via: "rechazar", motivo: str3(body.motivo), datos: conPersona(obj(body.datos)), actor }));
|
|
43578
44065
|
case "marcar-cargado":
|
|
43579
|
-
return respuesta(200, await registrarCargaManual(ctx, { companyId, casoId: input2.params.casoId ?? "", valorCargado: body.valorCargado, comentario: str3(body.comentario), persona: decididoPor, idempotencyKey: str3(body.idempotencyKey), actor }));
|
|
44066
|
+
return respuesta(200, await registrarCargaManual(ctx, { companyId, casoId: input2.params.casoId ?? "", valorCargado: body.valorCargado, archivoHash: str3(body.archivoHash), comentario: str3(body.comentario), persona: decididoPor, idempotencyKey: str3(body.idempotencyKey), actor }));
|
|
43580
44067
|
case "respuesta-paciente":
|
|
43581
44068
|
return respuesta(200, await registrarRespuestaPaciente(ctx, { companyId, casoId: input2.params.casoId ?? "", acepto: body.acepto === true, comentario: str3(body.comentario), persona: decididoPor, idempotencyKey: str3(body.idempotencyKey), actor }));
|
|
43582
44069
|
case "no-corresponde": {
|
|
@@ -43585,7 +44072,7 @@ async function manejarApi(ctx, input2) {
|
|
|
43585
44072
|
return respuesta(200, await transicionar(ctx, { companyId, casoId: input2.params.casoId ?? "", via: "no_corresponde", motivo: motivoConPersona(str3(body.motivo)), datos: conPersona(obj(body.datos)), idempotencyKey: str3(body.idempotencyKey), actor }));
|
|
43586
44073
|
}
|
|
43587
44074
|
case "version":
|
|
43588
|
-
return respuesta(200, { version: manifest_default.version, capacidades: ["carga-manual", "recupero-turnos", "ofrecer-primero", "cuenta-completa", "metricas-certificadas", "facturacion-pendiente", "credenciales", "demanda-pendiente", "reglas", "avisos"], workflows: listarWorkflows().map((w) => w.clave) });
|
|
44075
|
+
return respuesta(200, { version: manifest_default.version, capacidades: ["carga-manual", "recupero-turnos", "ofrecer-primero", "cuenta-completa", "metricas-certificadas", "facturacion-pendiente", "credenciales", "demanda-pendiente", "reglas", "avisos", "circular-aranceles"], workflows: listarWorkflows().map((w) => w.clave) });
|
|
43589
44076
|
case "politicas":
|
|
43590
44077
|
return respuesta(200, { politicas: await listarPoliticas(ctx, companyId) });
|
|
43591
44078
|
case "serie":
|
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.22",
|
|
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": [
|