paperclip-plugin-autopilot-ops 0.1.34 → 0.1.36
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 +19 -3
- package/dist/ui/index.js +19 -3
- package/dist/worker.js +595 -50
- 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.36";
|
|
4
4
|
var DB_NAMESPACE_SLUG = "autopilot_ops";
|
|
5
5
|
var PAGE_ROUTE = "autopilot";
|
|
6
6
|
var TOOL_NAMES = {
|
|
@@ -133,7 +133,7 @@ var manifest = {
|
|
|
133
133
|
{
|
|
134
134
|
name: TOOL_NAMES.abrirCaso,
|
|
135
135
|
displayName: "Abrir caso",
|
|
136
|
-
description: 'Abre un caso nuevo en un workflow, en su estado inicial. Workflows: `internacion_al_dia` (una internaci\xF3n abierta; referencia obligatoria `internacion:<id>`; datos: internacion_id), `honorario_en_cero` (referencia `honorarios:<profesional_id>`; datos: profesional_id, profesional), `debito_profesional` (referencia `debito:<profesional_id>:<nota_credito_id>`; datos: profesional_id, profesional, nota_credito_id, obra_social), `liquidacion_profesional` (referencia `liquidacion:<profesional_id>:<AAAA-MM-DD>`; datos: profesional_id, profesional, liquidado_el, institucion, mail_his), `cobranza_financiador` (un caso abierto por financiador; referencia obligatoria `cobranza:<obra_social_id>`; datos: obra_social (nombre como en el HIS), obra_social_id, institucion (nombre de la instituci\xF3n para el reclamo)), `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 (regla activa) y candidato tal cual lo devuelve buscar-demanda.mjs; el plugin verifica la regla con el candidato (si no la cumple, no abre) y 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), `rentabilidad_practica` (referencia obligatoria `rentabilidad:<prestacion_id>:<financiador_id>`; datos: prestacion_id, financiador_id, codigo, practica, financiador), `rentabilidad_internacion` (una internaci\xF3n egresada; referencia obligatoria `rentabilidad_internacion:<internacion_id>`; datos: internacion_id, financiador_id, financiador). `referencia` identifica el objeto real (convenio, cuenta, turno) y evita duplicados: si ya existe un caso abierto con la misma referencia, se devuelve ese (creado: false). En `rentabilidad_internacion` se devuelve el caso existente aunque est\xE9 cerrado: una internaci\xF3n se mide una sola vez. Pasar `idempotencyKey` para reintentos seguros.',
|
|
136
|
+
description: 'Abre un caso nuevo en un workflow, en su estado inicial. Workflows: `internacion_al_dia` (una internaci\xF3n abierta; referencia obligatoria `internacion:<id>`; datos: internacion_id), `honorario_en_cero` (referencia `honorarios:<profesional_id>`; datos: profesional_id, profesional), `debito_profesional` (referencia `debito:<profesional_id>:<nota_credito_id>`; datos: profesional_id, profesional, nota_credito_id, obra_social), `liquidacion_profesional` (referencia `liquidacion:<profesional_id>:<AAAA-MM-DD>`; datos: profesional_id, profesional, liquidado_el, institucion, mail_his), `cobranza_financiador` (un caso abierto por financiador; referencia obligatoria `cobranza:<obra_social_id>`; datos: obra_social (nombre como en el HIS), obra_social_id, institucion (nombre de la instituci\xF3n para el reclamo)), `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), `ajuste_convenio` (el ajuste de un convenio por \xEDndice; referencia obligatoria `ajuste:<convenio>:<AAAA-MM de la vigencia>`; datos: financiador, convenio (nombre en el HIS), vigencia_desde AAAA-MM-DD, ultimo_ajuste AAAA-MM (el mes desde el que se mide la variaci\xF3n), institucion), `demanda_pendiente` (referencia obligatoria `orden:<id>`, `solicitud:<id>` o `control:<persona_id>:<regla_id>`; datos: regla_id (regla activa) y candidato tal cual lo devuelve buscar-demanda.mjs; el plugin verifica la regla con el candidato (si no la cumple, no abre) y 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), `rentabilidad_practica` (referencia obligatoria `rentabilidad:<prestacion_id>:<financiador_id>`; datos: prestacion_id, financiador_id, codigo, practica, financiador), `rentabilidad_internacion` (una internaci\xF3n egresada; referencia obligatoria `rentabilidad_internacion:<internacion_id>`; datos: internacion_id, financiador_id, financiador). `referencia` identifica el objeto real (convenio, cuenta, turno) y evita duplicados: si ya existe un caso abierto con la misma referencia, se devuelve ese (creado: false). En `rentabilidad_internacion` se devuelve el caso existente aunque est\xE9 cerrado: una internaci\xF3n se mide una sola vez. Pasar `idempotencyKey` para reintentos seguros.',
|
|
137
137
|
parametersSchema: {
|
|
138
138
|
type: "object",
|
|
139
139
|
properties: {
|
|
@@ -150,7 +150,7 @@ var manifest = {
|
|
|
150
150
|
{
|
|
151
151
|
name: TOOL_NAMES.transicionar,
|
|
152
152
|
displayName: "Transicionar caso",
|
|
153
|
-
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 lectura (el bono tal cual lo devuelve revisar.mjs) y lectura_id (conector "base"); `comprobar` con la lectura nueva y lectura_id; el plugin decide los faltantes; `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 `cobranza_financiador`, `circular_aranceles`, `internacion_al_dia` y los de honorarios los datos de cada v\xEDa est\xE1n en la skill del puesto (cobranzas, aranceles, internaciones, honorarios): el agente informa lo que ley\xF3 del HIS o del documento tal cual y el plugin decide qu\xE9 se reclama o se carga. 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.',
|
|
153
|
+
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 lectura (el bono tal cual lo devuelve revisar.mjs) y lectura_id (conector "base"); `comprobar` con la lectura nueva y lectura_id; el plugin decide los faltantes; `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 `cobranza_financiador`, `circular_aranceles`, `ajuste_convenio`, `internacion_al_dia` y los de honorarios los datos de cada v\xEDa est\xE1n en la skill del puesto (cobranzas, aranceles, internaciones, honorarios): el agente informa lo que ley\xF3 del HIS o del documento tal cual y el plugin decide qu\xE9 se reclama o se carga. 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.',
|
|
154
154
|
parametersSchema: {
|
|
155
155
|
type: "object",
|
|
156
156
|
properties: {
|
|
@@ -450,6 +450,22 @@ var manifest = {
|
|
|
450
450
|
capability: "api.routes.register",
|
|
451
451
|
companyResolution: { from: "body", key: "companyId" }
|
|
452
452
|
},
|
|
453
|
+
{
|
|
454
|
+
routeKey: "ajustar",
|
|
455
|
+
method: "POST",
|
|
456
|
+
path: "/casos/:casoId/ajustar",
|
|
457
|
+
auth: "board",
|
|
458
|
+
capability: "api.routes.register",
|
|
459
|
+
companyResolution: { from: "body", key: "companyId" }
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
routeKey: "envio-nota",
|
|
463
|
+
method: "POST",
|
|
464
|
+
path: "/casos/:casoId/envio-nota",
|
|
465
|
+
auth: "board",
|
|
466
|
+
capability: "api.routes.register",
|
|
467
|
+
companyResolution: { from: "body", key: "companyId" }
|
|
468
|
+
},
|
|
453
469
|
{
|
|
454
470
|
routeKey: "respuesta-financiador",
|
|
455
471
|
method: "POST",
|
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.36";
|
|
12
12
|
var DB_NAMESPACE_SLUG = "autopilot_ops";
|
|
13
13
|
var PAGE_ROUTE = "autopilot";
|
|
14
14
|
var TOOL_NAMES = {
|
|
@@ -141,7 +141,7 @@ var manifest = {
|
|
|
141
141
|
{
|
|
142
142
|
name: TOOL_NAMES.abrirCaso,
|
|
143
143
|
displayName: "Abrir caso",
|
|
144
|
-
description: 'Abre un caso nuevo en un workflow, en su estado inicial. Workflows: `internacion_al_dia` (una internaci\xF3n abierta; referencia obligatoria `internacion:<id>`; datos: internacion_id), `honorario_en_cero` (referencia `honorarios:<profesional_id>`; datos: profesional_id, profesional), `debito_profesional` (referencia `debito:<profesional_id>:<nota_credito_id>`; datos: profesional_id, profesional, nota_credito_id, obra_social), `liquidacion_profesional` (referencia `liquidacion:<profesional_id>:<AAAA-MM-DD>`; datos: profesional_id, profesional, liquidado_el, institucion, mail_his), `cobranza_financiador` (un caso abierto por financiador; referencia obligatoria `cobranza:<obra_social_id>`; datos: obra_social (nombre como en el HIS), obra_social_id, institucion (nombre de la instituci\xF3n para el reclamo)), `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 (regla activa) y candidato tal cual lo devuelve buscar-demanda.mjs; el plugin verifica la regla con el candidato (si no la cumple, no abre) y 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), `rentabilidad_practica` (referencia obligatoria `rentabilidad:<prestacion_id>:<financiador_id>`; datos: prestacion_id, financiador_id, codigo, practica, financiador), `rentabilidad_internacion` (una internaci\xF3n egresada; referencia obligatoria `rentabilidad_internacion:<internacion_id>`; datos: internacion_id, financiador_id, financiador). `referencia` identifica el objeto real (convenio, cuenta, turno) y evita duplicados: si ya existe un caso abierto con la misma referencia, se devuelve ese (creado: false). En `rentabilidad_internacion` se devuelve el caso existente aunque est\xE9 cerrado: una internaci\xF3n se mide una sola vez. Pasar `idempotencyKey` para reintentos seguros.',
|
|
144
|
+
description: 'Abre un caso nuevo en un workflow, en su estado inicial. Workflows: `internacion_al_dia` (una internaci\xF3n abierta; referencia obligatoria `internacion:<id>`; datos: internacion_id), `honorario_en_cero` (referencia `honorarios:<profesional_id>`; datos: profesional_id, profesional), `debito_profesional` (referencia `debito:<profesional_id>:<nota_credito_id>`; datos: profesional_id, profesional, nota_credito_id, obra_social), `liquidacion_profesional` (referencia `liquidacion:<profesional_id>:<AAAA-MM-DD>`; datos: profesional_id, profesional, liquidado_el, institucion, mail_his), `cobranza_financiador` (un caso abierto por financiador; referencia obligatoria `cobranza:<obra_social_id>`; datos: obra_social (nombre como en el HIS), obra_social_id, institucion (nombre de la instituci\xF3n para el reclamo)), `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), `ajuste_convenio` (el ajuste de un convenio por \xEDndice; referencia obligatoria `ajuste:<convenio>:<AAAA-MM de la vigencia>`; datos: financiador, convenio (nombre en el HIS), vigencia_desde AAAA-MM-DD, ultimo_ajuste AAAA-MM (el mes desde el que se mide la variaci\xF3n), institucion), `demanda_pendiente` (referencia obligatoria `orden:<id>`, `solicitud:<id>` o `control:<persona_id>:<regla_id>`; datos: regla_id (regla activa) y candidato tal cual lo devuelve buscar-demanda.mjs; el plugin verifica la regla con el candidato (si no la cumple, no abre) y 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), `rentabilidad_practica` (referencia obligatoria `rentabilidad:<prestacion_id>:<financiador_id>`; datos: prestacion_id, financiador_id, codigo, practica, financiador), `rentabilidad_internacion` (una internaci\xF3n egresada; referencia obligatoria `rentabilidad_internacion:<internacion_id>`; datos: internacion_id, financiador_id, financiador). `referencia` identifica el objeto real (convenio, cuenta, turno) y evita duplicados: si ya existe un caso abierto con la misma referencia, se devuelve ese (creado: false). En `rentabilidad_internacion` se devuelve el caso existente aunque est\xE9 cerrado: una internaci\xF3n se mide una sola vez. Pasar `idempotencyKey` para reintentos seguros.',
|
|
145
145
|
parametersSchema: {
|
|
146
146
|
type: "object",
|
|
147
147
|
properties: {
|
|
@@ -158,7 +158,7 @@ var manifest = {
|
|
|
158
158
|
{
|
|
159
159
|
name: TOOL_NAMES.transicionar,
|
|
160
160
|
displayName: "Transicionar caso",
|
|
161
|
-
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 lectura (el bono tal cual lo devuelve revisar.mjs) y lectura_id (conector "base"); `comprobar` con la lectura nueva y lectura_id; el plugin decide los faltantes; `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 `cobranza_financiador`, `circular_aranceles`, `internacion_al_dia` y los de honorarios los datos de cada v\xEDa est\xE1n en la skill del puesto (cobranzas, aranceles, internaciones, honorarios): el agente informa lo que ley\xF3 del HIS o del documento tal cual y el plugin decide qu\xE9 se reclama o se carga. 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.',
|
|
161
|
+
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 lectura (el bono tal cual lo devuelve revisar.mjs) y lectura_id (conector "base"); `comprobar` con la lectura nueva y lectura_id; el plugin decide los faltantes; `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 `cobranza_financiador`, `circular_aranceles`, `ajuste_convenio`, `internacion_al_dia` y los de honorarios los datos de cada v\xEDa est\xE1n en la skill del puesto (cobranzas, aranceles, internaciones, honorarios): el agente informa lo que ley\xF3 del HIS o del documento tal cual y el plugin decide qu\xE9 se reclama o se carga. 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.',
|
|
162
162
|
parametersSchema: {
|
|
163
163
|
type: "object",
|
|
164
164
|
properties: {
|
|
@@ -458,6 +458,22 @@ var manifest = {
|
|
|
458
458
|
capability: "api.routes.register",
|
|
459
459
|
companyResolution: { from: "body", key: "companyId" }
|
|
460
460
|
},
|
|
461
|
+
{
|
|
462
|
+
routeKey: "ajustar",
|
|
463
|
+
method: "POST",
|
|
464
|
+
path: "/casos/:casoId/ajustar",
|
|
465
|
+
auth: "board",
|
|
466
|
+
capability: "api.routes.register",
|
|
467
|
+
companyResolution: { from: "body", key: "companyId" }
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
routeKey: "envio-nota",
|
|
471
|
+
method: "POST",
|
|
472
|
+
path: "/casos/:casoId/envio-nota",
|
|
473
|
+
auth: "board",
|
|
474
|
+
capability: "api.routes.register",
|
|
475
|
+
companyResolution: { from: "body", key: "companyId" }
|
|
476
|
+
},
|
|
461
477
|
{
|
|
462
478
|
routeKey: "respuesta-financiador",
|
|
463
479
|
method: "POST",
|
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.36";
|
|
39936
39936
|
var DB_NAMESPACE_SLUG = "autopilot_ops";
|
|
39937
39937
|
var PAGE_ROUTE = "autopilot";
|
|
39938
39938
|
var TOOL_NAMES = {
|
|
@@ -40065,7 +40065,7 @@ var manifest = {
|
|
|
40065
40065
|
{
|
|
40066
40066
|
name: TOOL_NAMES.abrirCaso,
|
|
40067
40067
|
displayName: "Abrir caso",
|
|
40068
|
-
description: 'Abre un caso nuevo en un workflow, en su estado inicial. Workflows: `internacion_al_dia` (una internaci\xF3n abierta; referencia obligatoria `internacion:<id>`; datos: internacion_id), `honorario_en_cero` (referencia `honorarios:<profesional_id>`; datos: profesional_id, profesional), `debito_profesional` (referencia `debito:<profesional_id>:<nota_credito_id>`; datos: profesional_id, profesional, nota_credito_id, obra_social), `liquidacion_profesional` (referencia `liquidacion:<profesional_id>:<AAAA-MM-DD>`; datos: profesional_id, profesional, liquidado_el, institucion, mail_his), `cobranza_financiador` (un caso abierto por financiador; referencia obligatoria `cobranza:<obra_social_id>`; datos: obra_social (nombre como en el HIS), obra_social_id, institucion (nombre de la instituci\xF3n para el reclamo)), `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 (regla activa) y candidato tal cual lo devuelve buscar-demanda.mjs; el plugin verifica la regla con el candidato (si no la cumple, no abre) y 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), `rentabilidad_practica` (referencia obligatoria `rentabilidad:<prestacion_id>:<financiador_id>`; datos: prestacion_id, financiador_id, codigo, practica, financiador), `rentabilidad_internacion` (una internaci\xF3n egresada; referencia obligatoria `rentabilidad_internacion:<internacion_id>`; datos: internacion_id, financiador_id, financiador). `referencia` identifica el objeto real (convenio, cuenta, turno) y evita duplicados: si ya existe un caso abierto con la misma referencia, se devuelve ese (creado: false). En `rentabilidad_internacion` se devuelve el caso existente aunque est\xE9 cerrado: una internaci\xF3n se mide una sola vez. Pasar `idempotencyKey` para reintentos seguros.',
|
|
40068
|
+
description: 'Abre un caso nuevo en un workflow, en su estado inicial. Workflows: `internacion_al_dia` (una internaci\xF3n abierta; referencia obligatoria `internacion:<id>`; datos: internacion_id), `honorario_en_cero` (referencia `honorarios:<profesional_id>`; datos: profesional_id, profesional), `debito_profesional` (referencia `debito:<profesional_id>:<nota_credito_id>`; datos: profesional_id, profesional, nota_credito_id, obra_social), `liquidacion_profesional` (referencia `liquidacion:<profesional_id>:<AAAA-MM-DD>`; datos: profesional_id, profesional, liquidado_el, institucion, mail_his), `cobranza_financiador` (un caso abierto por financiador; referencia obligatoria `cobranza:<obra_social_id>`; datos: obra_social (nombre como en el HIS), obra_social_id, institucion (nombre de la instituci\xF3n para el reclamo)), `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), `ajuste_convenio` (el ajuste de un convenio por \xEDndice; referencia obligatoria `ajuste:<convenio>:<AAAA-MM de la vigencia>`; datos: financiador, convenio (nombre en el HIS), vigencia_desde AAAA-MM-DD, ultimo_ajuste AAAA-MM (el mes desde el que se mide la variaci\xF3n), institucion), `demanda_pendiente` (referencia obligatoria `orden:<id>`, `solicitud:<id>` o `control:<persona_id>:<regla_id>`; datos: regla_id (regla activa) y candidato tal cual lo devuelve buscar-demanda.mjs; el plugin verifica la regla con el candidato (si no la cumple, no abre) y 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), `rentabilidad_practica` (referencia obligatoria `rentabilidad:<prestacion_id>:<financiador_id>`; datos: prestacion_id, financiador_id, codigo, practica, financiador), `rentabilidad_internacion` (una internaci\xF3n egresada; referencia obligatoria `rentabilidad_internacion:<internacion_id>`; datos: internacion_id, financiador_id, financiador). `referencia` identifica el objeto real (convenio, cuenta, turno) y evita duplicados: si ya existe un caso abierto con la misma referencia, se devuelve ese (creado: false). En `rentabilidad_internacion` se devuelve el caso existente aunque est\xE9 cerrado: una internaci\xF3n se mide una sola vez. Pasar `idempotencyKey` para reintentos seguros.',
|
|
40069
40069
|
parametersSchema: {
|
|
40070
40070
|
type: "object",
|
|
40071
40071
|
properties: {
|
|
@@ -40082,7 +40082,7 @@ var manifest = {
|
|
|
40082
40082
|
{
|
|
40083
40083
|
name: TOOL_NAMES.transicionar,
|
|
40084
40084
|
displayName: "Transicionar caso",
|
|
40085
|
-
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 lectura (el bono tal cual lo devuelve revisar.mjs) y lectura_id (conector "base"); `comprobar` con la lectura nueva y lectura_id; el plugin decide los faltantes; `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 `cobranza_financiador`, `circular_aranceles`, `internacion_al_dia` y los de honorarios los datos de cada v\xEDa est\xE1n en la skill del puesto (cobranzas, aranceles, internaciones, honorarios): el agente informa lo que ley\xF3 del HIS o del documento tal cual y el plugin decide qu\xE9 se reclama o se carga. 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.',
|
|
40085
|
+
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 lectura (el bono tal cual lo devuelve revisar.mjs) y lectura_id (conector "base"); `comprobar` con la lectura nueva y lectura_id; el plugin decide los faltantes; `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 `cobranza_financiador`, `circular_aranceles`, `ajuste_convenio`, `internacion_al_dia` y los de honorarios los datos de cada v\xEDa est\xE1n en la skill del puesto (cobranzas, aranceles, internaciones, honorarios): el agente informa lo que ley\xF3 del HIS o del documento tal cual y el plugin decide qu\xE9 se reclama o se carga. 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.',
|
|
40086
40086
|
parametersSchema: {
|
|
40087
40087
|
type: "object",
|
|
40088
40088
|
properties: {
|
|
@@ -40382,6 +40382,22 @@ var manifest = {
|
|
|
40382
40382
|
capability: "api.routes.register",
|
|
40383
40383
|
companyResolution: { from: "body", key: "companyId" }
|
|
40384
40384
|
},
|
|
40385
|
+
{
|
|
40386
|
+
routeKey: "ajustar",
|
|
40387
|
+
method: "POST",
|
|
40388
|
+
path: "/casos/:casoId/ajustar",
|
|
40389
|
+
auth: "board",
|
|
40390
|
+
capability: "api.routes.register",
|
|
40391
|
+
companyResolution: { from: "body", key: "companyId" }
|
|
40392
|
+
},
|
|
40393
|
+
{
|
|
40394
|
+
routeKey: "envio-nota",
|
|
40395
|
+
method: "POST",
|
|
40396
|
+
path: "/casos/:casoId/envio-nota",
|
|
40397
|
+
auth: "board",
|
|
40398
|
+
capability: "api.routes.register",
|
|
40399
|
+
companyResolution: { from: "body", key: "companyId" }
|
|
40400
|
+
},
|
|
40385
40401
|
{
|
|
40386
40402
|
routeKey: "respuesta-financiador",
|
|
40387
40403
|
method: "POST",
|
|
@@ -41032,6 +41048,170 @@ function compararRelectura(aCargar, leidos) {
|
|
|
41032
41048
|
return { diferencias, comprobadas: aCargar.length };
|
|
41033
41049
|
}
|
|
41034
41050
|
|
|
41051
|
+
// src/ajuste.ts
|
|
41052
|
+
var SERIES = {
|
|
41053
|
+
ipc_general: { nombre: "IPC nivel general", id: "148.3_INIVELNAL_DICI_M_26" },
|
|
41054
|
+
ipc_salud: { nombre: "IPC salud", id: "146.3_ISALUDNAL_DICI_M_18" }
|
|
41055
|
+
};
|
|
41056
|
+
var FORMULA_POR_DEFECTO = { componentes: [{ serie: "ipc_general", peso: 100 }] };
|
|
41057
|
+
var TOPE_AJUSTE_POR_DEFECTO = 15;
|
|
41058
|
+
var LIMITES_TOPE_AJUSTE = [1, 100];
|
|
41059
|
+
var LIMITES_PORCENTAJE = [-50, 100];
|
|
41060
|
+
function leerVolumenes(v, dias) {
|
|
41061
|
+
if (v === void 0 || v === null) return null;
|
|
41062
|
+
if (!Array.isArray(v)) return { error: "volumenes tiene que ser una lista [{codigo, cantidad}]" };
|
|
41063
|
+
const d = typeof dias === "number" && Number.isInteger(dias) && dias >= 7 && dias <= 999 ? dias : null;
|
|
41064
|
+
if (d === null) return { error: "volumenes necesita dias (el per\xEDodo le\xEDdo, de 7 a 999)" };
|
|
41065
|
+
const porCodigo = {};
|
|
41066
|
+
for (const [i, e] of v.entries()) {
|
|
41067
|
+
const r = e ?? {};
|
|
41068
|
+
const codigo = normalizarCodigo(r.codigo);
|
|
41069
|
+
const cantidad2 = typeof r.cantidad === "number" ? r.cantidad : Number(r.cantidad);
|
|
41070
|
+
if (!codigo || !Number.isFinite(cantidad2) || cantidad2 < 0) return { error: `volumenes[${i}]: c\xF3digo y cantidad mayor o igual a 0` };
|
|
41071
|
+
porCodigo[codigo] = (porCodigo[codigo] ?? 0) + cantidad2;
|
|
41072
|
+
}
|
|
41073
|
+
return { porCodigo, dias: d };
|
|
41074
|
+
}
|
|
41075
|
+
var redondear2 = (n, dec = 2) => Math.round(n * 10 ** dec) / 10 ** dec;
|
|
41076
|
+
var periodoValido = (p) => typeof p === "string" && /^\d{4}-(0[1-9]|1[0-2])$/.test(p);
|
|
41077
|
+
var porcentajeValido = (n) => typeof n === "number" && Number.isFinite(n) && n > LIMITES_PORCENTAJE[0] && n <= LIMITES_PORCENTAJE[1];
|
|
41078
|
+
function leerFormula(v) {
|
|
41079
|
+
if (v === void 0 || v === null) return FORMULA_POR_DEFECTO;
|
|
41080
|
+
if (typeof v !== "object" || Array.isArray(v)) return { error: "la f\xF3rmula tiene que ser {componentes: [{serie, peso}]} o {fijo: porcentaje}" };
|
|
41081
|
+
const o = v;
|
|
41082
|
+
if ("fijo" in o) {
|
|
41083
|
+
const f = leerMonto(o.fijo);
|
|
41084
|
+
return f !== null && porcentajeValido(f) ? { fijo: redondear2(f) } : { error: `el porcentaje fijo tiene que estar entre ${LIMITES_PORCENTAJE[0]} y ${LIMITES_PORCENTAJE[1]}` };
|
|
41085
|
+
}
|
|
41086
|
+
if (!Array.isArray(o.componentes) || o.componentes.length === 0) return { error: "la f\xF3rmula necesita al menos un \xEDndice" };
|
|
41087
|
+
const componentes = [];
|
|
41088
|
+
for (const c of o.componentes) {
|
|
41089
|
+
const r = c ?? {};
|
|
41090
|
+
const serie2 = typeof r.serie === "string" ? r.serie : "";
|
|
41091
|
+
const peso = leerMonto(r.peso);
|
|
41092
|
+
if (!SERIES[serie2]) return { error: `\xEDndice desconocido: ${serie2 || "(vac\xEDo)"}; los que hay: ${Object.keys(SERIES).join(", ")}` };
|
|
41093
|
+
if (peso === null || peso <= 0) return { error: `el peso de ${serie2} tiene que ser mayor a 0` };
|
|
41094
|
+
if (componentes.some((x) => x.serie === serie2)) return { error: `${serie2} est\xE1 repetido en la f\xF3rmula` };
|
|
41095
|
+
componentes.push({ serie: serie2, peso });
|
|
41096
|
+
}
|
|
41097
|
+
const suma = componentes.reduce((a, c) => a + c.peso, 0);
|
|
41098
|
+
if (Math.abs(suma - 100) > 0.01) return { error: `los pesos tienen que sumar 100 (suman ${suma})` };
|
|
41099
|
+
return { componentes };
|
|
41100
|
+
}
|
|
41101
|
+
function leerIndices(v) {
|
|
41102
|
+
if (!Array.isArray(v)) return { error: "indices tiene que ser una lista [{serie, desde: {periodo, valor}, hasta: {periodo, valor}, fuente}]" };
|
|
41103
|
+
const indices = [];
|
|
41104
|
+
for (const [i, e] of v.entries()) {
|
|
41105
|
+
const r = e ?? {};
|
|
41106
|
+
const serie2 = typeof r.serie === "string" ? r.serie : "";
|
|
41107
|
+
if (!SERIES[serie2]) return { error: `indices[${i}]: serie desconocida ${serie2 || "(vac\xEDa)"}` };
|
|
41108
|
+
if (indices.some((x) => x.serie === serie2)) return { error: `indices[${i}]: ${serie2} est\xE1 repetida` };
|
|
41109
|
+
const d = r.desde ?? {};
|
|
41110
|
+
const h = r.hasta ?? {};
|
|
41111
|
+
const vd = leerMonto(d.valor);
|
|
41112
|
+
const vh = leerMonto(h.valor);
|
|
41113
|
+
if (!periodoValido(d.periodo) || !periodoValido(h.periodo) || d.periodo >= h.periodo) return { error: `indices[${i}] (${serie2}): desde y hasta son meses AAAA-MM, desde antes que hasta` };
|
|
41114
|
+
if (vd === null || vh === null || vd <= 0 || vh <= 0) return { error: `indices[${i}] (${serie2}): los valores del \xEDndice tienen que ser n\xFAmeros mayores a 0` };
|
|
41115
|
+
if (typeof r.fuente !== "string" || !r.fuente.trim()) return { error: `indices[${i}] (${serie2}): falta la fuente` };
|
|
41116
|
+
indices.push({ serie: serie2, desde: { periodo: d.periodo, valor: vd }, hasta: { periodo: h.periodo, valor: vh }, fuente: r.fuente.trim().slice(0, 200) });
|
|
41117
|
+
}
|
|
41118
|
+
return { indices };
|
|
41119
|
+
}
|
|
41120
|
+
function porcentajeSegunFormula(formula, indices, desde, mesActual) {
|
|
41121
|
+
if ("fijo" in formula) return { porcentaje: formula.fijo, detalle: { fijo: formula.fijo }, hasta: null };
|
|
41122
|
+
const detalle = [];
|
|
41123
|
+
let hasta = null;
|
|
41124
|
+
for (const c of formula.componentes) {
|
|
41125
|
+
const ind = indices.find((x) => x.serie === c.serie);
|
|
41126
|
+
if (!ind) return { error: `falta el \xEDndice ${c.serie} (${SERIES[c.serie].nombre}) que usa la f\xF3rmula de la instituci\xF3n` };
|
|
41127
|
+
if (ind.desde.periodo !== desde) return { error: `${c.serie}: el per\xEDodo empieza en ${ind.desde.periodo} y el \xFAltimo ajuste fue en ${desde}` };
|
|
41128
|
+
if (ind.hasta.periodo >= mesActual) return { error: `${c.serie}: ${ind.hasta.periodo} todav\xEDa no est\xE1 publicado` };
|
|
41129
|
+
if (hasta && ind.hasta.periodo !== hasta) return { error: "los \xEDndices no terminan en el mismo mes" };
|
|
41130
|
+
hasta = ind.hasta.periodo;
|
|
41131
|
+
detalle.push({ serie: c.serie, nombre: SERIES[c.serie].nombre, variacion: redondear2((ind.hasta.valor / ind.desde.valor - 1) * 100), peso: c.peso });
|
|
41132
|
+
}
|
|
41133
|
+
const porcentaje3 = redondear2(detalle.reduce((a, d) => a + d.variacion * d.peso / 100, 0));
|
|
41134
|
+
if (!porcentajeValido(porcentaje3)) return { error: `el porcentaje calculado (${porcentaje3} %) est\xE1 fuera de rango: revisar los valores de los \xEDndices` };
|
|
41135
|
+
return { porcentaje: porcentaje3, detalle, hasta };
|
|
41136
|
+
}
|
|
41137
|
+
function armarAjuste(actual, porcentaje3) {
|
|
41138
|
+
const lectura = leerActual(actual);
|
|
41139
|
+
if ("error" in lectura) return { ok: false, error: lectura.error };
|
|
41140
|
+
if (!porcentajeValido(porcentaje3)) return { ok: false, error: `el porcentaje tiene que estar entre ${LIMITES_PORCENTAJE[0]} y ${LIMITES_PORCENTAJE[1]}` };
|
|
41141
|
+
const items = [];
|
|
41142
|
+
for (const p of lectura.practicas) {
|
|
41143
|
+
const valorActual = redondear2(p.componentes.reduce((a, c) => a + c.monto, 0));
|
|
41144
|
+
if (valorActual <= 0) continue;
|
|
41145
|
+
const valorNuevo = redondear2(valorActual * (1 + porcentaje3 / 100));
|
|
41146
|
+
const componentes = repartir(p.componentes, valorNuevo);
|
|
41147
|
+
items.push({
|
|
41148
|
+
codigo: p.codigo,
|
|
41149
|
+
codigo_his: p.codigo_his,
|
|
41150
|
+
nombre: p.nombre,
|
|
41151
|
+
valor_a: null,
|
|
41152
|
+
valor_b: null,
|
|
41153
|
+
valor_actual: valorActual,
|
|
41154
|
+
valor_nuevo: valorNuevo,
|
|
41155
|
+
variacion: redondear2((valorNuevo / valorActual - 1) * 100, 1),
|
|
41156
|
+
estado: componentes ? "propuesta" : "revisar",
|
|
41157
|
+
causa: componentes ? null : "componentes_en_cero",
|
|
41158
|
+
coinciden: true,
|
|
41159
|
+
motivo: componentes ? null : "no se puede repartir entre sus componentes: cargar a mano",
|
|
41160
|
+
componentes_nuevos: componentes
|
|
41161
|
+
});
|
|
41162
|
+
}
|
|
41163
|
+
if (items.length === 0) return { ok: false, error: "el convenio no tiene pr\xE1cticas con valor" };
|
|
41164
|
+
return { ok: true, items, actual: lectura.practicas };
|
|
41165
|
+
}
|
|
41166
|
+
function resumenAjuste(items, excluir, porcentaje3, porcentajeCalculado, tope, volumenes = null) {
|
|
41167
|
+
const fuera = new Set(Array.isArray(excluir) ? excluir.map(normalizarCodigo) : []);
|
|
41168
|
+
const dentro = items.filter((i) => !fuera.has(i.codigo));
|
|
41169
|
+
const cargan = dentro.filter((i) => i.estado === "propuesta");
|
|
41170
|
+
return {
|
|
41171
|
+
porcentaje: porcentaje3,
|
|
41172
|
+
porcentaje_calculado: porcentajeCalculado,
|
|
41173
|
+
practicas: cargan.length,
|
|
41174
|
+
excluidas: items.length - dentro.length,
|
|
41175
|
+
revisar: dentro.length - cargan.length,
|
|
41176
|
+
total_actual: redondear2(cargan.reduce((a, i) => a + (i.valor_actual ?? 0), 0)),
|
|
41177
|
+
total_nuevo: redondear2(cargan.reduce((a, i) => a + (i.valor_nuevo ?? 0), 0)),
|
|
41178
|
+
tope,
|
|
41179
|
+
supera_tope: Math.abs(porcentaje3) > tope,
|
|
41180
|
+
impacto_mensual: volumenes ? redondear2(cargan.reduce((a, i) => a + ((i.valor_nuevo ?? 0) - (i.valor_actual ?? 0)) * (volumenes.porCodigo[i.codigo] ?? 0), 0) * 30 / volumenes.dias, 0) : null
|
|
41181
|
+
};
|
|
41182
|
+
}
|
|
41183
|
+
function textoIndices(detalle, desde, hasta) {
|
|
41184
|
+
if (!Array.isArray(detalle)) return "lo acordado en el convenio";
|
|
41185
|
+
const mes = (p) => (/* @__PURE__ */ new Date(`${p}-15T12:00:00Z`)).toLocaleDateString("es-AR", { month: "long", year: "numeric", timeZone: "UTC" });
|
|
41186
|
+
const pct = (n) => `${n.toLocaleString("es-AR", { maximumFractionDigits: 2 })} %`;
|
|
41187
|
+
const partes = detalle.map((d) => `${d.nombre} ${pct(d.variacion)}`);
|
|
41188
|
+
const pesos4 = detalle.length > 1 ? ` (ponderados ${detalle.map((d) => `${d.peso} %`).join(" y ")})` : "";
|
|
41189
|
+
const periodo = desde && hasta ? `, de ${mes(desde)} a ${mes(hasta)}` : "";
|
|
41190
|
+
return `${partes.join(" y ")}${pesos4}${periodo}`;
|
|
41191
|
+
}
|
|
41192
|
+
var NOTA_POR_DEFECTO = {
|
|
41193
|
+
asunto: "Actualizaci\xF3n de aranceles \xB7 {convenio} \xB7 {institucion}",
|
|
41194
|
+
texto: "Estimados de {financiador}:\n\nDe acuerdo con la variaci\xF3n de {indices}, proponemos actualizar los aranceles del convenio {convenio} en un {porcentaje}, con vigencia desde el {vigencia}.\n\nAdjuntamos el detalle de las {practicas} pr\xE1cticas con sus valores actuales y los propuestos.\n\nQuedamos a la espera de su conformidad.\n\nSaludos cordiales,\n{firma}\n{institucion}"
|
|
41195
|
+
};
|
|
41196
|
+
function armarNota(plantilla, d) {
|
|
41197
|
+
const p = plantilla && typeof plantilla === "object" ? plantilla : {};
|
|
41198
|
+
const [a, m, dia5] = d.vigencia.split("-");
|
|
41199
|
+
const valores = {
|
|
41200
|
+
institucion: d.institucion,
|
|
41201
|
+
financiador: d.financiador,
|
|
41202
|
+
convenio: d.convenio,
|
|
41203
|
+
porcentaje: `${d.porcentaje.toLocaleString("es-AR", { maximumFractionDigits: 2 })} %`,
|
|
41204
|
+
indices: d.indices,
|
|
41205
|
+
vigencia: dia5 ? `${dia5}/${m}/${a}` : d.vigencia,
|
|
41206
|
+
firma: d.firma,
|
|
41207
|
+
practicas: String(d.practicas)
|
|
41208
|
+
};
|
|
41209
|
+
const llenar = (t6) => t6.replace(/\{(\w+)\}/g, (x, k) => valores[k] ?? x);
|
|
41210
|
+
const asunto = typeof p.asunto === "string" && p.asunto.trim() ? p.asunto : NOTA_POR_DEFECTO.asunto;
|
|
41211
|
+
const texto10 = typeof p.texto === "string" && p.texto.trim() ? p.texto : NOTA_POR_DEFECTO.texto;
|
|
41212
|
+
return { asunto: llenar(asunto).slice(0, 200), texto: llenar(texto10).slice(0, 5e3) };
|
|
41213
|
+
}
|
|
41214
|
+
|
|
41035
41215
|
// src/internacion.ts
|
|
41036
41216
|
var HORA_CORTE_POR_DEFECTO = "13:00";
|
|
41037
41217
|
var DIAS_SIN_ACTIVIDAD_POR_DEFECTO = 3;
|
|
@@ -41157,7 +41337,7 @@ function estadoInternacion(e) {
|
|
|
41157
41337
|
}
|
|
41158
41338
|
|
|
41159
41339
|
// src/honorarios.ts
|
|
41160
|
-
var
|
|
41340
|
+
var redondear3 = (n) => Math.round(n * 100) / 100;
|
|
41161
41341
|
var pesos2 = (n) => `$ ${n.toLocaleString("es-AR", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
41162
41342
|
var fecha3 = (iso5) => `${iso5.slice(8, 10)}/${iso5.slice(5, 7)}/${iso5.slice(0, 4)}`;
|
|
41163
41343
|
function texto2(v) {
|
|
@@ -41192,13 +41372,13 @@ function leerRegla(v) {
|
|
|
41192
41372
|
if (!rid) return null;
|
|
41193
41373
|
const prof = num3(r.prof);
|
|
41194
41374
|
const inst = num3(r.inst);
|
|
41195
|
-
return { id: rid, prof: prof === null ? null :
|
|
41375
|
+
return { id: rid, prof: prof === null ? null : redondear3(prof), inst: inst === null ? null : redondear3(inst) };
|
|
41196
41376
|
}
|
|
41197
41377
|
function honorarioDe(input2) {
|
|
41198
41378
|
const { regla, monto, cantidad: cantidad2 } = input2;
|
|
41199
41379
|
if (!regla) return 0;
|
|
41200
|
-
if (regla.prof !== null) return
|
|
41201
|
-
return
|
|
41380
|
+
if (regla.prof !== null) return redondear3(regla.prof * cantidad2);
|
|
41381
|
+
return redondear3(monto * cantidad2 * ((regla.inst ?? 0) / 100));
|
|
41202
41382
|
}
|
|
41203
41383
|
function motivoCero(regla, honorario, delHis = false) {
|
|
41204
41384
|
if (honorario > 0) return null;
|
|
@@ -41208,7 +41388,7 @@ function motivoCero(regla, honorario, delHis = false) {
|
|
|
41208
41388
|
function honorarioLeido(r, regla, monto, cantidad2) {
|
|
41209
41389
|
if (r.honorario === void 0 || r.honorario === null || r.honorario === "") return { honorario: honorarioDe({ regla, monto, cantidad: cantidad2 }), delHis: false };
|
|
41210
41390
|
const h = num3(r.honorario);
|
|
41211
|
-
return h === null || h < 0 ? null : { honorario:
|
|
41391
|
+
return h === null || h < 0 ? null : { honorario: redondear3(h), delHis: true };
|
|
41212
41392
|
}
|
|
41213
41393
|
function leerPrestaciones(v) {
|
|
41214
41394
|
if (!Array.isArray(v)) return { error: "prestaciones tiene que ser la lista que devuelve leer-honorarios.mjs" };
|
|
@@ -41225,7 +41405,7 @@ function leerPrestaciones(v) {
|
|
|
41225
41405
|
if (r.regla !== null && r.regla !== void 0 && !leerRegla(r.regla)) return { error: `prestaciones[${i}].regla: tiene que ser {id, prof, inst} o null` };
|
|
41226
41406
|
const h = honorarioLeido(r, leerRegla(r.regla), monto, cantidad2 > 0 ? cantidad2 : 1);
|
|
41227
41407
|
if (!h) return { error: `prestaciones[${i}].honorario: tiene que ser un n\xFAmero mayor o igual a 0` };
|
|
41228
|
-
out.push({ item_id: item, bono_id: bono, fecha: f, prestacion: texto2(r.prestacion)?.slice(0, 160) ?? "Prestaci\xF3n", obra_social: texto2(r.obra_social)?.slice(0, 120) ?? null, monto:
|
|
41408
|
+
out.push({ item_id: item, bono_id: bono, fecha: f, prestacion: texto2(r.prestacion)?.slice(0, 160) ?? "Prestaci\xF3n", obra_social: texto2(r.obra_social)?.slice(0, 120) ?? null, monto: redondear3(monto), cantidad: cantidad2 > 0 ? cantidad2 : 1, a_liquidar: verdad2(r.a_liquidar), liquidada: verdad2(r.liquidada), regla: leerRegla(r.regla), honorario_his: h.delHis ? h.honorario : null });
|
|
41229
41409
|
}
|
|
41230
41410
|
return { prestaciones: out };
|
|
41231
41411
|
}
|
|
@@ -41244,7 +41424,7 @@ function prestacionesEnCero(ps) {
|
|
|
41244
41424
|
prestaciones: items.length,
|
|
41245
41425
|
sin_regla: items.filter((p) => p.motivo === "sin_regla").length,
|
|
41246
41426
|
regla_sin_valor: items.filter((p) => p.motivo === "regla_sin_valor").length,
|
|
41247
|
-
monto:
|
|
41427
|
+
monto: redondear3(items.reduce((a, p) => a + p.monto * p.cantidad, 0)),
|
|
41248
41428
|
obras_sociales: [...new Set(items.map((p) => p.obra_social).filter((x) => Boolean(x)))].sort(),
|
|
41249
41429
|
desde: fechas[0] ?? null,
|
|
41250
41430
|
hasta: fechas[fechas.length - 1] ?? null
|
|
@@ -41270,7 +41450,7 @@ function leerDebitos(v) {
|
|
|
41270
41450
|
const hl = honorarioLeido(r, regla, montoItem, cantidad2);
|
|
41271
41451
|
if (!hl) return { error: `debitos[${i}].honorario: tiene que ser un n\xFAmero mayor o igual a 0` };
|
|
41272
41452
|
const hon = liquidada ? hl.honorario : null;
|
|
41273
|
-
const imp = liquidada ?
|
|
41453
|
+
const imp = liquidada ? redondear3(montoItem * cantidad2) : null;
|
|
41274
41454
|
out.push({
|
|
41275
41455
|
item_id: item,
|
|
41276
41456
|
bono_id: bono,
|
|
@@ -41278,13 +41458,13 @@ function leerDebitos(v) {
|
|
|
41278
41458
|
fecha_prestacion: dia(r.fecha_prestacion),
|
|
41279
41459
|
obra_social: texto2(r.obra_social)?.slice(0, 120) ?? null,
|
|
41280
41460
|
nota_credito: { id: ncId, numero: texto2(nc?.numero), fecha: dia(nc?.fecha), motivo: texto2(nc?.motivo)?.slice(0, 300) ?? null },
|
|
41281
|
-
monto_debitado:
|
|
41461
|
+
monto_debitado: redondear3(Math.abs(monto)),
|
|
41282
41462
|
liquidada,
|
|
41283
|
-
monto:
|
|
41463
|
+
monto: redondear3(montoItem),
|
|
41284
41464
|
cantidad: cantidad2 > 0 ? cantidad2 : 1,
|
|
41285
41465
|
regla,
|
|
41286
41466
|
liquidado: hon !== null && imp !== null ? { honorario: hon, importe: imp } : null,
|
|
41287
|
-
ya_trasladado:
|
|
41467
|
+
ya_trasladado: redondear3(Math.abs(num3(r.ya_trasladado) ?? 0))
|
|
41288
41468
|
});
|
|
41289
41469
|
}
|
|
41290
41470
|
return { debitos: out };
|
|
@@ -41293,12 +41473,12 @@ function evaluarDebito(d) {
|
|
|
41293
41473
|
if (!d.liquidado) return { ...d, estado: "sin_liquidar", a_descontar: 0 };
|
|
41294
41474
|
if (d.liquidado.importe <= 0 || d.liquidado.honorario <= 0 || d.monto_debitado <= 0) return { ...d, estado: "sin_monto", a_descontar: 0 };
|
|
41295
41475
|
const proporcional = Math.min(d.liquidado.honorario, d.liquidado.honorario * d.monto_debitado / d.liquidado.importe);
|
|
41296
|
-
const falta =
|
|
41476
|
+
const falta = redondear3(proporcional - d.ya_trasladado);
|
|
41297
41477
|
return falta >= 0.01 ? { ...d, estado: "a_trasladar", a_descontar: falta } : { ...d, estado: "ya_trasladado", a_descontar: 0 };
|
|
41298
41478
|
}
|
|
41299
41479
|
function resumenDebitos(ds) {
|
|
41300
41480
|
const a = ds.filter((d) => d.estado === "a_trasladar");
|
|
41301
|
-
return { a_trasladar: a.length, total:
|
|
41481
|
+
return { a_trasladar: a.length, total: redondear3(a.reduce((s, d) => s + d.a_descontar, 0)), sin_liquidar: ds.filter((d) => d.estado === "sin_liquidar").length, ya_trasladados: ds.filter((d) => d.estado === "ya_trasladado").length };
|
|
41302
41482
|
}
|
|
41303
41483
|
function leerLineasLiquidacion(v) {
|
|
41304
41484
|
if (!Array.isArray(v) || v.length === 0) return { error: "lineas tiene que ser la lista (no vac\xEDa) de bonos de la liquidaci\xF3n, como la devuelve leer-honorarios.mjs" };
|
|
@@ -41323,7 +41503,7 @@ function leerLineasLiquidacion(v) {
|
|
|
41323
41503
|
honorario += h;
|
|
41324
41504
|
if (h <= 0) enCero += 1;
|
|
41325
41505
|
}
|
|
41326
|
-
out.push({ bono_id: bono, fecha: dia(r.fecha), obra_social: texto2(r.obra_social)?.slice(0, 120) ?? null, importe:
|
|
41506
|
+
out.push({ bono_id: bono, fecha: dia(r.fecha), obra_social: texto2(r.obra_social)?.slice(0, 120) ?? null, importe: redondear3(importe), honorario: redondear3(honorario), en_cero: enCero });
|
|
41327
41507
|
}
|
|
41328
41508
|
return { lineas: out };
|
|
41329
41509
|
}
|
|
@@ -41333,14 +41513,14 @@ function resumirLiquidacion(lineas) {
|
|
|
41333
41513
|
const k = l.obra_social ?? "Particular / sin obra social";
|
|
41334
41514
|
const e = por.get(k) ?? { bonos: 0, honorario: 0 };
|
|
41335
41515
|
e.bonos += 1;
|
|
41336
|
-
e.honorario =
|
|
41516
|
+
e.honorario = redondear3(e.honorario + l.honorario);
|
|
41337
41517
|
por.set(k, e);
|
|
41338
41518
|
}
|
|
41339
41519
|
const fechas = lineas.map((l) => l.fecha).filter((x) => Boolean(x)).sort();
|
|
41340
41520
|
return {
|
|
41341
41521
|
bonos: lineas.length,
|
|
41342
|
-
honorario:
|
|
41343
|
-
importe:
|
|
41522
|
+
honorario: redondear3(lineas.reduce((a, l) => a + l.honorario, 0)),
|
|
41523
|
+
importe: redondear3(lineas.reduce((a, l) => a + l.importe, 0)),
|
|
41344
41524
|
en_cero: lineas.reduce((a, l) => a + l.en_cero, 0),
|
|
41345
41525
|
por_obra_social: [...por.entries()].map(([obra_social, e]) => ({ obra_social, ...e })).sort((a, b) => b.honorario - a.honorario),
|
|
41346
41526
|
desde: fechas[0] ?? null,
|
|
@@ -41376,7 +41556,7 @@ var ATRASO_EXCEPCION = 365;
|
|
|
41376
41556
|
var MAX_RECLAMOS_POR_DEFECTO = 2;
|
|
41377
41557
|
var DIAS_ENTRE_RECLAMOS_POR_DEFECTO = 15;
|
|
41378
41558
|
var GRACIA_COMPROMISO = 7;
|
|
41379
|
-
var
|
|
41559
|
+
var redondear4 = (n) => Math.round(n * 100) / 100;
|
|
41380
41560
|
var pesos3 = (n) => `$ ${n.toLocaleString("es-AR", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
41381
41561
|
var fecha4 = (iso5) => `${iso5.slice(8, 10)}/${iso5.slice(5, 7)}/${iso5.slice(0, 4)}`;
|
|
41382
41562
|
var normalizar = (s) => s.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/\s+/g, " ").trim();
|
|
@@ -41416,7 +41596,7 @@ function leerFacturas(v) {
|
|
|
41416
41596
|
vistas.add(id3);
|
|
41417
41597
|
if (!emision) return { error: `factura ${id3}: fecha_emision tiene que ser AAAA-MM-DD` };
|
|
41418
41598
|
if (monto === null || saldo === null) return { error: `factura ${id3}: monto_pesos y saldo_pesos tienen que ser n\xFAmeros` };
|
|
41419
|
-
out.push({ factura_id: id3, numero_comprobante: texto3(r.numero_comprobante) ?? id3, fecha_emision: emision, periodo_desde: dia2(r.periodo_desde), periodo_hasta: dia2(r.periodo_hasta), monto_pesos:
|
|
41599
|
+
out.push({ factura_id: id3, numero_comprobante: texto3(r.numero_comprobante) ?? id3, fecha_emision: emision, periodo_desde: dia2(r.periodo_desde), periodo_hasta: dia2(r.periodo_hasta), monto_pesos: redondear4(monto), saldo_pesos: redondear4(saldo), anulada: r.anulada === true || r.anulada === 1 || r.anulada === "1" });
|
|
41420
41600
|
}
|
|
41421
41601
|
return { facturas: out };
|
|
41422
41602
|
}
|
|
@@ -41448,7 +41628,7 @@ function evaluarFacturas(input2) {
|
|
|
41448
41628
|
};
|
|
41449
41629
|
});
|
|
41450
41630
|
const reclamadas = items.filter((i) => i.estado === "reclamada").sort((a, b) => b.dias_atraso - a.dias_atraso);
|
|
41451
|
-
const total =
|
|
41631
|
+
const total = redondear4(reclamadas.reduce((s, i) => s + i.saldo_pesos, 0));
|
|
41452
41632
|
return {
|
|
41453
41633
|
items: [...reclamadas, ...items.filter((i) => i.estado !== "reclamada")],
|
|
41454
41634
|
resumen: {
|
|
@@ -41467,7 +41647,7 @@ function evaluarFacturas(input2) {
|
|
|
41467
41647
|
}
|
|
41468
41648
|
function redactarReclamo(input2) {
|
|
41469
41649
|
const reclamadas = input2.items.filter((i) => i.estado === "reclamada" && i.cobro !== "cobrada");
|
|
41470
|
-
const total =
|
|
41650
|
+
const total = redondear4(reclamadas.reduce((s, i) => s + i.saldo_pesos, 0));
|
|
41471
41651
|
const asunto = `${input2.numero > 1 ? "Segundo reclamo" : "Reclamo"} de pago \xB7 ${input2.institucion} \xB7 ${reclamadas.length} ${reclamadas.length === 1 ? "factura" : "facturas"} \xB7 ${pesos3(total)}`;
|
|
41472
41652
|
const lineas = reclamadas.map((i) => `- Factura ${i.numero_comprobante} del ${fecha4(i.fecha_emision)}${i.periodo ? ` (per\xEDodo ${i.periodo})` : ""}: saldo ${pesos3(i.saldo_pesos)}, vencida hace ${i.dias_atraso} d\xEDas.`);
|
|
41473
41653
|
const cuerpo = [
|
|
@@ -41493,7 +41673,7 @@ function leerSaldos(v) {
|
|
|
41493
41673
|
const id3 = texto3(r.factura_id);
|
|
41494
41674
|
const saldo = num4(r.saldo_pesos);
|
|
41495
41675
|
if (!id3 || saldo === null) return { error: `leidos[${i}]: hace falta factura_id y saldo_pesos` };
|
|
41496
|
-
saldos.set(id3, { saldo:
|
|
41676
|
+
saldos.set(id3, { saldo: redondear4(saldo), notasCredito: redondear4(num4(r.notas_credito_pesos) ?? 0), recibos: redondear4(num4(r.recibos_aplicados_pesos) ?? 0) });
|
|
41497
41677
|
}
|
|
41498
41678
|
return { saldos };
|
|
41499
41679
|
}
|
|
@@ -41509,14 +41689,14 @@ function compararSaldos(items, resumen, leidos, notasCreditoAntes = {}) {
|
|
|
41509
41689
|
return i;
|
|
41510
41690
|
}
|
|
41511
41691
|
const saldo = Math.max(0, s.saldo);
|
|
41512
|
-
const nc = Math.max(0,
|
|
41692
|
+
const nc = Math.max(0, redondear4(s.notasCredito - (notasCreditoAntes[i.factura_id] ?? 0)));
|
|
41513
41693
|
const cobro = saldo <= SALDO_MINIMO ? "cobrada" : saldo < i.saldo_inicial - SALDO_MINIMO ? "parcial" : "pendiente";
|
|
41514
41694
|
return { ...i, saldo_pesos: saldo, cobro, notas_credito_nuevas: nc };
|
|
41515
41695
|
});
|
|
41516
41696
|
const reclamadas = nuevos.filter((i) => i.estado === "reclamada");
|
|
41517
|
-
const pendiente =
|
|
41518
|
-
const debitos =
|
|
41519
|
-
const cobrado =
|
|
41697
|
+
const pendiente = redondear4(reclamadas.reduce((s, i) => s + (i.cobro === "cobrada" ? 0 : i.saldo_pesos), 0));
|
|
41698
|
+
const debitos = redondear4(reclamadas.reduce((s, i) => s + i.notas_credito_nuevas, 0));
|
|
41699
|
+
const cobrado = redondear4(Math.max(0, resumen.total_reclamado - pendiente - debitos));
|
|
41520
41700
|
return { items: nuevos, resumen: { ...resumen, cobrado, pendiente, posibles_debitos: debitos }, todoCobrado: reclamadas.length > 0 && reclamadas.every((i) => i.cobro === "cobrada") && faltan.length === 0, faltan };
|
|
41521
41701
|
}
|
|
41522
41702
|
var RESPUESTAS_FINANCIADOR = ["paga", "observa", "no_responde"];
|
|
@@ -42614,6 +42794,213 @@ var circularAranceles = {
|
|
|
42614
42794
|
}
|
|
42615
42795
|
]
|
|
42616
42796
|
};
|
|
42797
|
+
var RESPUESTAS_AJUSTE = ["aprobado", "aprobado_otro", "rechazado"];
|
|
42798
|
+
var CANALES_NOTA = ["correo", "portal_del_financiador", "nota", "otro"];
|
|
42799
|
+
function calcularAjuste(caso, datos, contexto) {
|
|
42800
|
+
const formula = contexto.formulaAjuste ?? FORMULA_POR_DEFECTO;
|
|
42801
|
+
if ("error" in formula) return { ok: false, error: `la f\xF3rmula de la instituci\xF3n est\xE1 mal configurada: ${formula.error}` };
|
|
42802
|
+
const desde = texto4(caso.datos.ultimo_ajuste);
|
|
42803
|
+
if (!desde || !periodoValido(desde)) return { ok: false, error: "el caso no tiene el mes del \xFAltimo ajuste (ultimo_ajuste: AAAA-MM)" };
|
|
42804
|
+
let indices = [];
|
|
42805
|
+
if (!("fijo" in formula)) {
|
|
42806
|
+
const l = leerIndices(datos.indices);
|
|
42807
|
+
if ("error" in l) return { ok: false, error: l.error };
|
|
42808
|
+
indices = l.indices;
|
|
42809
|
+
}
|
|
42810
|
+
const mesActual = (contexto.hoy ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)).slice(0, 7);
|
|
42811
|
+
const p = porcentajeSegunFormula(formula, indices, desde, mesActual);
|
|
42812
|
+
if ("error" in p) return { ok: false, error: p.error };
|
|
42813
|
+
const a = armarAjuste(datos.actual, p.porcentaje);
|
|
42814
|
+
if (!a.ok) return { ok: false, error: a.error };
|
|
42815
|
+
return { ok: true, porcentaje: p.porcentaje, detalle: p.detalle, hasta: p.hasta, items: a.items };
|
|
42816
|
+
}
|
|
42817
|
+
function resumenAjusteDe(caso) {
|
|
42818
|
+
const r = caso.datos.resumen;
|
|
42819
|
+
return r && typeof r === "object" ? r : null;
|
|
42820
|
+
}
|
|
42821
|
+
function validarPorcentajeHumano(v, regla) {
|
|
42822
|
+
const n = num5(v);
|
|
42823
|
+
return n !== null && porcentajeValido(n) ? ok(regla, `${n} %`) : falla(regla, "porcentaje: un n\xFAmero entre -50 y 100");
|
|
42824
|
+
}
|
|
42825
|
+
var ajusteConvenio = {
|
|
42826
|
+
clave: "ajuste_convenio",
|
|
42827
|
+
nombre: "Ajuste de convenios por \xEDndice",
|
|
42828
|
+
puesto: "Convenios y facturaci\xF3n",
|
|
42829
|
+
descripcion: "Calcula el aumento de un convenio con los \xEDndices publicados (IPC de INDEC) y la f\xF3rmula de la instituci\xF3n, arma los valores nuevos de cada pr\xE1ctica y la nota al financiador; la direcci\xF3n revisa y aprueba, se registra la respuesta del financiador, se carga con el archivo que genera el plugin y el puesto comprueba releyendo cada valor.",
|
|
42830
|
+
inicial: "abierto",
|
|
42831
|
+
estados: ["abierto", "calculado", "pendiente_aprobacion", "por_enviar", "enviado", "acordado", "aplicado", "verificado", "con_diferencias", "rechazado", "rechazado_financiador", "cancelado"],
|
|
42832
|
+
finalesOk: ["verificado"],
|
|
42833
|
+
finalesNoOk: ["rechazado", "rechazado_financiador", "cancelado"],
|
|
42834
|
+
minutosAbsorbidos: 40,
|
|
42835
|
+
// Buscar los índices, calcular, armar la nota y comprobar cada valor (tope: una jornada).
|
|
42836
|
+
minutosSegun: (caso) => Math.min(480, 40 + 2 * cantidad(caso.datos.items)),
|
|
42837
|
+
transiciones: [
|
|
42838
|
+
{
|
|
42839
|
+
via: "calcular",
|
|
42840
|
+
de: ["abierto", "calculado"],
|
|
42841
|
+
a: "calculado",
|
|
42842
|
+
actor: ["agente", "humano"],
|
|
42843
|
+
nivelRequerido: "observa",
|
|
42844
|
+
descripcion: "Informar los valores publicados de los \xEDndices (indices: [{serie, desde: {periodo, valor}, hasta: {periodo, valor}, fuente}], desde el mes del \xFAltimo ajuste hasta el \xFAltimo publicado) y lo que tiene hoy el convenio en el HIS (actual). El plugin calcula el porcentaje con la f\xF3rmula de la instituci\xF3n y los valores nuevos.",
|
|
42845
|
+
validar: (caso, datos, contexto) => {
|
|
42846
|
+
const c = calcularAjuste(caso, datos, contexto);
|
|
42847
|
+
const fijo = contexto.formulaAjuste && "fijo" in contexto.formulaAjuste;
|
|
42848
|
+
return [
|
|
42849
|
+
texto4(datos.lectura_id) ? ok("lectura_registrada") : falla("lectura_registrada", "calcular requiere lectura_id: la lectura del convenio en el HIS registrada con ops_registrar_accion"),
|
|
42850
|
+
fijo || texto4(datos.lectura_indices_id) ? ok("indices_registrados") : falla("indices_registrados", "calcular requiere lectura_indices_id: la consulta de los \xEDndices (conector api) registrada con ops_registrar_accion"),
|
|
42851
|
+
c.ok ? ok("calculo_ajuste", `${c.porcentaje} % sobre ${c.items.length} pr\xE1cticas`) : falla("calculo_ajuste", c.error),
|
|
42852
|
+
...(() => {
|
|
42853
|
+
const v = leerVolumenes(datos.volumenes, datos.dias_volumen);
|
|
42854
|
+
return v && "error" in v ? [falla("volumenes_validos", v.error)] : [ok("volumenes_validos", v ? "con lo facturado" : "sin vol\xFAmenes: no se estima el impacto en pesos")];
|
|
42855
|
+
})()
|
|
42856
|
+
];
|
|
42857
|
+
}
|
|
42858
|
+
},
|
|
42859
|
+
{
|
|
42860
|
+
via: "proponer",
|
|
42861
|
+
de: ["calculado"],
|
|
42862
|
+
a: "pendiente_aprobacion",
|
|
42863
|
+
actor: ["agente", "humano"],
|
|
42864
|
+
nivelRequerido: "propone",
|
|
42865
|
+
descripcion: "Pedir la aprobaci\xF3n de la direcci\xF3n con el porcentaje y los valores que calcul\xF3 el plugin. Un ajuste de convenio siempre lo aprueba una persona.",
|
|
42866
|
+
validar: (caso) => {
|
|
42867
|
+
const r = resumenAjusteDe(caso);
|
|
42868
|
+
return [r && r.practicas > 0 ? ok("hay_que_cargar", `${r.practicas} pr\xE1cticas`) : falla("hay_que_cargar", "no hay ninguna pr\xE1ctica para ajustar: revisar la lectura del convenio o cancelar")];
|
|
42869
|
+
}
|
|
42870
|
+
},
|
|
42871
|
+
{
|
|
42872
|
+
via: "ajustar",
|
|
42873
|
+
de: ["pendiente_aprobacion"],
|
|
42874
|
+
a: "pendiente_aprobacion",
|
|
42875
|
+
actor: ["humano"],
|
|
42876
|
+
descripcion: "La direcci\xF3n cambia el porcentaje (porcentaje, con motivo) o deja pr\xE1cticas afuera (excluir: [c\xF3digos]); el plugin recalcula los valores.",
|
|
42877
|
+
validar: (caso, datos) => {
|
|
42878
|
+
const out = [];
|
|
42879
|
+
if (datos.porcentaje === void 0 && datos.excluir === void 0) return [falla("cambio_presente", "ajustar necesita porcentaje o excluir")];
|
|
42880
|
+
if (datos.porcentaje !== void 0) {
|
|
42881
|
+
out.push(validarPorcentajeHumano(datos.porcentaje, "porcentaje_valido"));
|
|
42882
|
+
out.push(texto4(datos.motivo) ? ok("motivo_presente") : falla("motivo_presente", "cambiar el porcentaje calculado requiere motivo"));
|
|
42883
|
+
}
|
|
42884
|
+
if (datos.excluir !== void 0) {
|
|
42885
|
+
if (!Array.isArray(datos.excluir)) out.push(falla("excluir_valido", "excluir tiene que ser una lista de c\xF3digos"));
|
|
42886
|
+
else {
|
|
42887
|
+
const items = leerItems(caso.datos.items);
|
|
42888
|
+
const codigos = new Set(items.map((i) => i.codigo));
|
|
42889
|
+
const ajenos = datos.excluir.map(normalizarCodigo).filter((c) => !codigos.has(c));
|
|
42890
|
+
out.push(ajenos.length === 0 ? ok("excluir_valido") : falla("excluir_valido", `c\xF3digos que no est\xE1n en el convenio: ${ajenos.join(", ")}`));
|
|
42891
|
+
out.push(itemsACargar(items, datos.excluir).length > 0 ? ok("hay_que_cargar") : falla("hay_que_cargar", "excluyendo esas no queda nada para ajustar: rechazar el ajuste"));
|
|
42892
|
+
}
|
|
42893
|
+
}
|
|
42894
|
+
return out;
|
|
42895
|
+
}
|
|
42896
|
+
},
|
|
42897
|
+
{
|
|
42898
|
+
via: "aprobar",
|
|
42899
|
+
de: ["pendiente_aprobacion"],
|
|
42900
|
+
a: "por_enviar",
|
|
42901
|
+
actor: ["humano"],
|
|
42902
|
+
descripcion: "La direcci\xF3n aprueba el ajuste; el plugin arma la nota al financiador. Si el porcentaje supera el tope de la instituci\xF3n hay que confirmarlo (confirmo_tope: true).",
|
|
42903
|
+
validar: (caso, datos) => {
|
|
42904
|
+
const r = resumenAjusteDe(caso);
|
|
42905
|
+
return [
|
|
42906
|
+
r && r.practicas > 0 ? ok("hay_que_cargar", `${r.practicas} pr\xE1cticas`) : falla("hay_que_cargar", "no hay ninguna pr\xE1ctica para ajustar"),
|
|
42907
|
+
!r?.supera_tope || datos.confirmo_tope === true ? ok("tope_confirmado", r ? `${r.porcentaje} % (tope ${r.tope} %)` : void 0) : falla("tope_confirmado", `el ${r.porcentaje} % supera el tope de ${r.tope} % de la instituci\xF3n: confirmarlo de forma expresa`)
|
|
42908
|
+
];
|
|
42909
|
+
}
|
|
42910
|
+
},
|
|
42911
|
+
{
|
|
42912
|
+
via: "rechazar",
|
|
42913
|
+
de: ["pendiente_aprobacion"],
|
|
42914
|
+
a: "rechazado",
|
|
42915
|
+
actor: ["humano"],
|
|
42916
|
+
descripcion: "La direcci\xF3n no hace el ajuste; con motivo.",
|
|
42917
|
+
validar: (_caso, datos) => [texto4(datos.motivo) ? ok("motivo_presente") : falla("motivo_presente", "el rechazo requiere motivo")]
|
|
42918
|
+
},
|
|
42919
|
+
{
|
|
42920
|
+
via: "enviar",
|
|
42921
|
+
de: ["por_enviar"],
|
|
42922
|
+
a: "enviado",
|
|
42923
|
+
actor: ["humano", "agente"],
|
|
42924
|
+
nivelRequerido: "ejecuta",
|
|
42925
|
+
descripcion: "Registrar que la nota se mand\xF3 al financiador: por d\xF3nde (canal) y a qui\xE9n (destinatario).",
|
|
42926
|
+
validar: (_caso, datos) => [
|
|
42927
|
+
CANALES_NOTA.includes(datos.canal) ? ok("canal_valido") : falla("canal_valido", `canal: ${CANALES_NOTA.join(", ")}`),
|
|
42928
|
+
texto4(datos.destinatario) ? ok("destinatario_presente") : falla("destinatario_presente", "destinatario (correo, \xE1rea o persona del financiador) es obligatorio")
|
|
42929
|
+
]
|
|
42930
|
+
},
|
|
42931
|
+
{
|
|
42932
|
+
via: "responder",
|
|
42933
|
+
de: ["enviado"],
|
|
42934
|
+
a: (_caso, datos) => datos.respuesta === "rechazado" ? "rechazado_financiador" : "acordado",
|
|
42935
|
+
actor: ["humano"],
|
|
42936
|
+
descripcion: "Registrar la respuesta del financiador: aprobado, aprobado con otro porcentaje (porcentaje) o rechazado (motivo).",
|
|
42937
|
+
validar: (_caso, datos) => {
|
|
42938
|
+
const r = datos.respuesta;
|
|
42939
|
+
if (!RESPUESTAS_AJUSTE.includes(r)) return [falla("respuesta_valida", `respuesta: ${RESPUESTAS_AJUSTE.join(", ")}`)];
|
|
42940
|
+
if (r === "aprobado_otro") return [ok("respuesta_valida"), validarPorcentajeHumano(datos.porcentaje, "porcentaje_valido")];
|
|
42941
|
+
if (r === "rechazado") return [ok("respuesta_valida"), texto4(datos.motivo) ? ok("motivo_presente") : falla("motivo_presente", "anotar qu\xE9 respondi\xF3 el financiador")];
|
|
42942
|
+
return [ok("respuesta_valida")];
|
|
42943
|
+
}
|
|
42944
|
+
},
|
|
42945
|
+
{
|
|
42946
|
+
via: "aplicar",
|
|
42947
|
+
de: ["acordado"],
|
|
42948
|
+
a: "aplicado",
|
|
42949
|
+
actor: ["agente", "humano"],
|
|
42950
|
+
nivelRequerido: "ejecuta",
|
|
42951
|
+
cargaManual: true,
|
|
42952
|
+
descripcion: "Cargar en el HIS el archivo que gener\xF3 el plugin (Actualizaci\xF3n con archivo) y registrarlo, con el hash del archivo cargado.",
|
|
42953
|
+
validar: (caso, datos) => {
|
|
42954
|
+
const esperado = texto4(caso.datos.archivo_csv?.hash);
|
|
42955
|
+
const informado = texto4(datos.archivo_hash);
|
|
42956
|
+
return [
|
|
42957
|
+
texto4(datos.accion_id) ? ok("accion_registrada") : falla("accion_registrada", "aplicar requiere accion_id de la carga registrada"),
|
|
42958
|
+
esperado && informado === esperado ? ok("archivo_aprobado") : falla("archivo_aprobado", esperado ? "el archivo cargado no es el que gener\xF3 el plugin para este ajuste (archivo_hash distinto): descargarlo de nuevo y cargar ese" : "el ajuste no tiene archivo generado")
|
|
42959
|
+
];
|
|
42960
|
+
}
|
|
42961
|
+
},
|
|
42962
|
+
{
|
|
42963
|
+
via: "verificar",
|
|
42964
|
+
de: ["aplicado"],
|
|
42965
|
+
a: (caso, datos) => {
|
|
42966
|
+
const r = compararRelectura(itemsACargar(leerItems(caso.datos.items), caso.datos.excluir), datos.leidos);
|
|
42967
|
+
return "error" in r || r.diferencias.length > 0 ? "con_diferencias" : "verificado";
|
|
42968
|
+
},
|
|
42969
|
+
actor: ["agente", "humano", "sistema"],
|
|
42970
|
+
nivelRequerido: "observa",
|
|
42971
|
+
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 acordado.",
|
|
42972
|
+
validar: (_caso, datos) => {
|
|
42973
|
+
const r = compararRelectura([], datos.leidos);
|
|
42974
|
+
return [
|
|
42975
|
+
texto4(datos.lectura_id) ? ok("lectura_registrada") : falla("lectura_registrada", "verificar requiere lectura_id: la relectura del convenio registrada"),
|
|
42976
|
+
"error" in r ? falla("leidos_validos", r.error) : ok("leidos_validos")
|
|
42977
|
+
];
|
|
42978
|
+
}
|
|
42979
|
+
},
|
|
42980
|
+
{
|
|
42981
|
+
via: "corregir",
|
|
42982
|
+
de: ["con_diferencias"],
|
|
42983
|
+
a: "aplicado",
|
|
42984
|
+
actor: ["humano"],
|
|
42985
|
+
cargaManual: true,
|
|
42986
|
+
descripcion: "Corregir en el HIS las pr\xE1cticas que no coincidieron y volver a comprobar.",
|
|
42987
|
+
validar: (_caso, datos) => [texto4(datos.accion_id) ? ok("accion_registrada") : falla("accion_registrada", "corregir requiere accion_id de la correcci\xF3n registrada")]
|
|
42988
|
+
},
|
|
42989
|
+
{
|
|
42990
|
+
via: "cancelar",
|
|
42991
|
+
de: ["abierto", "calculado", "pendiente_aprobacion", "por_enviar", "enviado", "acordado", "con_diferencias"],
|
|
42992
|
+
a: "cancelado",
|
|
42993
|
+
actor: ["humano", "agente"],
|
|
42994
|
+
nivelRequerido: "observa",
|
|
42995
|
+
descripcion: "Cancelar el ajuste (repetido, el convenio cambi\xF3 por otra v\xEDa, se renegoci\xF3).",
|
|
42996
|
+
validar: (_caso, datos) => [texto4(datos.motivo) ? ok("motivo_presente") : falla("motivo_presente", "la cancelaci\xF3n requiere motivo")]
|
|
42997
|
+
}
|
|
42998
|
+
]
|
|
42999
|
+
};
|
|
43000
|
+
function leerTopeAjuste(v) {
|
|
43001
|
+
const n = num5(v);
|
|
43002
|
+
return n !== null && n >= 1 && n <= 100 ? Math.round(n) : TOPE_AJUSTE_POR_DEFECTO;
|
|
43003
|
+
}
|
|
42617
43004
|
var CANALES_RECLAMO = ["correo", "portal_del_financiador", "nota", "telefono", "otro"];
|
|
42618
43005
|
function resumenCobranzaDe(caso) {
|
|
42619
43006
|
const r = caso.datos.resumen;
|
|
@@ -43296,6 +43683,7 @@ var WORKFLOWS = {
|
|
|
43296
43683
|
[credencialProfesional.clave]: credencialProfesional,
|
|
43297
43684
|
[demandaPendiente.clave]: demandaPendiente,
|
|
43298
43685
|
[circularAranceles.clave]: circularAranceles,
|
|
43686
|
+
[ajusteConvenio.clave]: ajusteConvenio,
|
|
43299
43687
|
[cobranzaFinanciador.clave]: cobranzaFinanciador,
|
|
43300
43688
|
[internacionAlDia.clave]: internacionAlDia,
|
|
43301
43689
|
[honorarioEnCero.clave]: honorarioEnCero,
|
|
@@ -43795,7 +44183,7 @@ async function informarPruebaRegla(ctx, input2) {
|
|
|
43795
44183
|
// src/rentabilidad.ts
|
|
43796
44184
|
var UMBRAL_MARGEN_POR_DEFECTO = 15;
|
|
43797
44185
|
var MESES_PRECIO_POR_DEFECTO = 6;
|
|
43798
|
-
var
|
|
44186
|
+
var redondear5 = (n) => Math.round(n * 100) / 100;
|
|
43799
44187
|
function texto5(v) {
|
|
43800
44188
|
return typeof v === "string" && v.trim() ? v.trim() : typeof v === "number" && Number.isFinite(v) ? String(v) : null;
|
|
43801
44189
|
}
|
|
@@ -43824,7 +44212,7 @@ function leerLecturaRentabilidad(v) {
|
|
|
43824
44212
|
const x = c ?? {};
|
|
43825
44213
|
const monto = num6(x.monto);
|
|
43826
44214
|
if (monto === null || monto < 0) return { error: `lectura.componentes[${i}].monto: tiene que ser un n\xFAmero \u2265 0` };
|
|
43827
|
-
componentes.push({ arancel: texto5(x.arancel) ?? "", monto:
|
|
44215
|
+
componentes.push({ arancel: texto5(x.arancel) ?? "", monto: redondear5(monto) });
|
|
43828
44216
|
}
|
|
43829
44217
|
const insumos = [];
|
|
43830
44218
|
for (const [i, c] of r.insumos.entries()) {
|
|
@@ -43832,7 +44220,7 @@ function leerLecturaRentabilidad(v) {
|
|
|
43832
44220
|
const cantidad2 = num6(x.cantidad);
|
|
43833
44221
|
if (cantidad2 === null || cantidad2 <= 0) return { error: `lectura.insumos[${i}].cantidad: tiene que ser un n\xFAmero > 0` };
|
|
43834
44222
|
const precio = num6(x.precio);
|
|
43835
|
-
insumos.push({ articulo_id: texto5(x.articulo_id) ?? String(i), articulo: texto5(x.articulo) ?? "", cantidad: cantidad2, precio: precio === null ? null :
|
|
44223
|
+
insumos.push({ articulo_id: texto5(x.articulo_id) ?? String(i), articulo: texto5(x.articulo) ?? "", cantidad: cantidad2, precio: precio === null ? null : redondear5(precio), modificado_en: dia3(x.modificado_en) });
|
|
43836
44224
|
}
|
|
43837
44225
|
const honorarios = [];
|
|
43838
44226
|
for (const [i, c] of r.honorarios.entries()) {
|
|
@@ -43870,7 +44258,7 @@ function evaluarRentabilidad(input2) {
|
|
|
43870
44258
|
const faltas = [];
|
|
43871
44259
|
const avisos = [];
|
|
43872
44260
|
const insumosARevisar = [];
|
|
43873
|
-
const valor =
|
|
44261
|
+
const valor = redondear5(l.componentes.reduce((a, c) => a + c.monto, 0));
|
|
43874
44262
|
if (valor <= 0) faltas.push("sin_valor_convenio");
|
|
43875
44263
|
let insumos = 0;
|
|
43876
44264
|
for (const i of l.insumos) {
|
|
@@ -43885,7 +44273,7 @@ function evaluarRentabilidad(input2) {
|
|
|
43885
44273
|
insumosARevisar.push(`${i.articulo || i.articulo_id} (precio ${i.modificado_en ? `del ${i.modificado_en.slice(8, 10)}/${i.modificado_en.slice(5, 7)}/${i.modificado_en.slice(0, 4)}` : "sin fecha"})`);
|
|
43886
44274
|
}
|
|
43887
44275
|
}
|
|
43888
|
-
insumos =
|
|
44276
|
+
insumos = redondear5(insumos);
|
|
43889
44277
|
if (l.insumos.length === 0) avisos.push("sin_insumos");
|
|
43890
44278
|
const debajo = [];
|
|
43891
44279
|
const honorarios = [];
|
|
@@ -43909,10 +44297,10 @@ function evaluarRentabilidad(input2) {
|
|
|
43909
44297
|
}
|
|
43910
44298
|
const honorarioMin = honorarios.length ? Math.min(...honorarios) : null;
|
|
43911
44299
|
const honorarioMax = honorarios.length ? Math.max(...honorarios) : null;
|
|
43912
|
-
const margenPeor = honorarioMax !== null && valor > 0 ?
|
|
43913
|
-
const margenMejor = honorarioMin !== null && valor > 0 ?
|
|
44300
|
+
const margenPeor = honorarioMax !== null && valor > 0 ? redondear5(valor - insumos - honorarioMax) : null;
|
|
44301
|
+
const margenMejor = honorarioMin !== null && valor > 0 ? redondear5(valor - insumos - honorarioMin) : null;
|
|
43914
44302
|
const margenPct = margenPeor !== null && valor > 0 ? Math.round(margenPeor / valor * 1e3) / 10 : null;
|
|
43915
|
-
const impacto = margenPeor !== null ?
|
|
44303
|
+
const impacto = margenPeor !== null ? redondear5(margenPeor * l.volumen_90d / 3) : null;
|
|
43916
44304
|
let clase2;
|
|
43917
44305
|
if (faltas.length > 0 || margenPct === null) clase2 = "datos_incompletos";
|
|
43918
44306
|
else if (margenPeor < 0) clase2 = "perdida";
|
|
@@ -44050,7 +44438,7 @@ var num7 = (v) => {
|
|
|
44050
44438
|
const n = typeof v === "number" ? v : typeof v === "string" && v.trim() ? Number(v) : NaN;
|
|
44051
44439
|
return Number.isFinite(n) ? n : null;
|
|
44052
44440
|
};
|
|
44053
|
-
var
|
|
44441
|
+
var redondear6 = (n) => Math.round(n * 100) / 100;
|
|
44054
44442
|
var fechaHora2 = (v) => {
|
|
44055
44443
|
const s = texto7(v);
|
|
44056
44444
|
return s && /^\d{4}-\d{2}-\d{2}([ T]\d{2}:\d{2}(:\d{2})?)?/.test(s) ? s.replace(" ", "T").slice(0, 19) : null;
|
|
@@ -44081,7 +44469,7 @@ function leerLecturaCostoInternacion(v) {
|
|
|
44081
44469
|
if (cantidad2 === null || cantidad2 < 0 || monto === null || monto < 0) return { error: `lectura.facturado[${i}]: cantidad y monto tienen que ser n\xFAmeros \u2265 0` };
|
|
44082
44470
|
if (f.regla !== null && f.regla !== void 0 && !leerRegla(f.regla)) return { error: `lectura.facturado[${i}].regla: tiene que ser {id, prof, inst} o null` };
|
|
44083
44471
|
const noLiquidable = f.liquidable === false || f.liquidable === 0 || f.liquidable === "0" || f.liquidable === "false";
|
|
44084
|
-
facturado.push({ prestacion_id: texto7(f.prestacion_id) ?? String(i), nombre: texto7(f.nombre) ?? "", cantidad: cantidad2, monto:
|
|
44472
|
+
facturado.push({ prestacion_id: texto7(f.prestacion_id) ?? String(i), nombre: texto7(f.nombre) ?? "", cantidad: cantidad2, monto: redondear6(monto), profesional_id: texto7(f.profesional_id), regla: leerRegla(f.regla), ...noLiquidable ? { liquidable: false } : {} });
|
|
44085
44473
|
}
|
|
44086
44474
|
const consumos = [];
|
|
44087
44475
|
for (const [i, x] of r.consumos.entries()) {
|
|
@@ -44167,7 +44555,7 @@ function evaluarCostoInternacion(input2) {
|
|
|
44167
44555
|
const umbral = input2.umbral ?? UMBRAL_MARGEN_POR_DEFECTO;
|
|
44168
44556
|
const faltas = [];
|
|
44169
44557
|
const avisos = [];
|
|
44170
|
-
const facturado =
|
|
44558
|
+
const facturado = redondear6(l.facturado.reduce((a, f) => a + f.monto * f.cantidad, 0));
|
|
44171
44559
|
if (facturado <= 0) faltas.push("sin_facturar");
|
|
44172
44560
|
if (!l.egreso) avisos.push("en_curso");
|
|
44173
44561
|
let honorarios = 0;
|
|
@@ -44177,7 +44565,7 @@ function evaluarCostoInternacion(input2) {
|
|
|
44177
44565
|
if (!f.regla || f.regla.prof === null && f.regla.inst === null) sinRegla += 1;
|
|
44178
44566
|
honorarios += honorarioDe({ regla: f.regla, monto: f.monto, cantidad: f.cantidad });
|
|
44179
44567
|
}
|
|
44180
|
-
honorarios =
|
|
44568
|
+
honorarios = redondear6(honorarios);
|
|
44181
44569
|
if (sinRegla > 0) avisos.push("prestaciones_sin_regla");
|
|
44182
44570
|
let insumos = 0;
|
|
44183
44571
|
const sinCosto = [];
|
|
@@ -44190,7 +44578,7 @@ function evaluarCostoInternacion(input2) {
|
|
|
44190
44578
|
}
|
|
44191
44579
|
insumos += cantidad2 * c.costo_unitario;
|
|
44192
44580
|
}
|
|
44193
|
-
insumos =
|
|
44581
|
+
insumos = redondear6(insumos);
|
|
44194
44582
|
if (sinCosto.length) faltas.push("insumo_sin_costo");
|
|
44195
44583
|
const { tramos, total, exactos } = diasPorServicio(l, hoy);
|
|
44196
44584
|
const porServicio = input2.costoDiaPorServicio ?? {};
|
|
@@ -44205,18 +44593,18 @@ function evaluarCostoInternacion(input2) {
|
|
|
44205
44593
|
else cama += (exactos.get(servicio) ?? dias) * costo_dia;
|
|
44206
44594
|
return { servicio, dias, costo_dia: costo_dia ?? null };
|
|
44207
44595
|
});
|
|
44208
|
-
cama =
|
|
44596
|
+
cama = redondear6(cama);
|
|
44209
44597
|
if (faltaCama) faltas.push("sin_costo_cama");
|
|
44210
44598
|
const minutos = l.cirugias.reduce((a, c) => a + (c.minutos ?? 0), 0);
|
|
44211
44599
|
let quirofano = 0;
|
|
44212
44600
|
if (l.cirugias.length > 0) {
|
|
44213
44601
|
if (input2.costoHoraQuirofano === null || input2.costoHoraQuirofano === void 0) faltas.push("sin_costo_quirofano");
|
|
44214
|
-
else quirofano =
|
|
44602
|
+
else quirofano = redondear6(minutos / 60 * input2.costoHoraQuirofano);
|
|
44215
44603
|
}
|
|
44216
44604
|
if (l.cirugias.some((c) => c.minutos === null)) faltas.push("cirugia_sin_duracion");
|
|
44217
|
-
const costo =
|
|
44605
|
+
const costo = redondear6(honorarios + insumos + cama + quirofano);
|
|
44218
44606
|
const calculable = faltas.length === 0 && facturado > 0;
|
|
44219
|
-
const margen = calculable ?
|
|
44607
|
+
const margen = calculable ? redondear6(facturado - costo) : null;
|
|
44220
44608
|
const margen_pct = margen !== null ? Math.round(margen / facturado * 1e3) / 10 : null;
|
|
44221
44609
|
let clase2;
|
|
44222
44610
|
if (margen === null || margen_pct === null) clase2 = "datos_incompletos";
|
|
@@ -44383,6 +44771,13 @@ function describirOpciones(antes, despues) {
|
|
|
44383
44771
|
if (antes.hora_corte !== despues.hora_corte && despues.hora_corte !== void 0) partes.push(`hora de corte de la evoluci\xF3n: ${String(despues.hora_corte)}`);
|
|
44384
44772
|
if (antes.dias_sin_actividad !== despues.dias_sin_actividad && despues.dias_sin_actividad !== void 0) partes.push(`d\xEDas sin actividad para sospechar un egreso sin registrar: ${String(despues.dias_sin_actividad)}`);
|
|
44385
44773
|
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)} %`);
|
|
44774
|
+
if (JSON.stringify(antes.formula) !== JSON.stringify(despues.formula) && despues.formula !== void 0) {
|
|
44775
|
+
const f = despues.formula;
|
|
44776
|
+
partes.push(`f\xF3rmula del ajuste: ${f.fijo !== void 0 ? `${f.fijo} % fijo` : (f.componentes ?? []).map((c) => `${c.serie.replace(/_/g, " ")} ${c.peso} %`).join(" + ")}`);
|
|
44777
|
+
}
|
|
44778
|
+
if (antes.tope_ajuste !== despues.tope_ajuste && despues.tope_ajuste !== void 0) partes.push(`tope del ajuste por \xEDndice: ${String(despues.tope_ajuste)} %`);
|
|
44779
|
+
if (JSON.stringify(antes.nota) !== JSON.stringify(despues.nota) && despues.nota !== void 0) partes.push("texto de la nota al financiador");
|
|
44780
|
+
for (const k of ["firma", "institucion"]) if (antes[k] !== despues[k] && despues[k] !== void 0) partes.push(`${k === "firma" ? "firma de la nota" : "nombre de la instituci\xF3n en la nota"}: ${String(despues[k] ?? "sin fijar")}`);
|
|
44386
44781
|
if (antes.umbral_margen !== despues.umbral_margen && despues.umbral_margen !== void 0) partes.push(`margen m\xEDnimo: ${String(despues.umbral_margen)} %`);
|
|
44387
44782
|
if (antes.meses_precio !== despues.meses_precio && despues.meses_precio !== void 0) partes.push(`meses para avisar un precio de insumo viejo: ${String(despues.meses_precio)}`);
|
|
44388
44783
|
if (antes.costo_dia_cama !== despues.costo_dia_cama && despues.costo_dia_cama !== void 0) partes.push(`costo del d\xEDa cama: ${despues.costo_dia_cama === null ? "sin fijar" : `$ ${String(despues.costo_dia_cama)}`}`);
|
|
@@ -44480,6 +44875,28 @@ async function cambiarPolitica(ctx, input2) {
|
|
|
44480
44875
|
if (v === null) throw new OpsError("validacion", "dias_sin_actividad: tiene que ser un n\xFAmero entero entre 1 y 15");
|
|
44481
44876
|
opciones.dias_sin_actividad = v;
|
|
44482
44877
|
}
|
|
44878
|
+
if (input2.workflow === "ajuste_convenio") {
|
|
44879
|
+
if (opciones.formula !== void 0) {
|
|
44880
|
+
const f = leerFormula(opciones.formula);
|
|
44881
|
+
if ("error" in f) throw new OpsError("validacion", `formula: ${f.error}`);
|
|
44882
|
+
opciones.formula = f;
|
|
44883
|
+
}
|
|
44884
|
+
if (opciones.dias_respuesta !== void 0) {
|
|
44885
|
+
const v = enteroEntre(opciones.dias_respuesta, 1, 60);
|
|
44886
|
+
if (v === null) throw new OpsError("validacion", "dias_respuesta: tiene que ser un n\xFAmero entero entre 1 y 60");
|
|
44887
|
+
opciones.dias_respuesta = v;
|
|
44888
|
+
}
|
|
44889
|
+
if (opciones.tope_ajuste !== void 0) {
|
|
44890
|
+
const v = enteroEntre(opciones.tope_ajuste, LIMITES_TOPE_AJUSTE[0], LIMITES_TOPE_AJUSTE[1]);
|
|
44891
|
+
if (v === null) throw new OpsError("validacion", `tope_ajuste: tiene que ser un n\xFAmero entero entre ${LIMITES_TOPE_AJUSTE[0]} y ${LIMITES_TOPE_AJUSTE[1]}`);
|
|
44892
|
+
opciones.tope_ajuste = v;
|
|
44893
|
+
}
|
|
44894
|
+
if (opciones.nota !== void 0) {
|
|
44895
|
+
const n = opciones.nota && typeof opciones.nota === "object" ? opciones.nota : {};
|
|
44896
|
+
opciones.nota = { asunto: str2(n.asunto)?.slice(0, 200) ?? null, texto: str2(n.texto)?.slice(0, 5e3) ?? null };
|
|
44897
|
+
}
|
|
44898
|
+
for (const k of ["firma", "institucion"]) if (opciones[k] !== void 0) opciones[k] = str2(opciones[k])?.slice(0, 200) ?? null;
|
|
44899
|
+
}
|
|
44483
44900
|
if (opciones.tope_variacion !== void 0) {
|
|
44484
44901
|
const tope = leerTope(opciones.tope_variacion);
|
|
44485
44902
|
if (tope === null) throw new OpsError("validacion", `tope_variacion: tiene que ser un n\xFAmero entre ${LIMITES_TOPE[0]} y ${LIMITES_TOPE[1]}`);
|
|
@@ -44667,6 +45084,15 @@ async function abrirCaso(ctx, input2) {
|
|
|
44667
45084
|
if (faltan.length) throw new OpsError("validacion", `circular_aranceles necesita ${faltan.join(", ")}`);
|
|
44668
45085
|
if (!/^\d{4}-\d{2}-\d{2}$/.test(String(d.vigencia_desde))) throw new OpsError("validacion", "vigencia_desde: AAAA-MM-DD");
|
|
44669
45086
|
}
|
|
45087
|
+
if (wf.clave === "ajuste_convenio") {
|
|
45088
|
+
const d = input2.datos ?? {};
|
|
45089
|
+
const faltan = ["financiador", "convenio", "vigencia_desde", "ultimo_ajuste"].filter((k) => !str2(d[k]));
|
|
45090
|
+
if (faltan.length) throw new OpsError("validacion", `ajuste_convenio necesita ${faltan.join(", ")}`);
|
|
45091
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(String(d.vigencia_desde))) throw new OpsError("validacion", "vigencia_desde: AAAA-MM-DD");
|
|
45092
|
+
if (!/^\d{4}-(0[1-9]|1[0-2])$/.test(String(d.ultimo_ajuste))) throw new OpsError("validacion", "ultimo_ajuste: el mes del \xFAltimo ajuste, AAAA-MM");
|
|
45093
|
+
const esperada = `ajuste:${(normalizarCodigo(d.convenio) || String(d.convenio).trim()).toLowerCase()}:${String(d.vigencia_desde).slice(0, 7)}`;
|
|
45094
|
+
if (referencia !== esperada) throw new OpsError("referencia_requerida", `en ajuste_convenio la referencia es obligatoria: ${esperada}`);
|
|
45095
|
+
}
|
|
44670
45096
|
const extra = {};
|
|
44671
45097
|
if (wf.clave === "honorario_en_cero" || wf.clave === "debito_profesional" || wf.clave === "liquidacion_profesional") {
|
|
44672
45098
|
const p = leerProfesional(input2.datos?.profesional) ?? leerProfesional({ id: input2.datos?.profesional_id, nombre: input2.datos?.profesional });
|
|
@@ -44781,6 +45207,12 @@ async function contextoDe(ctx, companyId, caso, via, datos) {
|
|
|
44781
45207
|
contexto.topeVariacion = leerTope((await obtenerOpciones(ctx, companyId, caso.workflow)).tope_variacion);
|
|
44782
45208
|
return contexto;
|
|
44783
45209
|
}
|
|
45210
|
+
if (caso.workflow === "ajuste_convenio") {
|
|
45211
|
+
const op = await obtenerOpciones(ctx, companyId, caso.workflow);
|
|
45212
|
+
contexto.formulaAjuste = leerFormula(op.formula);
|
|
45213
|
+
contexto.topeAjuste = leerTopeAjuste(op.tope_ajuste);
|
|
45214
|
+
return contexto;
|
|
45215
|
+
}
|
|
44784
45216
|
if (caso.workflow === "rentabilidad_practica") {
|
|
44785
45217
|
if (via === "evaluar" || via === "comprobar") {
|
|
44786
45218
|
const op = await obtenerOpciones(ctx, companyId, caso.workflow);
|
|
@@ -44890,7 +45322,7 @@ async function transicionar(ctx, input2) {
|
|
|
44890
45322
|
);
|
|
44891
45323
|
const lectura = rows[0];
|
|
44892
45324
|
let vieja = false;
|
|
44893
|
-
if (lectura && (resultado.via === "comprobar" || caso.workflow === "circular_aranceles" && resultado.via === "verificar")) {
|
|
45325
|
+
if (lectura && (resultado.via === "comprobar" || (caso.workflow === "circular_aranceles" || caso.workflow === "ajuste_convenio") && resultado.via === "verificar")) {
|
|
44894
45326
|
const ultima = await ctx.db.query(
|
|
44895
45327
|
`SELECT max(created_at) AS ultima FROM ${t3(ctx, "transicion")} WHERE company_id = $1 AND caso_id = $2`,
|
|
44896
45328
|
[input2.companyId, caso.id]
|
|
@@ -44945,6 +45377,87 @@ async function transicionar(ctx, input2) {
|
|
|
44945
45377
|
datos.verificada_el = contexto.hoy ?? null;
|
|
44946
45378
|
}
|
|
44947
45379
|
}
|
|
45380
|
+
if (caso.workflow === "ajuste_convenio") {
|
|
45381
|
+
for (const k of ["items", "resumen", "archivo_csv", "diferencias", "nota", "detalle", "periodo_hasta", "porcentaje_calculado", "porcentaje_acordado", "indices_usados"]) delete datos[k];
|
|
45382
|
+
const tope = contexto.topeAjuste ?? leerTopeAjuste(void 0);
|
|
45383
|
+
const porcentajeAntes = typeof caso.datos.porcentaje === "number" ? caso.datos.porcentaje : 0;
|
|
45384
|
+
const calculado = typeof caso.datos.porcentaje_calculado === "number" ? caso.datos.porcentaje_calculado : porcentajeAntes;
|
|
45385
|
+
const volGuardado = caso.datos.volumenes && typeof caso.datos.volumenes === "object" ? caso.datos.volumenes : null;
|
|
45386
|
+
const recalcular = (porcentaje3, excluir) => {
|
|
45387
|
+
const a = armarAjuste(caso.datos.actual, porcentaje3);
|
|
45388
|
+
if (!a.ok) return;
|
|
45389
|
+
datos.porcentaje = porcentaje3;
|
|
45390
|
+
datos.items = a.items;
|
|
45391
|
+
datos.resumen = resumenAjuste(a.items, excluir, porcentaje3, calculado, tope, volGuardado);
|
|
45392
|
+
};
|
|
45393
|
+
const pedido = typeof datos.porcentaje === "number" ? datos.porcentaje : typeof datos.porcentaje === "string" && datos.porcentaje.trim() ? Number(datos.porcentaje) : null;
|
|
45394
|
+
delete datos.porcentaje;
|
|
45395
|
+
if (resultado.via === "calcular") {
|
|
45396
|
+
delete datos.excluir;
|
|
45397
|
+
const c = calcularAjuste(caso, datos, contexto);
|
|
45398
|
+
const act = leerActual(datos.actual);
|
|
45399
|
+
const vl = leerVolumenes(datos.volumenes, datos.dias_volumen);
|
|
45400
|
+
const vol = vl && !("error" in vl) ? vl : null;
|
|
45401
|
+
delete datos.dias_volumen;
|
|
45402
|
+
if (c.ok && "practicas" in act) {
|
|
45403
|
+
Object.assign(datos, {
|
|
45404
|
+
porcentaje: c.porcentaje,
|
|
45405
|
+
porcentaje_calculado: c.porcentaje,
|
|
45406
|
+
detalle: c.detalle,
|
|
45407
|
+
periodo_hasta: c.hasta,
|
|
45408
|
+
items: c.items,
|
|
45409
|
+
excluir: [],
|
|
45410
|
+
resumen: resumenAjuste(c.items, [], c.porcentaje, c.porcentaje, tope, vol),
|
|
45411
|
+
volumenes: vol,
|
|
45412
|
+
// Lo que tenía el convenio antes del ajuste queda guardado en el caso: es el respaldo para volver atrás.
|
|
45413
|
+
actual: act.practicas,
|
|
45414
|
+
indices_usados: Array.isArray(datos.indices) ? datos.indices : []
|
|
45415
|
+
});
|
|
45416
|
+
} else delete datos.volumenes;
|
|
45417
|
+
delete datos.indices;
|
|
45418
|
+
} else {
|
|
45419
|
+
delete datos.actual;
|
|
45420
|
+
delete datos.indices;
|
|
45421
|
+
delete datos.volumenes;
|
|
45422
|
+
delete datos.dias_volumen;
|
|
45423
|
+
}
|
|
45424
|
+
if (resultado.via === "ajustar") {
|
|
45425
|
+
const excluir = Array.isArray(datos.excluir) ? datos.excluir.map(normalizarCodigo) : caso.datos.excluir ?? [];
|
|
45426
|
+
datos.excluir = excluir;
|
|
45427
|
+
recalcular(pedido ?? porcentajeAntes, excluir);
|
|
45428
|
+
} else if (resultado.via !== "calcular") delete datos.excluir;
|
|
45429
|
+
if (resultado.via === "responder" && datos.respuesta === "aprobado_otro" && pedido !== null) {
|
|
45430
|
+
datos.porcentaje_acordado = pedido;
|
|
45431
|
+
recalcular(pedido, caso.datos.excluir);
|
|
45432
|
+
}
|
|
45433
|
+
if (resultado.via === "responder" && datos.respuesta === "aprobado") datos.porcentaje_acordado = porcentajeAntes;
|
|
45434
|
+
if (resultado.a === "por_enviar") {
|
|
45435
|
+
const op = await obtenerOpciones(ctx, input2.companyId, caso.workflow);
|
|
45436
|
+
const r = caso.datos.resumen;
|
|
45437
|
+
datos.nota = armarNota(op.nota, {
|
|
45438
|
+
institucion: str2(op.institucion) ?? str2(caso.datos.institucion) ?? "",
|
|
45439
|
+
financiador: str2(caso.datos.financiador) ?? "",
|
|
45440
|
+
convenio: str2(caso.datos.convenio) ?? "",
|
|
45441
|
+
porcentaje: porcentajeAntes,
|
|
45442
|
+
indices: textoIndices(caso.datos.detalle ?? { fijo: porcentajeAntes }, str2(caso.datos.ultimo_ajuste), str2(caso.datos.periodo_hasta)),
|
|
45443
|
+
vigencia: str2(caso.datos.vigencia_desde) ?? "",
|
|
45444
|
+
firma: str2(op.firma) ?? "",
|
|
45445
|
+
practicas: r?.practicas ?? 0
|
|
45446
|
+
});
|
|
45447
|
+
}
|
|
45448
|
+
if (resultado.a === "acordado") {
|
|
45449
|
+
const items = datos.items ?? leerItems(caso.datos.items);
|
|
45450
|
+
const aCargar = itemsACargar(items, caso.datos.excluir);
|
|
45451
|
+
const actual = leerActual(caso.datos.actual);
|
|
45452
|
+
const archivo = archivoActualizacion(aCargar, "practicas" in actual ? actual.practicas : []);
|
|
45453
|
+
datos.archivo_csv = { contenido: archivo.contenido, hash: archivo.hash, filas: archivo.filas, aranceles: archivo.aranceles, nombre: `ajuste-${String(caso.datos.convenio ?? "convenio").toLowerCase().replace(/[^a-z0-9]+/g, "-")}-${String(caso.datos.vigencia_desde ?? "")}.csv` };
|
|
45454
|
+
}
|
|
45455
|
+
if (resultado.via === "verificar") {
|
|
45456
|
+
const r = compararRelectura(itemsACargar(leerItems(caso.datos.items), caso.datos.excluir), datos.leidos);
|
|
45457
|
+
datos.diferencias = "error" in r ? [] : r.diferencias;
|
|
45458
|
+
datos.verificada_el = contexto.hoy ?? null;
|
|
45459
|
+
}
|
|
45460
|
+
}
|
|
44948
45461
|
if (caso.workflow === "honorario_en_cero") {
|
|
44949
45462
|
for (const k of ["items", "resumen", "comprobado_el"]) delete datos[k];
|
|
44950
45463
|
if (resultado.via === "observar" || resultado.via === "comprobar") {
|
|
@@ -45530,7 +46043,7 @@ async function registrarCargaManual(ctx, input2) {
|
|
|
45530
46043
|
...caso.workflow === "cuenta_completa" ? { faltantes_que_completo: caso.datos.faltantes ?? [] } : {},
|
|
45531
46044
|
...caso.workflow === "facturacion_pendiente" ? { bono: caso.datos.bono_numero ?? caso.datos.bono_id, vence_el: caso.datos.vence_el } : {},
|
|
45532
46045
|
...caso.workflow === "cobranza_financiador" ? { canal: canalReclamo(input2.canal), destinatario: str2(input2.destinatario), reclamo: caso.datos.reclamo?.numero ?? 1, total: caso.datos.resumen?.pendiente ?? null } : {},
|
|
45533
|
-
...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 } : {},
|
|
46046
|
+
...caso.workflow === "circular_aranceles" || caso.workflow === "ajuste_convenio" ? { archivo_hash: archivoHash, archivo: caso.datos.archivo_csv?.nombre ?? null, practicas: caso.datos.archivo_csv?.filas ?? null, convenio: caso.datos.convenio ?? null } : {},
|
|
45534
46047
|
...caso.workflow === "credencial_profesional" ? { profesional: caso.datos.profesional ?? caso.datos.profesional_id, observacion_que_corrigio: caso.datos.observacion ?? null } : {},
|
|
45535
46048
|
...comentario ? { nota: comentario } : {}
|
|
45536
46049
|
},
|
|
@@ -45830,6 +46343,29 @@ async function registrarEnvioLiquidacion(ctx, input2) {
|
|
|
45830
46343
|
});
|
|
45831
46344
|
return transicionar(ctx, { companyId: input2.companyId, casoId: caso.id, via: "enviar", datos: { aviso_id: accion.id, canal, ...destinatario ? { destinatario } : {}, ...persona ? { registrado_por: persona } : {} }, actor: input2.actor });
|
|
45832
46345
|
}
|
|
46346
|
+
async function registrarEnvioNota(ctx, input2) {
|
|
46347
|
+
const caso = await leerCaso(ctx, input2.companyId, input2.casoId);
|
|
46348
|
+
if (!caso) throw new OpsError("caso_inexistente", `no existe el caso ${input2.casoId} en esta instituci\xF3n`);
|
|
46349
|
+
if (caso.workflow !== "ajuste_convenio") throw new OpsError("workflow_invalido", "solo los ajustes de convenio llevan nota al financiador");
|
|
46350
|
+
if (caso.estado !== "por_enviar") throw new OpsError("estado_invalido", caso.estado === "enviado" ? "la nota ya figura como enviada" : `el ajuste est\xE1 ${caso.estado}: la nota se env\xEDa cuando la direcci\xF3n lo aprueba`);
|
|
46351
|
+
if (input2.actor.tipo !== "humano") throw new OpsError("actor_no_permitido", "el env\xEDo lo registra una persona");
|
|
46352
|
+
const canal = typeof input2.canal === "string" && CANALES_NOTA.includes(input2.canal) ? input2.canal : "otro";
|
|
46353
|
+
const destinatario = str2(input2.destinatario);
|
|
46354
|
+
if (!destinatario) throw new OpsError("validacion", "destinatario_presente: anotar a qui\xE9n se mand\xF3 la nota (correo, \xE1rea o persona del financiador)", { validaciones: [{ regla: "destinatario_presente", ok: false, detalle: "falta el destinatario" }], estado: caso.estado });
|
|
46355
|
+
const persona = str2(input2.persona);
|
|
46356
|
+
const nota = caso.datos.nota;
|
|
46357
|
+
const accion = await registrarAccion(ctx, {
|
|
46358
|
+
companyId: input2.companyId,
|
|
46359
|
+
casoId: caso.id,
|
|
46360
|
+
tipo: "aviso",
|
|
46361
|
+
conector: canal === "correo" ? "mail" : "manual",
|
|
46362
|
+
objetivo: `Nota de ajuste a ${str2(caso.datos.financiador) ?? "el financiador"}`,
|
|
46363
|
+
resultado: "ok",
|
|
46364
|
+
evidencia: { canal, destinatario, asunto: nota?.asunto ?? null, porcentaje: caso.datos.porcentaje ?? null, ...persona ? { registrado_por: persona } : {} },
|
|
46365
|
+
actor: input2.actor
|
|
46366
|
+
});
|
|
46367
|
+
return transicionar(ctx, { companyId: input2.companyId, casoId: caso.id, via: "enviar", datos: { aviso_id: accion.id, canal, destinatario, ...persona ? { registrado_por: persona } : {} }, actor: input2.actor });
|
|
46368
|
+
}
|
|
45833
46369
|
function canalReclamo(v) {
|
|
45834
46370
|
return typeof v === "string" && CANALES_RECLAMO.includes(v) ? v : "otro";
|
|
45835
46371
|
}
|
|
@@ -46817,7 +47353,7 @@ async function manejarApi(ctx, input2) {
|
|
|
46817
47353
|
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 }));
|
|
46818
47354
|
}
|
|
46819
47355
|
case "version":
|
|
46820
|
-
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", "cobranzas", "internaciones", "honorarios"], workflows: listarWorkflows().map((w) => w.clave) });
|
|
47356
|
+
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", "ajuste-convenio", "cobranzas", "internaciones", "honorarios"], workflows: listarWorkflows().map((w) => w.clave) });
|
|
46821
47357
|
case "politicas":
|
|
46822
47358
|
return respuesta(200, { politicas: await listarPoliticas(ctx, companyId) });
|
|
46823
47359
|
case "serie":
|
|
@@ -46857,13 +47393,22 @@ async function manejarApi(ctx, input2) {
|
|
|
46857
47393
|
if (actor.tipo !== "humano") return respuesta(403, { error: "actor_no_permitido", motivo: "el env\xEDo lo registra una persona" });
|
|
46858
47394
|
return respuesta(200, await registrarEnvioLiquidacion(ctx, { companyId, casoId: input2.params.casoId ?? "", canal: body.canal, destinatario: str3(body.destinatario), persona: decididoPor, actor }));
|
|
46859
47395
|
}
|
|
47396
|
+
case "ajustar": {
|
|
47397
|
+
if (actor.tipo !== "humano") return respuesta(403, { error: "actor_no_permitido", motivo: "el porcentaje y las pr\xE1cticas del ajuste los cambia la direcci\xF3n" });
|
|
47398
|
+
const datosA = { ...body.porcentaje !== void 0 ? { porcentaje: body.porcentaje } : {}, ...Array.isArray(body.excluir) ? { excluir: body.excluir } : {} };
|
|
47399
|
+
return respuesta(200, await transicionar(ctx, { companyId, casoId: input2.params.casoId ?? "", via: "ajustar", motivo: motivoConPersona(str3(body.motivo)), datos: conPersona({ ...datosA, ...str3(body.motivo) ? { motivo: str3(body.motivo) } : {} }), idempotencyKey: str3(body.idempotencyKey), actor }));
|
|
47400
|
+
}
|
|
47401
|
+
case "envio-nota": {
|
|
47402
|
+
if (actor.tipo !== "humano") return respuesta(403, { error: "actor_no_permitido", motivo: "el env\xEDo lo registra una persona" });
|
|
47403
|
+
return respuesta(200, await registrarEnvioNota(ctx, { companyId, casoId: input2.params.casoId ?? "", canal: body.canal, destinatario: str3(body.destinatario), persona: decididoPor, actor }));
|
|
47404
|
+
}
|
|
46860
47405
|
case "respuesta-financiador": {
|
|
46861
47406
|
if (actor.tipo !== "humano") return respuesta(403, { error: "actor_no_permitido", motivo: "la respuesta del financiador la registra una persona" });
|
|
46862
47407
|
if (str3(body.respuesta) === "cerrar") {
|
|
46863
47408
|
if (!str3(body.detalle)) return respuesta(422, { error: "validacion", motivo: "motivo_presente: cerrar la cobranza requiere motivo" });
|
|
46864
47409
|
return respuesta(200, await transicionar(ctx, { companyId, casoId: input2.params.casoId ?? "", via: "cerrar", motivo: motivoConPersona(str3(body.detalle)), datos: conPersona({}), actor }));
|
|
46865
47410
|
}
|
|
46866
|
-
const datosR = { respuesta: str3(body.respuesta), ...str3(body.fechaCompromiso) ? { fecha_compromiso: str3(body.fechaCompromiso) } : {}, ...str3(body.detalle) ? { detalle: str3(body.detalle) } : {} };
|
|
47411
|
+
const datosR = { respuesta: str3(body.respuesta), ...str3(body.fechaCompromiso) ? { fecha_compromiso: str3(body.fechaCompromiso) } : {}, ...str3(body.detalle) ? { detalle: str3(body.detalle) } : {}, ...body.porcentaje !== void 0 ? { porcentaje: body.porcentaje } : {} };
|
|
46867
47412
|
return respuesta(200, await transicionar(ctx, { companyId, casoId: input2.params.casoId ?? "", via: "responder", motivo: motivoConPersona(str3(body.detalle)), datos: conPersona(datosR), actor }));
|
|
46868
47413
|
}
|
|
46869
47414
|
case "insumos-practica":
|
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.36",
|
|
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": [
|