paperclip-plugin-autopilot-ops 0.1.62 → 0.1.64

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/manifest.ts
2
2
  var PLUGIN_ID = "alephoo.autopilot-ops";
3
- var PLUGIN_VERSION = "0.1.62";
3
+ var PLUGIN_VERSION = "0.1.64";
4
4
  var DB_NAMESPACE_SLUG = "autopilot_ops";
5
5
  var PAGE_ROUTE = "autopilot";
6
6
  var TOOL_NAMES = {
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.62";
11
+ var PLUGIN_VERSION = "0.1.64";
12
12
  var DB_NAMESPACE_SLUG = "autopilot_ops";
13
13
  var PAGE_ROUTE = "autopilot";
14
14
  var TOOL_NAMES = {
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.62";
39935
+ var PLUGIN_VERSION = "0.1.64";
39936
39936
  var DB_NAMESPACE_SLUG = "autopilot_ops";
39937
39937
  var PAGE_ROUTE = "autopilot";
39938
39938
  var TOOL_NAMES = {
@@ -44453,7 +44453,7 @@ function dia3(v) {
44453
44453
  var LIQUIDACIONES_MINIMAS = 3;
44454
44454
  var INSUMOS_CAUSA_PCT = 10;
44455
44455
  var PLANES_SIN_VALOR_PCT = 10;
44456
- function honorarioDeRegla(regla, valor) {
44456
+ function honorarioDeRegla(regla, valor, rutina = "nueva") {
44457
44457
  const pct = (n2) => `${n2.toLocaleString("es-AR", { maximumFractionDigits: 2 })} %`;
44458
44458
  const importe = regla.importe ?? null;
44459
44459
  if (importe !== null && importe > 0) {
@@ -44461,6 +44461,7 @@ function honorarioDeRegla(regla, valor) {
44461
44461
  if (regla.inst !== null) return { honorario: redondear5(importe * (1 - regla.inst / 100)), fuente: "importe", detalle: `honorario ${pesos4(importe)} menos ${pct(regla.inst)} de la instituci\xF3n` };
44462
44462
  return { honorario: importe, fuente: "importe", detalle: `honorario fijo ${pesos4(importe)}` };
44463
44463
  }
44464
+ if (rutina === "mensual" && (regla.prof !== null || regla.inst !== null)) return { honorario: 0, fuente: "importe", detalle: "la liquidaci\xF3n mensual por obra social del HIS liquida en $ 0 una regla sin honorario base" };
44464
44465
  if (regla.prof !== null) return { honorario: regla.prof, fuente: "monto_fijo", detalle: `\xABprof\xBB ${regla.prof.toLocaleString("es-AR", { maximumFractionDigits: 2 })} sin honorario base: el HIS liquida ${pesos4(regla.prof)} fijos` };
44465
44466
  if (regla.inst !== null) return { honorario: redondear5(valor * regla.inst / 100), fuente: "porcentaje", detalle: `${pct(regla.inst)} de lo que paga el convenio` };
44466
44467
  return null;
@@ -44469,7 +44470,6 @@ var HONORARIO_MINIMO_PCT = 1;
44469
44470
  var HONORARIO_FIJO_MINIMO_PCT = 10;
44470
44471
  var INSUMOS_A_VERIFICAR_PCT = 50;
44471
44472
  function noEsPractica(l) {
44472
- if (l.honorarios.length > 0) return false;
44473
44473
  return /medicament|descartable|insumo|material|farmac|internaci|hoteler/i.test(l.tipo ?? "") || /(comprimid|ampolla|frasco|sachet|cateter|catéter|guantes?\b|jeringa|gasas?\b|capsula|cápsula|\d\s*(mg|mcg|ml|ui)\b|d[ií]as? (en )?cama|d[ií]a de internaci|pensi[oó]n|habitaci[oó]n)/i.test(l.nombre);
44474
44474
  }
44475
44475
  function leerLecturaRentabilidad(v) {
@@ -44624,7 +44624,7 @@ function evaluarRentabilidad(input2) {
44624
44624
  if (l.honorarios.length === 0) faltas.push("sin_profesional");
44625
44625
  for (const h of l.honorarios) {
44626
44626
  const nombre = h.profesional ?? h.profesional_id;
44627
- const deRegla = h.regla ? honorarioDeRegla(h.regla, valor) : null;
44627
+ const deRegla = h.regla ? honorarioDeRegla(h.regla, valor, input2.rutina ?? "nueva") : null;
44628
44628
  let hon;
44629
44629
  const pocasLiquidaciones = Boolean(h.liquidado && deRegla && typeof h.liquidado.muestra === "number" && h.liquidado.muestra < LIQUIDACIONES_MINIMAS);
44630
44630
  const reglaProporcional = deRegla?.fuente === "porcentaje";
@@ -44674,6 +44674,7 @@ function evaluarRentabilidad(input2) {
44674
44674
  const descuento = redondear5(valor * descuentoPct / 100);
44675
44675
  const ind = indirectosDe(l, valor, input2.indirectos ?? [], input2.bases ?? null);
44676
44676
  for (const a of ind.avisos) if (!avisos.includes(a)) avisos.push(a);
44677
+ if (l.minutos_estimados && (input2.indirectos ?? []).some((c) => c.activo !== false && c.reparto === "minuto")) avisos.push("duracion_estimada");
44677
44678
  const medidoCon = input2.medirCon === "total" && input2.bases ? "total" : "directo";
44678
44679
  const indUsado = medidoCon === "total" ? ind.total : 0;
44679
44680
  const margen = honorarioBase !== null && valor > 0 ? redondear5(valor - descuento - insumos - honorarioBase) : null;
@@ -44719,7 +44720,7 @@ function evaluarRentabilidad(input2) {
44719
44720
  if ((clase2 === "perdida" || clase2 === "margen_bajo") && honorarioBase !== null) {
44720
44721
  const sinInsumos = valor - descuento - honorarioBase - indUsado;
44721
44722
  const llegan = reparto.filter((r) => r.margen >= minimo);
44722
- const noLlegan = reparto.filter((r) => r.margen < minimo);
44723
+ const noLlegan = reparto.filter((r) => r.margen < minimo).sort((a, b) => a.margen - b.margen);
44723
44724
  if (llegan.length > 0 && noLlegan.length > 0) {
44724
44725
  causa = "honorario_profesional";
44725
44726
  causaProfesionales = noLlegan.map((r) => ({ profesional_id: r.profesional_id, profesional: r.profesional, honorario: r.honorario, margen: r.margen }));
@@ -44770,7 +44771,7 @@ function evaluarRentabilidad(input2) {
44770
44771
  insumos_objetivo: causa === "insumos" ? insumosObjetivo : null,
44771
44772
  descuento,
44772
44773
  descuento_pct: descuentoPct,
44773
- no_es_practica: noEsPractica(l),
44774
+ no_es_practica: !input2.esPractica && noEsPractica(l),
44774
44775
  perdida_menor: impacto !== null && impacto < 0 && Math.abs(impacto) < perdidaMenor,
44775
44776
  porque: null,
44776
44777
  faltas,
@@ -45213,7 +45214,9 @@ function opcionesRentabilidad(op) {
45213
45214
  perdidaMenor: enteroEntre(op.perdida_menor, 0, 1e7) ?? PERDIDA_MENOR_POR_DEFECTO,
45214
45215
  sinInsumos: Array.isArray(op.practicas_sin_insumos) ? op.practicas_sin_insumos.map(String).slice(0, 5e3) : [],
45215
45216
  descuento: porcentajeDescuento(op.descuento_cobro) ?? 0,
45216
- descuentoPorFinanciador: leerDescuentos(op.descuentos_financiador)
45217
+ descuentoPorFinanciador: leerDescuentos(op.descuentos_financiador),
45218
+ rutina: op.rutina_liquidacion === "mensual" ? "mensual" : "nueva",
45219
+ sonPracticas: Array.isArray(op.son_practicas) ? op.son_practicas.map(String).slice(0, 5e3) : []
45217
45220
  };
45218
45221
  }
45219
45222
  async function leerBases(ctx, companyId) {
@@ -45223,7 +45226,7 @@ async function leerBases(ctx, companyId) {
45223
45226
  async function recalcularBases(ctx, companyId, costos) {
45224
45227
  const ms = await listarMediciones(ctx, companyId);
45225
45228
  if (!ms.length) return null;
45226
- const lecturas = ms.map((m) => ({ minutos: m.minutos, tipo: m.tipo, volumen: m.evaluacion.volumen ?? 0, dias: m.evaluacion.dias ?? 90, componentes: m.componentes }));
45229
+ const lecturas = ms.filter((m) => !m.evaluacion.no_es_practica).map((m) => ({ minutos: m.minutos, tipo: m.tipo, volumen: m.evaluacion.volumen ?? 0, dias: m.evaluacion.dias ?? 90, componentes: m.componentes }));
45227
45230
  const bases = basesIndirectos(lecturas, costos);
45228
45231
  await ctx.db.execute(
45229
45232
  `INSERT INTO ${t3(ctx, "rentabilidad_base")} (company_id, bases, practicas, calculada_el) VALUES ($1, $2::jsonb, $3, now())
@@ -45678,6 +45681,7 @@ function describirOpciones(antes, despues) {
45678
45681
  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)}`);
45679
45682
  if (antes.medir_con !== despues.medir_con && despues.medir_con !== void 0) partes.push(despues.medir_con === "total" ? "mide con el margen total (despu\xE9s de los costos indirectos)" : "mide con el margen directo (antes de los costos indirectos)");
45680
45683
  if (antes.perdida_menor !== despues.perdida_menor && despues.perdida_menor !== void 0) partes.push(`p\xE9rdida menor por mes: $ ${Number(despues.perdida_menor).toLocaleString("es-AR")}`);
45684
+ if (antes.rutina_liquidacion !== despues.rutina_liquidacion && despues.rutina_liquidacion !== void 0) partes.push(despues.rutina_liquidacion === "mensual" ? "honorarios con la liquidaci\xF3n mensual por obra social del HIS" : "honorarios con la liquidaci\xF3n nueva del HIS");
45681
45685
  if (antes.descuento_cobro !== despues.descuento_cobro && despues.descuento_cobro !== void 0) partes.push(`se pierde al cobrar: ${Number(despues.descuento_cobro).toLocaleString("es-AR")} % de lo facturado`);
45682
45686
  if (JSON.stringify(antes.descuentos_financiador ?? {}) !== JSON.stringify(despues.descuentos_financiador ?? {}) && despues.descuentos_financiador !== void 0) {
45683
45687
  const d = Object.entries(despues.descuentos_financiador ?? {});
@@ -45741,11 +45745,16 @@ function imputarMinutos(lecturas) {
45741
45745
  const o = [...xs].sort((a, b) => a - b);
45742
45746
  return o.length ? o[Math.floor(o.length / 2)] : null;
45743
45747
  };
45744
- const conDuracion = lecturas.filter((l) => l.minutos !== null);
45745
- const general = mediana2(conDuracion.map((l) => l.minutos));
45746
45748
  const porTipo = /* @__PURE__ */ new Map();
45747
- for (const l of conDuracion) porTipo.set(l.tipo ?? "", [...porTipo.get(l.tipo ?? "") ?? [], l.minutos]);
45748
- for (const l of lecturas) if (l.minutos === null) l.minutos = mediana2(porTipo.get(l.tipo ?? "") ?? []) ?? general;
45749
+ for (const l of lecturas) if (l.minutos !== null && l.tipo) porTipo.set(l.tipo, [...porTipo.get(l.tipo) ?? [], l.minutos]);
45750
+ for (const l of lecturas) {
45751
+ if (l.minutos !== null || !l.tipo) continue;
45752
+ const m = mediana2(porTipo.get(l.tipo) ?? []);
45753
+ if (m !== null) {
45754
+ l.minutos = m;
45755
+ l.minutos_estimados = true;
45756
+ }
45757
+ }
45749
45758
  }
45750
45759
  function resultadoPedido(d, e) {
45751
45760
  const accion = str2(d.accion) ?? "pedir_aumento";
@@ -45762,7 +45771,7 @@ function resultadoPedido(d, e) {
45762
45771
  return { accion, pedido: null, ahora: null, cumple: e.clase === "rentable" ? true : null };
45763
45772
  }
45764
45773
  function evaluarConOpciones(l, hoy, op, bases) {
45765
- return evaluarRentabilidad({ lectura: l, hoy, umbral: op.umbral, mesesPrecio: op.mesesPrecio, indirectos: op.indirectos, bases, medirCon: op.medirCon, perdidaMenor: op.perdidaMenor, sinInsumos: op.sinInsumos.includes(normalizarCodigo(l.codigo) ?? ""), descuento: descuentoDe(op, l.financiador) });
45774
+ return evaluarRentabilidad({ lectura: l, hoy, umbral: op.umbral, mesesPrecio: op.mesesPrecio, indirectos: op.indirectos, bases, medirCon: op.medirCon, perdidaMenor: op.perdidaMenor, sinInsumos: op.sinInsumos.includes(normalizarCodigo(l.codigo) ?? ""), descuento: descuentoDe(op, l.financiador), rutina: op.rutina, esPractica: op.sonPracticas.includes(normalizarCodigo(l.codigo) ?? "") });
45766
45775
  }
45767
45776
  async function evaluarRentabilidades(ctx, companyId, lecturas) {
45768
45777
  if (!Array.isArray(lecturas)) throw new OpsError("validacion", "lecturas: tiene que ser una lista");
@@ -45776,11 +45785,11 @@ async function evaluarRentabilidades(ctx, companyId, lecturas) {
45776
45785
  imputarMinutos(validas);
45777
45786
  const anteriores = new Map((await listarMediciones(ctx, companyId)).map((m) => [m.referencia, m]));
45778
45787
  const completa = validas.length > 0 && validas.length >= anteriores.size * 0.8;
45779
- const bases = completa ? await guardarBases(ctx, companyId, validas, opciones.indirectos) : await leerBases(ctx, companyId);
45788
+ const practicas = validas.filter((l) => opciones.sonPracticas.includes(normalizarCodigo(l.codigo) ?? "") || !noEsPractica(l));
45789
+ const bases = completa ? await guardarBases(ctx, companyId, practicas, opciones.indirectos) : await leerBases(ctx, companyId);
45780
45790
  const pedidos = await ultimosPedidos(ctx, companyId);
45781
45791
  const medidas = [];
45782
45792
  const resultados = [];
45783
- const noSonPracticas = [];
45784
45793
  for (const [indice, l] of leidas.entries()) {
45785
45794
  if ("error" in l) {
45786
45795
  resultados.push({ indice, error: l.error });
@@ -45789,8 +45798,7 @@ async function evaluarRentabilidades(ctx, companyId, lecturas) {
45789
45798
  const evaluacion = evaluarConOpciones(l.lectura, hoy, opciones, bases);
45790
45799
  const referencia = `rentabilidad:${l.lectura.prestacion_id}:${l.lectura.financiador_id}`;
45791
45800
  const previa = anteriores.get(referencia);
45792
- if (evaluacion.no_es_practica) noSonPracticas.push(referencia);
45793
- else await registrarCambiosHis(ctx, {
45801
+ if (!evaluacion.no_es_practica) await registrarCambiosHis(ctx, {
45794
45802
  companyId,
45795
45803
  referencia,
45796
45804
  codigo: l.lectura.codigo,
@@ -45798,11 +45806,11 @@ async function evaluarRentabilidades(ctx, companyId, lecturas) {
45798
45806
  antes: previa ? { valor: previa.evaluacion.valor, honorarios: (previa.evaluacion.honorarios ?? []).map((h) => ({ profesional_id: h.profesional_id, profesional: h.profesional, honorario: h.honorario })) } : null,
45799
45807
  despues: { valor: evaluacion.valor, honorarios: evaluacion.honorarios.map((h) => ({ profesional_id: h.profesional_id, profesional: h.profesional, honorario: h.honorario })) }
45800
45808
  });
45801
- if (!evaluacion.no_es_practica) medidas.push({ lectura: l.lectura, evaluacion });
45809
+ medidas.push({ lectura: l.lectura, evaluacion });
45802
45810
  const apertura = decidirApertura(evaluacion, pedidos.get(referencia) ?? null, hoy, opciones.perdidaMenor);
45803
45811
  resultados.push({ clave: `${l.lectura.prestacion_id}:${l.lectura.financiador_id}`, referencia, codigo: l.lectura.codigo, practica: l.lectura.nombre, financiador: l.lectura.financiador, evaluacion, oracion: oracionRentabilidad(l.lectura, evaluacion), abrir: apertura.abrir, motivo_abrir: apertura.motivo });
45804
45812
  }
45805
- await guardarMediciones(ctx, companyId, medidas, noSonPracticas);
45813
+ await guardarMediciones(ctx, companyId, medidas);
45806
45814
  return { resultados, opciones };
45807
45815
  }
45808
45816
  function enteroEntre2(v, min, max) {
@@ -45912,7 +45920,12 @@ async function cambiarPolitica(ctx, input2) {
45912
45920
  if (v === null) throw new OpsError("validacion", "descuento_cobro: tiene que ser un porcentaje entre 0 y 60");
45913
45921
  opciones.descuento_cobro = v;
45914
45922
  }
45923
+ if (opciones.rutina_liquidacion !== void 0 && opciones.rutina_liquidacion !== "nueva" && opciones.rutina_liquidacion !== "mensual") throw new OpsError("validacion", "rutina_liquidacion: tiene que ser \xABnueva\xBB o \xABmensual\xBB");
45915
45924
  if (opciones.descuentos_financiador !== void 0) opciones.descuentos_financiador = leerDescuentos(opciones.descuentos_financiador);
45925
+ if (opciones.son_practicas !== void 0) {
45926
+ if (!Array.isArray(opciones.son_practicas)) throw new OpsError("validacion", "son_practicas: tiene que ser una lista de c\xF3digos");
45927
+ opciones.son_practicas = [...new Set(opciones.son_practicas.map((c) => normalizarCodigo(String(c ?? ""))).filter((c) => Boolean(c)))].slice(0, 5e3);
45928
+ }
45916
45929
  if (opciones.practicas_sin_insumos !== void 0) {
45917
45930
  if (!Array.isArray(opciones.practicas_sin_insumos)) throw new OpsError("validacion", "practicas_sin_insumos: tiene que ser una lista de c\xF3digos");
45918
45931
  opciones.practicas_sin_insumos = [...new Set(opciones.practicas_sin_insumos.map((c) => normalizarCodigo(String(c ?? ""))).filter((c) => Boolean(c)))].slice(0, 5e3);
@@ -46217,7 +46230,13 @@ async function contextoDe(ctx, companyId, caso, via, datos) {
46217
46230
  if (via === "evaluar" || via === "comprobar") {
46218
46231
  const op = opcionesRentabilidad(await obtenerOpciones(ctx, companyId, caso.workflow));
46219
46232
  const l = leerLecturaRentabilidad(datos.lectura);
46220
- if (!("error" in l)) conInsumosDeOpera(l.lectura, await insumosPorCodigo(ctx, companyId));
46233
+ if (!("error" in l)) {
46234
+ conInsumosDeOpera(l.lectura, await insumosPorCodigo(ctx, companyId));
46235
+ if (l.lectura.minutos === null) {
46236
+ const guardada = (await listarMediciones(ctx, companyId)).find((m) => m.referencia === `rentabilidad:${l.lectura.prestacion_id}:${l.lectura.financiador_id}`);
46237
+ if (guardada?.minutos) Object.assign(l.lectura, { minutos: guardada.minutos, minutos_estimados: true });
46238
+ }
46239
+ }
46221
46240
  contexto.rentabilidad = "error" in l ? { error: l.error } : { clave: `${l.lectura.prestacion_id}:${l.lectura.financiador_id}`, evaluacion: evaluarConOpciones(l.lectura, hoy, op, await leerBases(ctx, companyId)) };
46222
46241
  }
46223
46242
  return contexto;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paperclip-plugin-autopilot-ops",
3
- "version": "0.1.62",
3
+ "version": "0.1.64",
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": [