paperclip-plugin-autopilot-ops 0.1.34 → 0.1.35
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 +563 -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.35";
|
|
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.35";
|
|
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.35";
|
|
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,154 @@ 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
|
+
var redondear2 = (n, dec = 2) => Math.round(n * 10 ** dec) / 10 ** dec;
|
|
41061
|
+
var periodoValido = (p) => typeof p === "string" && /^\d{4}-(0[1-9]|1[0-2])$/.test(p);
|
|
41062
|
+
var porcentajeValido = (n) => typeof n === "number" && Number.isFinite(n) && n > LIMITES_PORCENTAJE[0] && n <= LIMITES_PORCENTAJE[1];
|
|
41063
|
+
function leerFormula(v) {
|
|
41064
|
+
if (v === void 0 || v === null) return FORMULA_POR_DEFECTO;
|
|
41065
|
+
if (typeof v !== "object" || Array.isArray(v)) return { error: "la f\xF3rmula tiene que ser {componentes: [{serie, peso}]} o {fijo: porcentaje}" };
|
|
41066
|
+
const o = v;
|
|
41067
|
+
if ("fijo" in o) {
|
|
41068
|
+
const f = leerMonto(o.fijo);
|
|
41069
|
+
return f !== null && porcentajeValido(f) ? { fijo: redondear2(f) } : { error: `el porcentaje fijo tiene que estar entre ${LIMITES_PORCENTAJE[0]} y ${LIMITES_PORCENTAJE[1]}` };
|
|
41070
|
+
}
|
|
41071
|
+
if (!Array.isArray(o.componentes) || o.componentes.length === 0) return { error: "la f\xF3rmula necesita al menos un \xEDndice" };
|
|
41072
|
+
const componentes = [];
|
|
41073
|
+
for (const c of o.componentes) {
|
|
41074
|
+
const r = c ?? {};
|
|
41075
|
+
const serie2 = typeof r.serie === "string" ? r.serie : "";
|
|
41076
|
+
const peso = leerMonto(r.peso);
|
|
41077
|
+
if (!SERIES[serie2]) return { error: `\xEDndice desconocido: ${serie2 || "(vac\xEDo)"}; los que hay: ${Object.keys(SERIES).join(", ")}` };
|
|
41078
|
+
if (peso === null || peso <= 0) return { error: `el peso de ${serie2} tiene que ser mayor a 0` };
|
|
41079
|
+
if (componentes.some((x) => x.serie === serie2)) return { error: `${serie2} est\xE1 repetido en la f\xF3rmula` };
|
|
41080
|
+
componentes.push({ serie: serie2, peso });
|
|
41081
|
+
}
|
|
41082
|
+
const suma = componentes.reduce((a, c) => a + c.peso, 0);
|
|
41083
|
+
if (Math.abs(suma - 100) > 0.01) return { error: `los pesos tienen que sumar 100 (suman ${suma})` };
|
|
41084
|
+
return { componentes };
|
|
41085
|
+
}
|
|
41086
|
+
function leerIndices(v) {
|
|
41087
|
+
if (!Array.isArray(v)) return { error: "indices tiene que ser una lista [{serie, desde: {periodo, valor}, hasta: {periodo, valor}, fuente}]" };
|
|
41088
|
+
const indices = [];
|
|
41089
|
+
for (const [i, e] of v.entries()) {
|
|
41090
|
+
const r = e ?? {};
|
|
41091
|
+
const serie2 = typeof r.serie === "string" ? r.serie : "";
|
|
41092
|
+
if (!SERIES[serie2]) return { error: `indices[${i}]: serie desconocida ${serie2 || "(vac\xEDa)"}` };
|
|
41093
|
+
if (indices.some((x) => x.serie === serie2)) return { error: `indices[${i}]: ${serie2} est\xE1 repetida` };
|
|
41094
|
+
const d = r.desde ?? {};
|
|
41095
|
+
const h = r.hasta ?? {};
|
|
41096
|
+
const vd = leerMonto(d.valor);
|
|
41097
|
+
const vh = leerMonto(h.valor);
|
|
41098
|
+
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` };
|
|
41099
|
+
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` };
|
|
41100
|
+
if (typeof r.fuente !== "string" || !r.fuente.trim()) return { error: `indices[${i}] (${serie2}): falta la fuente` };
|
|
41101
|
+
indices.push({ serie: serie2, desde: { periodo: d.periodo, valor: vd }, hasta: { periodo: h.periodo, valor: vh }, fuente: r.fuente.trim().slice(0, 200) });
|
|
41102
|
+
}
|
|
41103
|
+
return { indices };
|
|
41104
|
+
}
|
|
41105
|
+
function porcentajeSegunFormula(formula, indices, desde, mesActual) {
|
|
41106
|
+
if ("fijo" in formula) return { porcentaje: formula.fijo, detalle: { fijo: formula.fijo }, hasta: null };
|
|
41107
|
+
const detalle = [];
|
|
41108
|
+
let hasta = null;
|
|
41109
|
+
for (const c of formula.componentes) {
|
|
41110
|
+
const ind = indices.find((x) => x.serie === c.serie);
|
|
41111
|
+
if (!ind) return { error: `falta el \xEDndice ${c.serie} (${SERIES[c.serie].nombre}) que usa la f\xF3rmula de la instituci\xF3n` };
|
|
41112
|
+
if (ind.desde.periodo !== desde) return { error: `${c.serie}: el per\xEDodo empieza en ${ind.desde.periodo} y el \xFAltimo ajuste fue en ${desde}` };
|
|
41113
|
+
if (ind.hasta.periodo >= mesActual) return { error: `${c.serie}: ${ind.hasta.periodo} todav\xEDa no est\xE1 publicado` };
|
|
41114
|
+
if (hasta && ind.hasta.periodo !== hasta) return { error: "los \xEDndices no terminan en el mismo mes" };
|
|
41115
|
+
hasta = ind.hasta.periodo;
|
|
41116
|
+
detalle.push({ serie: c.serie, nombre: SERIES[c.serie].nombre, variacion: redondear2((ind.hasta.valor / ind.desde.valor - 1) * 100), peso: c.peso });
|
|
41117
|
+
}
|
|
41118
|
+
const porcentaje3 = redondear2(detalle.reduce((a, d) => a + d.variacion * d.peso / 100, 0));
|
|
41119
|
+
if (!porcentajeValido(porcentaje3)) return { error: `el porcentaje calculado (${porcentaje3} %) est\xE1 fuera de rango: revisar los valores de los \xEDndices` };
|
|
41120
|
+
return { porcentaje: porcentaje3, detalle, hasta };
|
|
41121
|
+
}
|
|
41122
|
+
function armarAjuste(actual, porcentaje3) {
|
|
41123
|
+
const lectura = leerActual(actual);
|
|
41124
|
+
if ("error" in lectura) return { ok: false, error: lectura.error };
|
|
41125
|
+
if (!porcentajeValido(porcentaje3)) return { ok: false, error: `el porcentaje tiene que estar entre ${LIMITES_PORCENTAJE[0]} y ${LIMITES_PORCENTAJE[1]}` };
|
|
41126
|
+
const items = [];
|
|
41127
|
+
for (const p of lectura.practicas) {
|
|
41128
|
+
const valorActual = redondear2(p.componentes.reduce((a, c) => a + c.monto, 0));
|
|
41129
|
+
if (valorActual <= 0) continue;
|
|
41130
|
+
const valorNuevo = redondear2(valorActual * (1 + porcentaje3 / 100));
|
|
41131
|
+
const componentes = repartir(p.componentes, valorNuevo);
|
|
41132
|
+
items.push({
|
|
41133
|
+
codigo: p.codigo,
|
|
41134
|
+
codigo_his: p.codigo_his,
|
|
41135
|
+
nombre: p.nombre,
|
|
41136
|
+
valor_a: null,
|
|
41137
|
+
valor_b: null,
|
|
41138
|
+
valor_actual: valorActual,
|
|
41139
|
+
valor_nuevo: valorNuevo,
|
|
41140
|
+
variacion: redondear2((valorNuevo / valorActual - 1) * 100, 1),
|
|
41141
|
+
estado: componentes ? "propuesta" : "revisar",
|
|
41142
|
+
causa: componentes ? null : "componentes_en_cero",
|
|
41143
|
+
coinciden: true,
|
|
41144
|
+
motivo: componentes ? null : "no se puede repartir entre sus componentes: cargar a mano",
|
|
41145
|
+
componentes_nuevos: componentes
|
|
41146
|
+
});
|
|
41147
|
+
}
|
|
41148
|
+
if (items.length === 0) return { ok: false, error: "el convenio no tiene pr\xE1cticas con valor" };
|
|
41149
|
+
return { ok: true, items, actual: lectura.practicas };
|
|
41150
|
+
}
|
|
41151
|
+
function resumenAjuste(items, excluir, porcentaje3, porcentajeCalculado, tope) {
|
|
41152
|
+
const fuera = new Set(Array.isArray(excluir) ? excluir.map(normalizarCodigo) : []);
|
|
41153
|
+
const dentro = items.filter((i) => !fuera.has(i.codigo));
|
|
41154
|
+
const cargan = dentro.filter((i) => i.estado === "propuesta");
|
|
41155
|
+
return {
|
|
41156
|
+
porcentaje: porcentaje3,
|
|
41157
|
+
porcentaje_calculado: porcentajeCalculado,
|
|
41158
|
+
practicas: cargan.length,
|
|
41159
|
+
excluidas: items.length - dentro.length,
|
|
41160
|
+
revisar: dentro.length - cargan.length,
|
|
41161
|
+
total_actual: redondear2(cargan.reduce((a, i) => a + (i.valor_actual ?? 0), 0)),
|
|
41162
|
+
total_nuevo: redondear2(cargan.reduce((a, i) => a + (i.valor_nuevo ?? 0), 0)),
|
|
41163
|
+
tope,
|
|
41164
|
+
supera_tope: Math.abs(porcentaje3) > tope
|
|
41165
|
+
};
|
|
41166
|
+
}
|
|
41167
|
+
function textoIndices(detalle, desde, hasta) {
|
|
41168
|
+
if (!Array.isArray(detalle)) return "lo acordado en el convenio";
|
|
41169
|
+
const mes = (p) => (/* @__PURE__ */ new Date(`${p}-15T12:00:00Z`)).toLocaleDateString("es-AR", { month: "long", year: "numeric", timeZone: "UTC" });
|
|
41170
|
+
const pct = (n) => `${n.toLocaleString("es-AR", { maximumFractionDigits: 2 })} %`;
|
|
41171
|
+
const partes = detalle.map((d) => `${d.nombre} ${pct(d.variacion)}`);
|
|
41172
|
+
const pesos4 = detalle.length > 1 ? ` (ponderados ${detalle.map((d) => `${d.peso} %`).join(" y ")})` : "";
|
|
41173
|
+
const periodo = desde && hasta ? `, de ${mes(desde)} a ${mes(hasta)}` : "";
|
|
41174
|
+
return `${partes.join(" y ")}${pesos4}${periodo}`;
|
|
41175
|
+
}
|
|
41176
|
+
var NOTA_POR_DEFECTO = {
|
|
41177
|
+
asunto: "Actualizaci\xF3n de aranceles \xB7 {convenio} \xB7 {institucion}",
|
|
41178
|
+
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}"
|
|
41179
|
+
};
|
|
41180
|
+
function armarNota(plantilla, d) {
|
|
41181
|
+
const p = plantilla && typeof plantilla === "object" ? plantilla : {};
|
|
41182
|
+
const [a, m, dia5] = d.vigencia.split("-");
|
|
41183
|
+
const valores = {
|
|
41184
|
+
institucion: d.institucion,
|
|
41185
|
+
financiador: d.financiador,
|
|
41186
|
+
convenio: d.convenio,
|
|
41187
|
+
porcentaje: `${d.porcentaje.toLocaleString("es-AR", { maximumFractionDigits: 2 })} %`,
|
|
41188
|
+
indices: d.indices,
|
|
41189
|
+
vigencia: dia5 ? `${dia5}/${m}/${a}` : d.vigencia,
|
|
41190
|
+
firma: d.firma,
|
|
41191
|
+
practicas: String(d.practicas)
|
|
41192
|
+
};
|
|
41193
|
+
const llenar = (t6) => t6.replace(/\{(\w+)\}/g, (x, k) => valores[k] ?? x);
|
|
41194
|
+
const asunto = typeof p.asunto === "string" && p.asunto.trim() ? p.asunto : NOTA_POR_DEFECTO.asunto;
|
|
41195
|
+
const texto10 = typeof p.texto === "string" && p.texto.trim() ? p.texto : NOTA_POR_DEFECTO.texto;
|
|
41196
|
+
return { asunto: llenar(asunto).slice(0, 200), texto: llenar(texto10).slice(0, 5e3) };
|
|
41197
|
+
}
|
|
41198
|
+
|
|
41035
41199
|
// src/internacion.ts
|
|
41036
41200
|
var HORA_CORTE_POR_DEFECTO = "13:00";
|
|
41037
41201
|
var DIAS_SIN_ACTIVIDAD_POR_DEFECTO = 3;
|
|
@@ -41157,7 +41321,7 @@ function estadoInternacion(e) {
|
|
|
41157
41321
|
}
|
|
41158
41322
|
|
|
41159
41323
|
// src/honorarios.ts
|
|
41160
|
-
var
|
|
41324
|
+
var redondear3 = (n) => Math.round(n * 100) / 100;
|
|
41161
41325
|
var pesos2 = (n) => `$ ${n.toLocaleString("es-AR", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
41162
41326
|
var fecha3 = (iso5) => `${iso5.slice(8, 10)}/${iso5.slice(5, 7)}/${iso5.slice(0, 4)}`;
|
|
41163
41327
|
function texto2(v) {
|
|
@@ -41192,13 +41356,13 @@ function leerRegla(v) {
|
|
|
41192
41356
|
if (!rid) return null;
|
|
41193
41357
|
const prof = num3(r.prof);
|
|
41194
41358
|
const inst = num3(r.inst);
|
|
41195
|
-
return { id: rid, prof: prof === null ? null :
|
|
41359
|
+
return { id: rid, prof: prof === null ? null : redondear3(prof), inst: inst === null ? null : redondear3(inst) };
|
|
41196
41360
|
}
|
|
41197
41361
|
function honorarioDe(input2) {
|
|
41198
41362
|
const { regla, monto, cantidad: cantidad2 } = input2;
|
|
41199
41363
|
if (!regla) return 0;
|
|
41200
|
-
if (regla.prof !== null) return
|
|
41201
|
-
return
|
|
41364
|
+
if (regla.prof !== null) return redondear3(regla.prof * cantidad2);
|
|
41365
|
+
return redondear3(monto * cantidad2 * ((regla.inst ?? 0) / 100));
|
|
41202
41366
|
}
|
|
41203
41367
|
function motivoCero(regla, honorario, delHis = false) {
|
|
41204
41368
|
if (honorario > 0) return null;
|
|
@@ -41208,7 +41372,7 @@ function motivoCero(regla, honorario, delHis = false) {
|
|
|
41208
41372
|
function honorarioLeido(r, regla, monto, cantidad2) {
|
|
41209
41373
|
if (r.honorario === void 0 || r.honorario === null || r.honorario === "") return { honorario: honorarioDe({ regla, monto, cantidad: cantidad2 }), delHis: false };
|
|
41210
41374
|
const h = num3(r.honorario);
|
|
41211
|
-
return h === null || h < 0 ? null : { honorario:
|
|
41375
|
+
return h === null || h < 0 ? null : { honorario: redondear3(h), delHis: true };
|
|
41212
41376
|
}
|
|
41213
41377
|
function leerPrestaciones(v) {
|
|
41214
41378
|
if (!Array.isArray(v)) return { error: "prestaciones tiene que ser la lista que devuelve leer-honorarios.mjs" };
|
|
@@ -41225,7 +41389,7 @@ function leerPrestaciones(v) {
|
|
|
41225
41389
|
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
41390
|
const h = honorarioLeido(r, leerRegla(r.regla), monto, cantidad2 > 0 ? cantidad2 : 1);
|
|
41227
41391
|
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:
|
|
41392
|
+
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
41393
|
}
|
|
41230
41394
|
return { prestaciones: out };
|
|
41231
41395
|
}
|
|
@@ -41244,7 +41408,7 @@ function prestacionesEnCero(ps) {
|
|
|
41244
41408
|
prestaciones: items.length,
|
|
41245
41409
|
sin_regla: items.filter((p) => p.motivo === "sin_regla").length,
|
|
41246
41410
|
regla_sin_valor: items.filter((p) => p.motivo === "regla_sin_valor").length,
|
|
41247
|
-
monto:
|
|
41411
|
+
monto: redondear3(items.reduce((a, p) => a + p.monto * p.cantidad, 0)),
|
|
41248
41412
|
obras_sociales: [...new Set(items.map((p) => p.obra_social).filter((x) => Boolean(x)))].sort(),
|
|
41249
41413
|
desde: fechas[0] ?? null,
|
|
41250
41414
|
hasta: fechas[fechas.length - 1] ?? null
|
|
@@ -41270,7 +41434,7 @@ function leerDebitos(v) {
|
|
|
41270
41434
|
const hl = honorarioLeido(r, regla, montoItem, cantidad2);
|
|
41271
41435
|
if (!hl) return { error: `debitos[${i}].honorario: tiene que ser un n\xFAmero mayor o igual a 0` };
|
|
41272
41436
|
const hon = liquidada ? hl.honorario : null;
|
|
41273
|
-
const imp = liquidada ?
|
|
41437
|
+
const imp = liquidada ? redondear3(montoItem * cantidad2) : null;
|
|
41274
41438
|
out.push({
|
|
41275
41439
|
item_id: item,
|
|
41276
41440
|
bono_id: bono,
|
|
@@ -41278,13 +41442,13 @@ function leerDebitos(v) {
|
|
|
41278
41442
|
fecha_prestacion: dia(r.fecha_prestacion),
|
|
41279
41443
|
obra_social: texto2(r.obra_social)?.slice(0, 120) ?? null,
|
|
41280
41444
|
nota_credito: { id: ncId, numero: texto2(nc?.numero), fecha: dia(nc?.fecha), motivo: texto2(nc?.motivo)?.slice(0, 300) ?? null },
|
|
41281
|
-
monto_debitado:
|
|
41445
|
+
monto_debitado: redondear3(Math.abs(monto)),
|
|
41282
41446
|
liquidada,
|
|
41283
|
-
monto:
|
|
41447
|
+
monto: redondear3(montoItem),
|
|
41284
41448
|
cantidad: cantidad2 > 0 ? cantidad2 : 1,
|
|
41285
41449
|
regla,
|
|
41286
41450
|
liquidado: hon !== null && imp !== null ? { honorario: hon, importe: imp } : null,
|
|
41287
|
-
ya_trasladado:
|
|
41451
|
+
ya_trasladado: redondear3(Math.abs(num3(r.ya_trasladado) ?? 0))
|
|
41288
41452
|
});
|
|
41289
41453
|
}
|
|
41290
41454
|
return { debitos: out };
|
|
@@ -41293,12 +41457,12 @@ function evaluarDebito(d) {
|
|
|
41293
41457
|
if (!d.liquidado) return { ...d, estado: "sin_liquidar", a_descontar: 0 };
|
|
41294
41458
|
if (d.liquidado.importe <= 0 || d.liquidado.honorario <= 0 || d.monto_debitado <= 0) return { ...d, estado: "sin_monto", a_descontar: 0 };
|
|
41295
41459
|
const proporcional = Math.min(d.liquidado.honorario, d.liquidado.honorario * d.monto_debitado / d.liquidado.importe);
|
|
41296
|
-
const falta =
|
|
41460
|
+
const falta = redondear3(proporcional - d.ya_trasladado);
|
|
41297
41461
|
return falta >= 0.01 ? { ...d, estado: "a_trasladar", a_descontar: falta } : { ...d, estado: "ya_trasladado", a_descontar: 0 };
|
|
41298
41462
|
}
|
|
41299
41463
|
function resumenDebitos(ds) {
|
|
41300
41464
|
const a = ds.filter((d) => d.estado === "a_trasladar");
|
|
41301
|
-
return { a_trasladar: a.length, total:
|
|
41465
|
+
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
41466
|
}
|
|
41303
41467
|
function leerLineasLiquidacion(v) {
|
|
41304
41468
|
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 +41487,7 @@ function leerLineasLiquidacion(v) {
|
|
|
41323
41487
|
honorario += h;
|
|
41324
41488
|
if (h <= 0) enCero += 1;
|
|
41325
41489
|
}
|
|
41326
|
-
out.push({ bono_id: bono, fecha: dia(r.fecha), obra_social: texto2(r.obra_social)?.slice(0, 120) ?? null, importe:
|
|
41490
|
+
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
41491
|
}
|
|
41328
41492
|
return { lineas: out };
|
|
41329
41493
|
}
|
|
@@ -41333,14 +41497,14 @@ function resumirLiquidacion(lineas) {
|
|
|
41333
41497
|
const k = l.obra_social ?? "Particular / sin obra social";
|
|
41334
41498
|
const e = por.get(k) ?? { bonos: 0, honorario: 0 };
|
|
41335
41499
|
e.bonos += 1;
|
|
41336
|
-
e.honorario =
|
|
41500
|
+
e.honorario = redondear3(e.honorario + l.honorario);
|
|
41337
41501
|
por.set(k, e);
|
|
41338
41502
|
}
|
|
41339
41503
|
const fechas = lineas.map((l) => l.fecha).filter((x) => Boolean(x)).sort();
|
|
41340
41504
|
return {
|
|
41341
41505
|
bonos: lineas.length,
|
|
41342
|
-
honorario:
|
|
41343
|
-
importe:
|
|
41506
|
+
honorario: redondear3(lineas.reduce((a, l) => a + l.honorario, 0)),
|
|
41507
|
+
importe: redondear3(lineas.reduce((a, l) => a + l.importe, 0)),
|
|
41344
41508
|
en_cero: lineas.reduce((a, l) => a + l.en_cero, 0),
|
|
41345
41509
|
por_obra_social: [...por.entries()].map(([obra_social, e]) => ({ obra_social, ...e })).sort((a, b) => b.honorario - a.honorario),
|
|
41346
41510
|
desde: fechas[0] ?? null,
|
|
@@ -41376,7 +41540,7 @@ var ATRASO_EXCEPCION = 365;
|
|
|
41376
41540
|
var MAX_RECLAMOS_POR_DEFECTO = 2;
|
|
41377
41541
|
var DIAS_ENTRE_RECLAMOS_POR_DEFECTO = 15;
|
|
41378
41542
|
var GRACIA_COMPROMISO = 7;
|
|
41379
|
-
var
|
|
41543
|
+
var redondear4 = (n) => Math.round(n * 100) / 100;
|
|
41380
41544
|
var pesos3 = (n) => `$ ${n.toLocaleString("es-AR", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
41381
41545
|
var fecha4 = (iso5) => `${iso5.slice(8, 10)}/${iso5.slice(5, 7)}/${iso5.slice(0, 4)}`;
|
|
41382
41546
|
var normalizar = (s) => s.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/\s+/g, " ").trim();
|
|
@@ -41416,7 +41580,7 @@ function leerFacturas(v) {
|
|
|
41416
41580
|
vistas.add(id3);
|
|
41417
41581
|
if (!emision) return { error: `factura ${id3}: fecha_emision tiene que ser AAAA-MM-DD` };
|
|
41418
41582
|
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:
|
|
41583
|
+
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
41584
|
}
|
|
41421
41585
|
return { facturas: out };
|
|
41422
41586
|
}
|
|
@@ -41448,7 +41612,7 @@ function evaluarFacturas(input2) {
|
|
|
41448
41612
|
};
|
|
41449
41613
|
});
|
|
41450
41614
|
const reclamadas = items.filter((i) => i.estado === "reclamada").sort((a, b) => b.dias_atraso - a.dias_atraso);
|
|
41451
|
-
const total =
|
|
41615
|
+
const total = redondear4(reclamadas.reduce((s, i) => s + i.saldo_pesos, 0));
|
|
41452
41616
|
return {
|
|
41453
41617
|
items: [...reclamadas, ...items.filter((i) => i.estado !== "reclamada")],
|
|
41454
41618
|
resumen: {
|
|
@@ -41467,7 +41631,7 @@ function evaluarFacturas(input2) {
|
|
|
41467
41631
|
}
|
|
41468
41632
|
function redactarReclamo(input2) {
|
|
41469
41633
|
const reclamadas = input2.items.filter((i) => i.estado === "reclamada" && i.cobro !== "cobrada");
|
|
41470
|
-
const total =
|
|
41634
|
+
const total = redondear4(reclamadas.reduce((s, i) => s + i.saldo_pesos, 0));
|
|
41471
41635
|
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
41636
|
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
41637
|
const cuerpo = [
|
|
@@ -41493,7 +41657,7 @@ function leerSaldos(v) {
|
|
|
41493
41657
|
const id3 = texto3(r.factura_id);
|
|
41494
41658
|
const saldo = num4(r.saldo_pesos);
|
|
41495
41659
|
if (!id3 || saldo === null) return { error: `leidos[${i}]: hace falta factura_id y saldo_pesos` };
|
|
41496
|
-
saldos.set(id3, { saldo:
|
|
41660
|
+
saldos.set(id3, { saldo: redondear4(saldo), notasCredito: redondear4(num4(r.notas_credito_pesos) ?? 0), recibos: redondear4(num4(r.recibos_aplicados_pesos) ?? 0) });
|
|
41497
41661
|
}
|
|
41498
41662
|
return { saldos };
|
|
41499
41663
|
}
|
|
@@ -41509,14 +41673,14 @@ function compararSaldos(items, resumen, leidos, notasCreditoAntes = {}) {
|
|
|
41509
41673
|
return i;
|
|
41510
41674
|
}
|
|
41511
41675
|
const saldo = Math.max(0, s.saldo);
|
|
41512
|
-
const nc = Math.max(0,
|
|
41676
|
+
const nc = Math.max(0, redondear4(s.notasCredito - (notasCreditoAntes[i.factura_id] ?? 0)));
|
|
41513
41677
|
const cobro = saldo <= SALDO_MINIMO ? "cobrada" : saldo < i.saldo_inicial - SALDO_MINIMO ? "parcial" : "pendiente";
|
|
41514
41678
|
return { ...i, saldo_pesos: saldo, cobro, notas_credito_nuevas: nc };
|
|
41515
41679
|
});
|
|
41516
41680
|
const reclamadas = nuevos.filter((i) => i.estado === "reclamada");
|
|
41517
|
-
const pendiente =
|
|
41518
|
-
const debitos =
|
|
41519
|
-
const cobrado =
|
|
41681
|
+
const pendiente = redondear4(reclamadas.reduce((s, i) => s + (i.cobro === "cobrada" ? 0 : i.saldo_pesos), 0));
|
|
41682
|
+
const debitos = redondear4(reclamadas.reduce((s, i) => s + i.notas_credito_nuevas, 0));
|
|
41683
|
+
const cobrado = redondear4(Math.max(0, resumen.total_reclamado - pendiente - debitos));
|
|
41520
41684
|
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
41685
|
}
|
|
41522
41686
|
var RESPUESTAS_FINANCIADOR = ["paga", "observa", "no_responde"];
|
|
@@ -42614,6 +42778,209 @@ var circularAranceles = {
|
|
|
42614
42778
|
}
|
|
42615
42779
|
]
|
|
42616
42780
|
};
|
|
42781
|
+
var RESPUESTAS_AJUSTE = ["aprobado", "aprobado_otro", "rechazado"];
|
|
42782
|
+
var CANALES_NOTA = ["correo", "portal_del_financiador", "nota", "otro"];
|
|
42783
|
+
function calcularAjuste(caso, datos, contexto) {
|
|
42784
|
+
const formula = contexto.formulaAjuste ?? FORMULA_POR_DEFECTO;
|
|
42785
|
+
if ("error" in formula) return { ok: false, error: `la f\xF3rmula de la instituci\xF3n est\xE1 mal configurada: ${formula.error}` };
|
|
42786
|
+
const desde = texto4(caso.datos.ultimo_ajuste);
|
|
42787
|
+
if (!desde || !periodoValido(desde)) return { ok: false, error: "el caso no tiene el mes del \xFAltimo ajuste (ultimo_ajuste: AAAA-MM)" };
|
|
42788
|
+
let indices = [];
|
|
42789
|
+
if (!("fijo" in formula)) {
|
|
42790
|
+
const l = leerIndices(datos.indices);
|
|
42791
|
+
if ("error" in l) return { ok: false, error: l.error };
|
|
42792
|
+
indices = l.indices;
|
|
42793
|
+
}
|
|
42794
|
+
const mesActual = (contexto.hoy ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)).slice(0, 7);
|
|
42795
|
+
const p = porcentajeSegunFormula(formula, indices, desde, mesActual);
|
|
42796
|
+
if ("error" in p) return { ok: false, error: p.error };
|
|
42797
|
+
const a = armarAjuste(datos.actual, p.porcentaje);
|
|
42798
|
+
if (!a.ok) return { ok: false, error: a.error };
|
|
42799
|
+
return { ok: true, porcentaje: p.porcentaje, detalle: p.detalle, hasta: p.hasta, items: a.items };
|
|
42800
|
+
}
|
|
42801
|
+
function resumenAjusteDe(caso) {
|
|
42802
|
+
const r = caso.datos.resumen;
|
|
42803
|
+
return r && typeof r === "object" ? r : null;
|
|
42804
|
+
}
|
|
42805
|
+
function validarPorcentajeHumano(v, regla) {
|
|
42806
|
+
const n = num5(v);
|
|
42807
|
+
return n !== null && porcentajeValido(n) ? ok(regla, `${n} %`) : falla(regla, "porcentaje: un n\xFAmero entre -50 y 100");
|
|
42808
|
+
}
|
|
42809
|
+
var ajusteConvenio = {
|
|
42810
|
+
clave: "ajuste_convenio",
|
|
42811
|
+
nombre: "Ajuste de convenios por \xEDndice",
|
|
42812
|
+
puesto: "Convenios y facturaci\xF3n",
|
|
42813
|
+
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.",
|
|
42814
|
+
inicial: "abierto",
|
|
42815
|
+
estados: ["abierto", "calculado", "pendiente_aprobacion", "por_enviar", "enviado", "acordado", "aplicado", "verificado", "con_diferencias", "rechazado", "rechazado_financiador", "cancelado"],
|
|
42816
|
+
finalesOk: ["verificado"],
|
|
42817
|
+
finalesNoOk: ["rechazado", "rechazado_financiador", "cancelado"],
|
|
42818
|
+
minutosAbsorbidos: 40,
|
|
42819
|
+
// Buscar los índices, calcular, armar la nota y comprobar cada valor (tope: una jornada).
|
|
42820
|
+
minutosSegun: (caso) => Math.min(480, 40 + 2 * cantidad(caso.datos.items)),
|
|
42821
|
+
transiciones: [
|
|
42822
|
+
{
|
|
42823
|
+
via: "calcular",
|
|
42824
|
+
de: ["abierto", "calculado"],
|
|
42825
|
+
a: "calculado",
|
|
42826
|
+
actor: ["agente", "humano"],
|
|
42827
|
+
nivelRequerido: "observa",
|
|
42828
|
+
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.",
|
|
42829
|
+
validar: (caso, datos, contexto) => {
|
|
42830
|
+
const c = calcularAjuste(caso, datos, contexto);
|
|
42831
|
+
const fijo = contexto.formulaAjuste && "fijo" in contexto.formulaAjuste;
|
|
42832
|
+
return [
|
|
42833
|
+
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"),
|
|
42834
|
+
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"),
|
|
42835
|
+
c.ok ? ok("calculo_ajuste", `${c.porcentaje} % sobre ${c.items.length} pr\xE1cticas`) : falla("calculo_ajuste", c.error)
|
|
42836
|
+
];
|
|
42837
|
+
}
|
|
42838
|
+
},
|
|
42839
|
+
{
|
|
42840
|
+
via: "proponer",
|
|
42841
|
+
de: ["calculado"],
|
|
42842
|
+
a: "pendiente_aprobacion",
|
|
42843
|
+
actor: ["agente", "humano"],
|
|
42844
|
+
nivelRequerido: "propone",
|
|
42845
|
+
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.",
|
|
42846
|
+
validar: (caso) => {
|
|
42847
|
+
const r = resumenAjusteDe(caso);
|
|
42848
|
+
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")];
|
|
42849
|
+
}
|
|
42850
|
+
},
|
|
42851
|
+
{
|
|
42852
|
+
via: "ajustar",
|
|
42853
|
+
de: ["pendiente_aprobacion"],
|
|
42854
|
+
a: "pendiente_aprobacion",
|
|
42855
|
+
actor: ["humano"],
|
|
42856
|
+
descripcion: "La direcci\xF3n cambia el porcentaje (porcentaje, con motivo) o deja pr\xE1cticas afuera (excluir: [c\xF3digos]); el plugin recalcula los valores.",
|
|
42857
|
+
validar: (caso, datos) => {
|
|
42858
|
+
const out = [];
|
|
42859
|
+
if (datos.porcentaje === void 0 && datos.excluir === void 0) return [falla("cambio_presente", "ajustar necesita porcentaje o excluir")];
|
|
42860
|
+
if (datos.porcentaje !== void 0) {
|
|
42861
|
+
out.push(validarPorcentajeHumano(datos.porcentaje, "porcentaje_valido"));
|
|
42862
|
+
out.push(texto4(datos.motivo) ? ok("motivo_presente") : falla("motivo_presente", "cambiar el porcentaje calculado requiere motivo"));
|
|
42863
|
+
}
|
|
42864
|
+
if (datos.excluir !== void 0) {
|
|
42865
|
+
if (!Array.isArray(datos.excluir)) out.push(falla("excluir_valido", "excluir tiene que ser una lista de c\xF3digos"));
|
|
42866
|
+
else {
|
|
42867
|
+
const items = leerItems(caso.datos.items);
|
|
42868
|
+
const codigos = new Set(items.map((i) => i.codigo));
|
|
42869
|
+
const ajenos = datos.excluir.map(normalizarCodigo).filter((c) => !codigos.has(c));
|
|
42870
|
+
out.push(ajenos.length === 0 ? ok("excluir_valido") : falla("excluir_valido", `c\xF3digos que no est\xE1n en el convenio: ${ajenos.join(", ")}`));
|
|
42871
|
+
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"));
|
|
42872
|
+
}
|
|
42873
|
+
}
|
|
42874
|
+
return out;
|
|
42875
|
+
}
|
|
42876
|
+
},
|
|
42877
|
+
{
|
|
42878
|
+
via: "aprobar",
|
|
42879
|
+
de: ["pendiente_aprobacion"],
|
|
42880
|
+
a: "por_enviar",
|
|
42881
|
+
actor: ["humano"],
|
|
42882
|
+
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).",
|
|
42883
|
+
validar: (caso, datos) => {
|
|
42884
|
+
const r = resumenAjusteDe(caso);
|
|
42885
|
+
return [
|
|
42886
|
+
r && r.practicas > 0 ? ok("hay_que_cargar", `${r.practicas} pr\xE1cticas`) : falla("hay_que_cargar", "no hay ninguna pr\xE1ctica para ajustar"),
|
|
42887
|
+
!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`)
|
|
42888
|
+
];
|
|
42889
|
+
}
|
|
42890
|
+
},
|
|
42891
|
+
{
|
|
42892
|
+
via: "rechazar",
|
|
42893
|
+
de: ["pendiente_aprobacion"],
|
|
42894
|
+
a: "rechazado",
|
|
42895
|
+
actor: ["humano"],
|
|
42896
|
+
descripcion: "La direcci\xF3n no hace el ajuste; con motivo.",
|
|
42897
|
+
validar: (_caso, datos) => [texto4(datos.motivo) ? ok("motivo_presente") : falla("motivo_presente", "el rechazo requiere motivo")]
|
|
42898
|
+
},
|
|
42899
|
+
{
|
|
42900
|
+
via: "enviar",
|
|
42901
|
+
de: ["por_enviar"],
|
|
42902
|
+
a: "enviado",
|
|
42903
|
+
actor: ["humano", "agente"],
|
|
42904
|
+
nivelRequerido: "ejecuta",
|
|
42905
|
+
descripcion: "Registrar que la nota se mand\xF3 al financiador: por d\xF3nde (canal) y a qui\xE9n (destinatario).",
|
|
42906
|
+
validar: (_caso, datos) => [
|
|
42907
|
+
CANALES_NOTA.includes(datos.canal) ? ok("canal_valido") : falla("canal_valido", `canal: ${CANALES_NOTA.join(", ")}`),
|
|
42908
|
+
texto4(datos.destinatario) ? ok("destinatario_presente") : falla("destinatario_presente", "destinatario (correo, \xE1rea o persona del financiador) es obligatorio")
|
|
42909
|
+
]
|
|
42910
|
+
},
|
|
42911
|
+
{
|
|
42912
|
+
via: "responder",
|
|
42913
|
+
de: ["enviado"],
|
|
42914
|
+
a: (_caso, datos) => datos.respuesta === "rechazado" ? "rechazado_financiador" : "acordado",
|
|
42915
|
+
actor: ["humano"],
|
|
42916
|
+
descripcion: "Registrar la respuesta del financiador: aprobado, aprobado con otro porcentaje (porcentaje) o rechazado (motivo).",
|
|
42917
|
+
validar: (_caso, datos) => {
|
|
42918
|
+
const r = datos.respuesta;
|
|
42919
|
+
if (!RESPUESTAS_AJUSTE.includes(r)) return [falla("respuesta_valida", `respuesta: ${RESPUESTAS_AJUSTE.join(", ")}`)];
|
|
42920
|
+
if (r === "aprobado_otro") return [ok("respuesta_valida"), validarPorcentajeHumano(datos.porcentaje, "porcentaje_valido")];
|
|
42921
|
+
if (r === "rechazado") return [ok("respuesta_valida"), texto4(datos.motivo) ? ok("motivo_presente") : falla("motivo_presente", "anotar qu\xE9 respondi\xF3 el financiador")];
|
|
42922
|
+
return [ok("respuesta_valida")];
|
|
42923
|
+
}
|
|
42924
|
+
},
|
|
42925
|
+
{
|
|
42926
|
+
via: "aplicar",
|
|
42927
|
+
de: ["acordado"],
|
|
42928
|
+
a: "aplicado",
|
|
42929
|
+
actor: ["agente", "humano"],
|
|
42930
|
+
nivelRequerido: "ejecuta",
|
|
42931
|
+
cargaManual: true,
|
|
42932
|
+
descripcion: "Cargar en el HIS el archivo que gener\xF3 el plugin (Actualizaci\xF3n con archivo) y registrarlo, con el hash del archivo cargado.",
|
|
42933
|
+
validar: (caso, datos) => {
|
|
42934
|
+
const esperado = texto4(caso.datos.archivo_csv?.hash);
|
|
42935
|
+
const informado = texto4(datos.archivo_hash);
|
|
42936
|
+
return [
|
|
42937
|
+
texto4(datos.accion_id) ? ok("accion_registrada") : falla("accion_registrada", "aplicar requiere accion_id de la carga registrada"),
|
|
42938
|
+
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")
|
|
42939
|
+
];
|
|
42940
|
+
}
|
|
42941
|
+
},
|
|
42942
|
+
{
|
|
42943
|
+
via: "verificar",
|
|
42944
|
+
de: ["aplicado"],
|
|
42945
|
+
a: (caso, datos) => {
|
|
42946
|
+
const r = compararRelectura(itemsACargar(leerItems(caso.datos.items), caso.datos.excluir), datos.leidos);
|
|
42947
|
+
return "error" in r || r.diferencias.length > 0 ? "con_diferencias" : "verificado";
|
|
42948
|
+
},
|
|
42949
|
+
actor: ["agente", "humano", "sistema"],
|
|
42950
|
+
nivelRequerido: "observa",
|
|
42951
|
+
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.",
|
|
42952
|
+
validar: (_caso, datos) => {
|
|
42953
|
+
const r = compararRelectura([], datos.leidos);
|
|
42954
|
+
return [
|
|
42955
|
+
texto4(datos.lectura_id) ? ok("lectura_registrada") : falla("lectura_registrada", "verificar requiere lectura_id: la relectura del convenio registrada"),
|
|
42956
|
+
"error" in r ? falla("leidos_validos", r.error) : ok("leidos_validos")
|
|
42957
|
+
];
|
|
42958
|
+
}
|
|
42959
|
+
},
|
|
42960
|
+
{
|
|
42961
|
+
via: "corregir",
|
|
42962
|
+
de: ["con_diferencias"],
|
|
42963
|
+
a: "aplicado",
|
|
42964
|
+
actor: ["humano"],
|
|
42965
|
+
cargaManual: true,
|
|
42966
|
+
descripcion: "Corregir en el HIS las pr\xE1cticas que no coincidieron y volver a comprobar.",
|
|
42967
|
+
validar: (_caso, datos) => [texto4(datos.accion_id) ? ok("accion_registrada") : falla("accion_registrada", "corregir requiere accion_id de la correcci\xF3n registrada")]
|
|
42968
|
+
},
|
|
42969
|
+
{
|
|
42970
|
+
via: "cancelar",
|
|
42971
|
+
de: ["abierto", "calculado", "pendiente_aprobacion", "por_enviar", "enviado", "acordado", "con_diferencias"],
|
|
42972
|
+
a: "cancelado",
|
|
42973
|
+
actor: ["humano", "agente"],
|
|
42974
|
+
nivelRequerido: "observa",
|
|
42975
|
+
descripcion: "Cancelar el ajuste (repetido, el convenio cambi\xF3 por otra v\xEDa, se renegoci\xF3).",
|
|
42976
|
+
validar: (_caso, datos) => [texto4(datos.motivo) ? ok("motivo_presente") : falla("motivo_presente", "la cancelaci\xF3n requiere motivo")]
|
|
42977
|
+
}
|
|
42978
|
+
]
|
|
42979
|
+
};
|
|
42980
|
+
function leerTopeAjuste(v) {
|
|
42981
|
+
const n = num5(v);
|
|
42982
|
+
return n !== null && n >= 1 && n <= 100 ? Math.round(n) : TOPE_AJUSTE_POR_DEFECTO;
|
|
42983
|
+
}
|
|
42617
42984
|
var CANALES_RECLAMO = ["correo", "portal_del_financiador", "nota", "telefono", "otro"];
|
|
42618
42985
|
function resumenCobranzaDe(caso) {
|
|
42619
42986
|
const r = caso.datos.resumen;
|
|
@@ -43296,6 +43663,7 @@ var WORKFLOWS = {
|
|
|
43296
43663
|
[credencialProfesional.clave]: credencialProfesional,
|
|
43297
43664
|
[demandaPendiente.clave]: demandaPendiente,
|
|
43298
43665
|
[circularAranceles.clave]: circularAranceles,
|
|
43666
|
+
[ajusteConvenio.clave]: ajusteConvenio,
|
|
43299
43667
|
[cobranzaFinanciador.clave]: cobranzaFinanciador,
|
|
43300
43668
|
[internacionAlDia.clave]: internacionAlDia,
|
|
43301
43669
|
[honorarioEnCero.clave]: honorarioEnCero,
|
|
@@ -43795,7 +44163,7 @@ async function informarPruebaRegla(ctx, input2) {
|
|
|
43795
44163
|
// src/rentabilidad.ts
|
|
43796
44164
|
var UMBRAL_MARGEN_POR_DEFECTO = 15;
|
|
43797
44165
|
var MESES_PRECIO_POR_DEFECTO = 6;
|
|
43798
|
-
var
|
|
44166
|
+
var redondear5 = (n) => Math.round(n * 100) / 100;
|
|
43799
44167
|
function texto5(v) {
|
|
43800
44168
|
return typeof v === "string" && v.trim() ? v.trim() : typeof v === "number" && Number.isFinite(v) ? String(v) : null;
|
|
43801
44169
|
}
|
|
@@ -43824,7 +44192,7 @@ function leerLecturaRentabilidad(v) {
|
|
|
43824
44192
|
const x = c ?? {};
|
|
43825
44193
|
const monto = num6(x.monto);
|
|
43826
44194
|
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:
|
|
44195
|
+
componentes.push({ arancel: texto5(x.arancel) ?? "", monto: redondear5(monto) });
|
|
43828
44196
|
}
|
|
43829
44197
|
const insumos = [];
|
|
43830
44198
|
for (const [i, c] of r.insumos.entries()) {
|
|
@@ -43832,7 +44200,7 @@ function leerLecturaRentabilidad(v) {
|
|
|
43832
44200
|
const cantidad2 = num6(x.cantidad);
|
|
43833
44201
|
if (cantidad2 === null || cantidad2 <= 0) return { error: `lectura.insumos[${i}].cantidad: tiene que ser un n\xFAmero > 0` };
|
|
43834
44202
|
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 :
|
|
44203
|
+
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
44204
|
}
|
|
43837
44205
|
const honorarios = [];
|
|
43838
44206
|
for (const [i, c] of r.honorarios.entries()) {
|
|
@@ -43870,7 +44238,7 @@ function evaluarRentabilidad(input2) {
|
|
|
43870
44238
|
const faltas = [];
|
|
43871
44239
|
const avisos = [];
|
|
43872
44240
|
const insumosARevisar = [];
|
|
43873
|
-
const valor =
|
|
44241
|
+
const valor = redondear5(l.componentes.reduce((a, c) => a + c.monto, 0));
|
|
43874
44242
|
if (valor <= 0) faltas.push("sin_valor_convenio");
|
|
43875
44243
|
let insumos = 0;
|
|
43876
44244
|
for (const i of l.insumos) {
|
|
@@ -43885,7 +44253,7 @@ function evaluarRentabilidad(input2) {
|
|
|
43885
44253
|
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
44254
|
}
|
|
43887
44255
|
}
|
|
43888
|
-
insumos =
|
|
44256
|
+
insumos = redondear5(insumos);
|
|
43889
44257
|
if (l.insumos.length === 0) avisos.push("sin_insumos");
|
|
43890
44258
|
const debajo = [];
|
|
43891
44259
|
const honorarios = [];
|
|
@@ -43909,10 +44277,10 @@ function evaluarRentabilidad(input2) {
|
|
|
43909
44277
|
}
|
|
43910
44278
|
const honorarioMin = honorarios.length ? Math.min(...honorarios) : null;
|
|
43911
44279
|
const honorarioMax = honorarios.length ? Math.max(...honorarios) : null;
|
|
43912
|
-
const margenPeor = honorarioMax !== null && valor > 0 ?
|
|
43913
|
-
const margenMejor = honorarioMin !== null && valor > 0 ?
|
|
44280
|
+
const margenPeor = honorarioMax !== null && valor > 0 ? redondear5(valor - insumos - honorarioMax) : null;
|
|
44281
|
+
const margenMejor = honorarioMin !== null && valor > 0 ? redondear5(valor - insumos - honorarioMin) : null;
|
|
43914
44282
|
const margenPct = margenPeor !== null && valor > 0 ? Math.round(margenPeor / valor * 1e3) / 10 : null;
|
|
43915
|
-
const impacto = margenPeor !== null ?
|
|
44283
|
+
const impacto = margenPeor !== null ? redondear5(margenPeor * l.volumen_90d / 3) : null;
|
|
43916
44284
|
let clase2;
|
|
43917
44285
|
if (faltas.length > 0 || margenPct === null) clase2 = "datos_incompletos";
|
|
43918
44286
|
else if (margenPeor < 0) clase2 = "perdida";
|
|
@@ -44050,7 +44418,7 @@ var num7 = (v) => {
|
|
|
44050
44418
|
const n = typeof v === "number" ? v : typeof v === "string" && v.trim() ? Number(v) : NaN;
|
|
44051
44419
|
return Number.isFinite(n) ? n : null;
|
|
44052
44420
|
};
|
|
44053
|
-
var
|
|
44421
|
+
var redondear6 = (n) => Math.round(n * 100) / 100;
|
|
44054
44422
|
var fechaHora2 = (v) => {
|
|
44055
44423
|
const s = texto7(v);
|
|
44056
44424
|
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 +44449,7 @@ function leerLecturaCostoInternacion(v) {
|
|
|
44081
44449
|
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
44450
|
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
44451
|
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:
|
|
44452
|
+
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
44453
|
}
|
|
44086
44454
|
const consumos = [];
|
|
44087
44455
|
for (const [i, x] of r.consumos.entries()) {
|
|
@@ -44167,7 +44535,7 @@ function evaluarCostoInternacion(input2) {
|
|
|
44167
44535
|
const umbral = input2.umbral ?? UMBRAL_MARGEN_POR_DEFECTO;
|
|
44168
44536
|
const faltas = [];
|
|
44169
44537
|
const avisos = [];
|
|
44170
|
-
const facturado =
|
|
44538
|
+
const facturado = redondear6(l.facturado.reduce((a, f) => a + f.monto * f.cantidad, 0));
|
|
44171
44539
|
if (facturado <= 0) faltas.push("sin_facturar");
|
|
44172
44540
|
if (!l.egreso) avisos.push("en_curso");
|
|
44173
44541
|
let honorarios = 0;
|
|
@@ -44177,7 +44545,7 @@ function evaluarCostoInternacion(input2) {
|
|
|
44177
44545
|
if (!f.regla || f.regla.prof === null && f.regla.inst === null) sinRegla += 1;
|
|
44178
44546
|
honorarios += honorarioDe({ regla: f.regla, monto: f.monto, cantidad: f.cantidad });
|
|
44179
44547
|
}
|
|
44180
|
-
honorarios =
|
|
44548
|
+
honorarios = redondear6(honorarios);
|
|
44181
44549
|
if (sinRegla > 0) avisos.push("prestaciones_sin_regla");
|
|
44182
44550
|
let insumos = 0;
|
|
44183
44551
|
const sinCosto = [];
|
|
@@ -44190,7 +44558,7 @@ function evaluarCostoInternacion(input2) {
|
|
|
44190
44558
|
}
|
|
44191
44559
|
insumos += cantidad2 * c.costo_unitario;
|
|
44192
44560
|
}
|
|
44193
|
-
insumos =
|
|
44561
|
+
insumos = redondear6(insumos);
|
|
44194
44562
|
if (sinCosto.length) faltas.push("insumo_sin_costo");
|
|
44195
44563
|
const { tramos, total, exactos } = diasPorServicio(l, hoy);
|
|
44196
44564
|
const porServicio = input2.costoDiaPorServicio ?? {};
|
|
@@ -44205,18 +44573,18 @@ function evaluarCostoInternacion(input2) {
|
|
|
44205
44573
|
else cama += (exactos.get(servicio) ?? dias) * costo_dia;
|
|
44206
44574
|
return { servicio, dias, costo_dia: costo_dia ?? null };
|
|
44207
44575
|
});
|
|
44208
|
-
cama =
|
|
44576
|
+
cama = redondear6(cama);
|
|
44209
44577
|
if (faltaCama) faltas.push("sin_costo_cama");
|
|
44210
44578
|
const minutos = l.cirugias.reduce((a, c) => a + (c.minutos ?? 0), 0);
|
|
44211
44579
|
let quirofano = 0;
|
|
44212
44580
|
if (l.cirugias.length > 0) {
|
|
44213
44581
|
if (input2.costoHoraQuirofano === null || input2.costoHoraQuirofano === void 0) faltas.push("sin_costo_quirofano");
|
|
44214
|
-
else quirofano =
|
|
44582
|
+
else quirofano = redondear6(minutos / 60 * input2.costoHoraQuirofano);
|
|
44215
44583
|
}
|
|
44216
44584
|
if (l.cirugias.some((c) => c.minutos === null)) faltas.push("cirugia_sin_duracion");
|
|
44217
|
-
const costo =
|
|
44585
|
+
const costo = redondear6(honorarios + insumos + cama + quirofano);
|
|
44218
44586
|
const calculable = faltas.length === 0 && facturado > 0;
|
|
44219
|
-
const margen = calculable ?
|
|
44587
|
+
const margen = calculable ? redondear6(facturado - costo) : null;
|
|
44220
44588
|
const margen_pct = margen !== null ? Math.round(margen / facturado * 1e3) / 10 : null;
|
|
44221
44589
|
let clase2;
|
|
44222
44590
|
if (margen === null || margen_pct === null) clase2 = "datos_incompletos";
|
|
@@ -44383,6 +44751,13 @@ function describirOpciones(antes, despues) {
|
|
|
44383
44751
|
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
44752
|
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
44753
|
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)} %`);
|
|
44754
|
+
if (JSON.stringify(antes.formula) !== JSON.stringify(despues.formula) && despues.formula !== void 0) {
|
|
44755
|
+
const f = despues.formula;
|
|
44756
|
+
partes.push(`f\xF3rmula del ajuste: ${f.fijo !== void 0 ? `${f.fijo} % fijo` : (f.componentes ?? []).map((c) => `${c.serie.replace(/_/g, " ")} ${c.peso} %`).join(" + ")}`);
|
|
44757
|
+
}
|
|
44758
|
+
if (antes.tope_ajuste !== despues.tope_ajuste && despues.tope_ajuste !== void 0) partes.push(`tope del ajuste por \xEDndice: ${String(despues.tope_ajuste)} %`);
|
|
44759
|
+
if (JSON.stringify(antes.nota) !== JSON.stringify(despues.nota) && despues.nota !== void 0) partes.push("texto de la nota al financiador");
|
|
44760
|
+
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
44761
|
if (antes.umbral_margen !== despues.umbral_margen && despues.umbral_margen !== void 0) partes.push(`margen m\xEDnimo: ${String(despues.umbral_margen)} %`);
|
|
44387
44762
|
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
44763
|
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 +44855,23 @@ async function cambiarPolitica(ctx, input2) {
|
|
|
44480
44855
|
if (v === null) throw new OpsError("validacion", "dias_sin_actividad: tiene que ser un n\xFAmero entero entre 1 y 15");
|
|
44481
44856
|
opciones.dias_sin_actividad = v;
|
|
44482
44857
|
}
|
|
44858
|
+
if (input2.workflow === "ajuste_convenio") {
|
|
44859
|
+
if (opciones.formula !== void 0) {
|
|
44860
|
+
const f = leerFormula(opciones.formula);
|
|
44861
|
+
if ("error" in f) throw new OpsError("validacion", `formula: ${f.error}`);
|
|
44862
|
+
opciones.formula = f;
|
|
44863
|
+
}
|
|
44864
|
+
if (opciones.tope_ajuste !== void 0) {
|
|
44865
|
+
const v = enteroEntre(opciones.tope_ajuste, LIMITES_TOPE_AJUSTE[0], LIMITES_TOPE_AJUSTE[1]);
|
|
44866
|
+
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]}`);
|
|
44867
|
+
opciones.tope_ajuste = v;
|
|
44868
|
+
}
|
|
44869
|
+
if (opciones.nota !== void 0) {
|
|
44870
|
+
const n = opciones.nota && typeof opciones.nota === "object" ? opciones.nota : {};
|
|
44871
|
+
opciones.nota = { asunto: str2(n.asunto)?.slice(0, 200) ?? null, texto: str2(n.texto)?.slice(0, 5e3) ?? null };
|
|
44872
|
+
}
|
|
44873
|
+
for (const k of ["firma", "institucion"]) if (opciones[k] !== void 0) opciones[k] = str2(opciones[k])?.slice(0, 200) ?? null;
|
|
44874
|
+
}
|
|
44483
44875
|
if (opciones.tope_variacion !== void 0) {
|
|
44484
44876
|
const tope = leerTope(opciones.tope_variacion);
|
|
44485
44877
|
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 +45059,15 @@ async function abrirCaso(ctx, input2) {
|
|
|
44667
45059
|
if (faltan.length) throw new OpsError("validacion", `circular_aranceles necesita ${faltan.join(", ")}`);
|
|
44668
45060
|
if (!/^\d{4}-\d{2}-\d{2}$/.test(String(d.vigencia_desde))) throw new OpsError("validacion", "vigencia_desde: AAAA-MM-DD");
|
|
44669
45061
|
}
|
|
45062
|
+
if (wf.clave === "ajuste_convenio") {
|
|
45063
|
+
const d = input2.datos ?? {};
|
|
45064
|
+
const faltan = ["financiador", "convenio", "vigencia_desde", "ultimo_ajuste"].filter((k) => !str2(d[k]));
|
|
45065
|
+
if (faltan.length) throw new OpsError("validacion", `ajuste_convenio necesita ${faltan.join(", ")}`);
|
|
45066
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(String(d.vigencia_desde))) throw new OpsError("validacion", "vigencia_desde: AAAA-MM-DD");
|
|
45067
|
+
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");
|
|
45068
|
+
const esperada = `ajuste:${(normalizarCodigo(d.convenio) || String(d.convenio).trim()).toLowerCase()}:${String(d.vigencia_desde).slice(0, 7)}`;
|
|
45069
|
+
if (referencia !== esperada) throw new OpsError("referencia_requerida", `en ajuste_convenio la referencia es obligatoria: ${esperada}`);
|
|
45070
|
+
}
|
|
44670
45071
|
const extra = {};
|
|
44671
45072
|
if (wf.clave === "honorario_en_cero" || wf.clave === "debito_profesional" || wf.clave === "liquidacion_profesional") {
|
|
44672
45073
|
const p = leerProfesional(input2.datos?.profesional) ?? leerProfesional({ id: input2.datos?.profesional_id, nombre: input2.datos?.profesional });
|
|
@@ -44781,6 +45182,12 @@ async function contextoDe(ctx, companyId, caso, via, datos) {
|
|
|
44781
45182
|
contexto.topeVariacion = leerTope((await obtenerOpciones(ctx, companyId, caso.workflow)).tope_variacion);
|
|
44782
45183
|
return contexto;
|
|
44783
45184
|
}
|
|
45185
|
+
if (caso.workflow === "ajuste_convenio") {
|
|
45186
|
+
const op = await obtenerOpciones(ctx, companyId, caso.workflow);
|
|
45187
|
+
contexto.formulaAjuste = leerFormula(op.formula);
|
|
45188
|
+
contexto.topeAjuste = leerTopeAjuste(op.tope_ajuste);
|
|
45189
|
+
return contexto;
|
|
45190
|
+
}
|
|
44784
45191
|
if (caso.workflow === "rentabilidad_practica") {
|
|
44785
45192
|
if (via === "evaluar" || via === "comprobar") {
|
|
44786
45193
|
const op = await obtenerOpciones(ctx, companyId, caso.workflow);
|
|
@@ -44890,7 +45297,7 @@ async function transicionar(ctx, input2) {
|
|
|
44890
45297
|
);
|
|
44891
45298
|
const lectura = rows[0];
|
|
44892
45299
|
let vieja = false;
|
|
44893
|
-
if (lectura && (resultado.via === "comprobar" || caso.workflow === "circular_aranceles" && resultado.via === "verificar")) {
|
|
45300
|
+
if (lectura && (resultado.via === "comprobar" || (caso.workflow === "circular_aranceles" || caso.workflow === "ajuste_convenio") && resultado.via === "verificar")) {
|
|
44894
45301
|
const ultima = await ctx.db.query(
|
|
44895
45302
|
`SELECT max(created_at) AS ultima FROM ${t3(ctx, "transicion")} WHERE company_id = $1 AND caso_id = $2`,
|
|
44896
45303
|
[input2.companyId, caso.id]
|
|
@@ -44945,6 +45352,80 @@ async function transicionar(ctx, input2) {
|
|
|
44945
45352
|
datos.verificada_el = contexto.hoy ?? null;
|
|
44946
45353
|
}
|
|
44947
45354
|
}
|
|
45355
|
+
if (caso.workflow === "ajuste_convenio") {
|
|
45356
|
+
for (const k of ["items", "resumen", "archivo_csv", "diferencias", "nota", "detalle", "periodo_hasta", "porcentaje_calculado", "porcentaje_acordado", "indices_usados"]) delete datos[k];
|
|
45357
|
+
const tope = contexto.topeAjuste ?? leerTopeAjuste(void 0);
|
|
45358
|
+
const porcentajeAntes = typeof caso.datos.porcentaje === "number" ? caso.datos.porcentaje : 0;
|
|
45359
|
+
const calculado = typeof caso.datos.porcentaje_calculado === "number" ? caso.datos.porcentaje_calculado : porcentajeAntes;
|
|
45360
|
+
const recalcular = (porcentaje3, excluir) => {
|
|
45361
|
+
const a = armarAjuste(caso.datos.actual, porcentaje3);
|
|
45362
|
+
if (!a.ok) return;
|
|
45363
|
+
datos.porcentaje = porcentaje3;
|
|
45364
|
+
datos.items = a.items;
|
|
45365
|
+
datos.resumen = resumenAjuste(a.items, excluir, porcentaje3, calculado, tope);
|
|
45366
|
+
};
|
|
45367
|
+
const pedido = typeof datos.porcentaje === "number" ? datos.porcentaje : typeof datos.porcentaje === "string" && datos.porcentaje.trim() ? Number(datos.porcentaje) : null;
|
|
45368
|
+
delete datos.porcentaje;
|
|
45369
|
+
if (resultado.via === "calcular") {
|
|
45370
|
+
delete datos.excluir;
|
|
45371
|
+
const c = calcularAjuste(caso, datos, contexto);
|
|
45372
|
+
const act = leerActual(datos.actual);
|
|
45373
|
+
if (c.ok && "practicas" in act) {
|
|
45374
|
+
Object.assign(datos, {
|
|
45375
|
+
porcentaje: c.porcentaje,
|
|
45376
|
+
porcentaje_calculado: c.porcentaje,
|
|
45377
|
+
detalle: c.detalle,
|
|
45378
|
+
periodo_hasta: c.hasta,
|
|
45379
|
+
items: c.items,
|
|
45380
|
+
excluir: [],
|
|
45381
|
+
resumen: resumenAjuste(c.items, [], c.porcentaje, c.porcentaje, tope),
|
|
45382
|
+
// Lo que tenía el convenio antes del ajuste queda guardado en el caso: es el respaldo para volver atrás.
|
|
45383
|
+
actual: act.practicas,
|
|
45384
|
+
indices_usados: Array.isArray(datos.indices) ? datos.indices : []
|
|
45385
|
+
});
|
|
45386
|
+
}
|
|
45387
|
+
delete datos.indices;
|
|
45388
|
+
} else {
|
|
45389
|
+
delete datos.actual;
|
|
45390
|
+
delete datos.indices;
|
|
45391
|
+
}
|
|
45392
|
+
if (resultado.via === "ajustar") {
|
|
45393
|
+
const excluir = Array.isArray(datos.excluir) ? datos.excluir.map(normalizarCodigo) : caso.datos.excluir ?? [];
|
|
45394
|
+
datos.excluir = excluir;
|
|
45395
|
+
recalcular(pedido ?? porcentajeAntes, excluir);
|
|
45396
|
+
} else if (resultado.via !== "calcular") delete datos.excluir;
|
|
45397
|
+
if (resultado.via === "responder" && datos.respuesta === "aprobado_otro" && pedido !== null) {
|
|
45398
|
+
datos.porcentaje_acordado = pedido;
|
|
45399
|
+
recalcular(pedido, caso.datos.excluir);
|
|
45400
|
+
}
|
|
45401
|
+
if (resultado.via === "responder" && datos.respuesta === "aprobado") datos.porcentaje_acordado = porcentajeAntes;
|
|
45402
|
+
if (resultado.a === "por_enviar") {
|
|
45403
|
+
const op = await obtenerOpciones(ctx, input2.companyId, caso.workflow);
|
|
45404
|
+
const r = caso.datos.resumen;
|
|
45405
|
+
datos.nota = armarNota(op.nota, {
|
|
45406
|
+
institucion: str2(op.institucion) ?? str2(caso.datos.institucion) ?? "",
|
|
45407
|
+
financiador: str2(caso.datos.financiador) ?? "",
|
|
45408
|
+
convenio: str2(caso.datos.convenio) ?? "",
|
|
45409
|
+
porcentaje: porcentajeAntes,
|
|
45410
|
+
indices: textoIndices(caso.datos.detalle ?? { fijo: porcentajeAntes }, str2(caso.datos.ultimo_ajuste), str2(caso.datos.periodo_hasta)),
|
|
45411
|
+
vigencia: str2(caso.datos.vigencia_desde) ?? "",
|
|
45412
|
+
firma: str2(op.firma) ?? "",
|
|
45413
|
+
practicas: r?.practicas ?? 0
|
|
45414
|
+
});
|
|
45415
|
+
}
|
|
45416
|
+
if (resultado.a === "acordado") {
|
|
45417
|
+
const items = datos.items ?? leerItems(caso.datos.items);
|
|
45418
|
+
const aCargar = itemsACargar(items, caso.datos.excluir);
|
|
45419
|
+
const actual = leerActual(caso.datos.actual);
|
|
45420
|
+
const archivo = archivoActualizacion(aCargar, "practicas" in actual ? actual.practicas : []);
|
|
45421
|
+
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` };
|
|
45422
|
+
}
|
|
45423
|
+
if (resultado.via === "verificar") {
|
|
45424
|
+
const r = compararRelectura(itemsACargar(leerItems(caso.datos.items), caso.datos.excluir), datos.leidos);
|
|
45425
|
+
datos.diferencias = "error" in r ? [] : r.diferencias;
|
|
45426
|
+
datos.verificada_el = contexto.hoy ?? null;
|
|
45427
|
+
}
|
|
45428
|
+
}
|
|
44948
45429
|
if (caso.workflow === "honorario_en_cero") {
|
|
44949
45430
|
for (const k of ["items", "resumen", "comprobado_el"]) delete datos[k];
|
|
44950
45431
|
if (resultado.via === "observar" || resultado.via === "comprobar") {
|
|
@@ -45530,7 +46011,7 @@ async function registrarCargaManual(ctx, input2) {
|
|
|
45530
46011
|
...caso.workflow === "cuenta_completa" ? { faltantes_que_completo: caso.datos.faltantes ?? [] } : {},
|
|
45531
46012
|
...caso.workflow === "facturacion_pendiente" ? { bono: caso.datos.bono_numero ?? caso.datos.bono_id, vence_el: caso.datos.vence_el } : {},
|
|
45532
46013
|
...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 } : {},
|
|
46014
|
+
...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
46015
|
...caso.workflow === "credencial_profesional" ? { profesional: caso.datos.profesional ?? caso.datos.profesional_id, observacion_que_corrigio: caso.datos.observacion ?? null } : {},
|
|
45535
46016
|
...comentario ? { nota: comentario } : {}
|
|
45536
46017
|
},
|
|
@@ -45830,6 +46311,29 @@ async function registrarEnvioLiquidacion(ctx, input2) {
|
|
|
45830
46311
|
});
|
|
45831
46312
|
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
46313
|
}
|
|
46314
|
+
async function registrarEnvioNota(ctx, input2) {
|
|
46315
|
+
const caso = await leerCaso(ctx, input2.companyId, input2.casoId);
|
|
46316
|
+
if (!caso) throw new OpsError("caso_inexistente", `no existe el caso ${input2.casoId} en esta instituci\xF3n`);
|
|
46317
|
+
if (caso.workflow !== "ajuste_convenio") throw new OpsError("workflow_invalido", "solo los ajustes de convenio llevan nota al financiador");
|
|
46318
|
+
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`);
|
|
46319
|
+
if (input2.actor.tipo !== "humano") throw new OpsError("actor_no_permitido", "el env\xEDo lo registra una persona");
|
|
46320
|
+
const canal = typeof input2.canal === "string" && CANALES_NOTA.includes(input2.canal) ? input2.canal : "otro";
|
|
46321
|
+
const destinatario = str2(input2.destinatario);
|
|
46322
|
+
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 });
|
|
46323
|
+
const persona = str2(input2.persona);
|
|
46324
|
+
const nota = caso.datos.nota;
|
|
46325
|
+
const accion = await registrarAccion(ctx, {
|
|
46326
|
+
companyId: input2.companyId,
|
|
46327
|
+
casoId: caso.id,
|
|
46328
|
+
tipo: "aviso",
|
|
46329
|
+
conector: canal === "correo" ? "mail" : "manual",
|
|
46330
|
+
objetivo: `Nota de ajuste a ${str2(caso.datos.financiador) ?? "el financiador"}`,
|
|
46331
|
+
resultado: "ok",
|
|
46332
|
+
evidencia: { canal, destinatario, asunto: nota?.asunto ?? null, porcentaje: caso.datos.porcentaje ?? null, ...persona ? { registrado_por: persona } : {} },
|
|
46333
|
+
actor: input2.actor
|
|
46334
|
+
});
|
|
46335
|
+
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 });
|
|
46336
|
+
}
|
|
45833
46337
|
function canalReclamo(v) {
|
|
45834
46338
|
return typeof v === "string" && CANALES_RECLAMO.includes(v) ? v : "otro";
|
|
45835
46339
|
}
|
|
@@ -46817,7 +47321,7 @@ async function manejarApi(ctx, input2) {
|
|
|
46817
47321
|
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
47322
|
}
|
|
46819
47323
|
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) });
|
|
47324
|
+
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
47325
|
case "politicas":
|
|
46822
47326
|
return respuesta(200, { politicas: await listarPoliticas(ctx, companyId) });
|
|
46823
47327
|
case "serie":
|
|
@@ -46857,13 +47361,22 @@ async function manejarApi(ctx, input2) {
|
|
|
46857
47361
|
if (actor.tipo !== "humano") return respuesta(403, { error: "actor_no_permitido", motivo: "el env\xEDo lo registra una persona" });
|
|
46858
47362
|
return respuesta(200, await registrarEnvioLiquidacion(ctx, { companyId, casoId: input2.params.casoId ?? "", canal: body.canal, destinatario: str3(body.destinatario), persona: decididoPor, actor }));
|
|
46859
47363
|
}
|
|
47364
|
+
case "ajustar": {
|
|
47365
|
+
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" });
|
|
47366
|
+
const datosA = { ...body.porcentaje !== void 0 ? { porcentaje: body.porcentaje } : {}, ...Array.isArray(body.excluir) ? { excluir: body.excluir } : {} };
|
|
47367
|
+
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 }));
|
|
47368
|
+
}
|
|
47369
|
+
case "envio-nota": {
|
|
47370
|
+
if (actor.tipo !== "humano") return respuesta(403, { error: "actor_no_permitido", motivo: "el env\xEDo lo registra una persona" });
|
|
47371
|
+
return respuesta(200, await registrarEnvioNota(ctx, { companyId, casoId: input2.params.casoId ?? "", canal: body.canal, destinatario: str3(body.destinatario), persona: decididoPor, actor }));
|
|
47372
|
+
}
|
|
46860
47373
|
case "respuesta-financiador": {
|
|
46861
47374
|
if (actor.tipo !== "humano") return respuesta(403, { error: "actor_no_permitido", motivo: "la respuesta del financiador la registra una persona" });
|
|
46862
47375
|
if (str3(body.respuesta) === "cerrar") {
|
|
46863
47376
|
if (!str3(body.detalle)) return respuesta(422, { error: "validacion", motivo: "motivo_presente: cerrar la cobranza requiere motivo" });
|
|
46864
47377
|
return respuesta(200, await transicionar(ctx, { companyId, casoId: input2.params.casoId ?? "", via: "cerrar", motivo: motivoConPersona(str3(body.detalle)), datos: conPersona({}), actor }));
|
|
46865
47378
|
}
|
|
46866
|
-
const datosR = { respuesta: str3(body.respuesta), ...str3(body.fechaCompromiso) ? { fecha_compromiso: str3(body.fechaCompromiso) } : {}, ...str3(body.detalle) ? { detalle: str3(body.detalle) } : {} };
|
|
47379
|
+
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
47380
|
return respuesta(200, await transicionar(ctx, { companyId, casoId: input2.params.casoId ?? "", via: "responder", motivo: motivoConPersona(str3(body.detalle)), datos: conPersona(datosR), actor }));
|
|
46868
47381
|
}
|
|
46869
47382
|
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.35",
|
|
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": [
|