paperclip-plugin-autopilot-ops 0.1.61 → 0.1.63
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 +1 -1
- package/dist/ui/index.js +1 -1
- package/dist/worker.js +48 -14
- package/package.json +1 -1
package/dist/manifest.js
CHANGED
package/dist/ui/index.js
CHANGED
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.63";
|
|
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;
|
|
@@ -44708,7 +44709,7 @@ function evaluarRentabilidad(input2) {
|
|
|
44708
44709
|
if (i >= 0) avisos.splice(i, 1);
|
|
44709
44710
|
}
|
|
44710
44711
|
if (valor > 0 && (insumos / valor * 100 > INSUMOS_A_VERIFICAR_PCT || honorarioMax !== null && honorarioMax > valor)) avisos.push("dato_a_verificar");
|
|
44711
|
-
const impacto = impactoPromedio !== null &&
|
|
44712
|
+
const impacto = impactoPromedio !== null && perdidaProfesionales !== null ? Math.min(impactoPromedio, perdidaProfesionales) : impactoPromedio;
|
|
44712
44713
|
const reparto = valor > 0 ? conHonorario.map((h) => {
|
|
44713
44714
|
const m = redondear5(valor - descuento - insumos - indUsado - h.honorario);
|
|
44714
44715
|
return { profesional_id: h.profesional_id, profesional: h.profesional, honorario: h.honorario, margen: m, volumen: h.volumen, por_mes: h.volumen !== null ? porMes(m, h.volumen) : null };
|
|
@@ -44729,9 +44730,12 @@ function evaluarRentabilidad(input2) {
|
|
|
44729
44730
|
}
|
|
44730
44731
|
const honorarioPct = !conHonorario.length ? 0 : segunQuien ? conHonorario.reduce((a, h) => a + h.pct * h.volumen, 0) / totalVol : conHonorario.reduce((m, h) => h.honorario === honorarioMax ? Math.max(m, h.pct) : m, 0);
|
|
44731
44732
|
const indirectosPct = medidoCon === "total" && valor > 0 ? ind.detalle.filter((d) => (input2.indirectos ?? []).some((c) => c.id === d.id && c.reparto === "porcentaje")).reduce((a, d) => a + d.monto, 0) / valor : 0;
|
|
44732
|
-
const
|
|
44733
|
-
const
|
|
44734
|
-
const
|
|
44733
|
+
const peorDebajo = debajoProf.length ? debajoProf.reduce((a, x) => x.h.honorario > a.h.honorario ? x : a).h : null;
|
|
44734
|
+
const [honObj, pctObj] = peorDebajo ? [peorDebajo.honorario, peorDebajo.pct] : [honorarioBase, honorarioPct];
|
|
44735
|
+
const fijos = honObj !== null ? honObj - pctObj * valor + insumos + (indUsado - indirectosPct * valor) : null;
|
|
44736
|
+
const libre = 1 - (umbral + descuentoPct) / 100 - pctObj - indirectosPct;
|
|
44737
|
+
const objetivoCalculado = fijos !== null && libre > 0.05 ? Math.ceil(fijos / libre) : null;
|
|
44738
|
+
const valorObjetivo = objetivoCalculado !== null && objetivoCalculado > valor ? objetivoCalculado : null;
|
|
44735
44739
|
const honorarioObjetivo = valor > 0 ? Math.max(0, Math.floor(valor * (1 - umbral / 100) - descuento - insumos - indUsado)) : null;
|
|
44736
44740
|
const insumosObjetivo = valor > 0 && honorarioBase !== null ? Math.max(0, Math.floor(valor * (1 - umbral / 100) - descuento - honorarioBase - indUsado)) : null;
|
|
44737
44741
|
const e = {
|
|
@@ -44819,6 +44823,8 @@ function oracionRentabilidad(l, e) {
|
|
|
44819
44823
|
const porque = porqueRentabilidad(l, e);
|
|
44820
44824
|
const total = e.medido_con === "total" ? " despu\xE9s de los costos indirectos" : "";
|
|
44821
44825
|
if (e.clase === "perdida") return `${practica} da p\xE9rdida${total}.${porque ? ` ${porque}` : ""}${mensual}`;
|
|
44826
|
+
const prof = e.causa === "honorario_profesional" ? e.causa_profesionales[0] : void 0;
|
|
44827
|
+
if (e.clase === "margen_bajo" && prof && e.valor > 0) return `${practica}: con ${prof.profesional} deja un margen de ${porcentaje(Math.round(prof.margen / e.valor * 1e3) / 10)}${total}, debajo del ${porcentaje(e.umbral)} de la instituci\xF3n.${porque ? ` ${porque}` : ""}${mensual}`;
|
|
44822
44828
|
if (e.clase === "margen_bajo") return `${practica} deja un margen de ${porcentaje(e.margen_pct)}${total}, debajo del ${porcentaje(e.umbral)} de la instituci\xF3n.${porque ? ` ${porque}` : ""}${mensual}`;
|
|
44823
44829
|
return `${practica} deja un margen de ${porcentaje(e.margen_pct)}${total}.`;
|
|
44824
44830
|
}
|
|
@@ -45208,7 +45214,8 @@ function opcionesRentabilidad(op) {
|
|
|
45208
45214
|
perdidaMenor: enteroEntre(op.perdida_menor, 0, 1e7) ?? PERDIDA_MENOR_POR_DEFECTO,
|
|
45209
45215
|
sinInsumos: Array.isArray(op.practicas_sin_insumos) ? op.practicas_sin_insumos.map(String).slice(0, 5e3) : [],
|
|
45210
45216
|
descuento: porcentajeDescuento(op.descuento_cobro) ?? 0,
|
|
45211
|
-
descuentoPorFinanciador: leerDescuentos(op.descuentos_financiador)
|
|
45217
|
+
descuentoPorFinanciador: leerDescuentos(op.descuentos_financiador),
|
|
45218
|
+
rutina: op.rutina_liquidacion === "mensual" ? "mensual" : "nueva"
|
|
45212
45219
|
};
|
|
45213
45220
|
}
|
|
45214
45221
|
async function leerBases(ctx, companyId) {
|
|
@@ -45237,6 +45244,7 @@ ON CONFLICT (company_id) DO UPDATE SET bases = EXCLUDED.bases, practicas = EXCLU
|
|
|
45237
45244
|
return bases;
|
|
45238
45245
|
}
|
|
45239
45246
|
async function guardarMediciones(ctx, companyId, items, quitar = []) {
|
|
45247
|
+
await ctx.db.execute(`DELETE FROM ${t3(ctx, "rentabilidad_medicion")} WHERE company_id = $1 AND medido_el < now() - interval '45 days'`, [companyId]);
|
|
45240
45248
|
for (const referencia of quitar) await ctx.db.execute(`DELETE FROM ${t3(ctx, "rentabilidad_medicion")} WHERE company_id = $1 AND referencia = $2`, [companyId, referencia]);
|
|
45241
45249
|
for (const { lectura: l, evaluacion } of items) {
|
|
45242
45250
|
await ctx.db.execute(
|
|
@@ -45288,7 +45296,7 @@ function decidirApertura(e, ultimo, hoy, piso = 0) {
|
|
|
45288
45296
|
if (antes !== null && antes >= 0 && ahora !== null && ahora < 0) return { abrir: true, motivo: "aceptada cuando dejaba margen, ahora da p\xE9rdida" };
|
|
45289
45297
|
const mismoCalculo = !ultimo.calculo_aceptado || !e.calculo || ultimo.calculo_aceptado === e.calculo;
|
|
45290
45298
|
const [a0, a1] = mismoCalculo ? [antes, ahora] : [ultimo.peor_aceptada ?? null, e.impacto_peor ?? null];
|
|
45291
|
-
if (a0 !== null && a1 !== null && a1 < 0 && a0 - a1 > Math.max(Math.abs(a0) * CRECIMIENTO_QUE_REABRE, piso)) return { abrir: true, motivo: "la p\xE9rdida por mes creci\xF3 m\xE1s de un 20 % desde que la acept\xF3 la direcci\xF3n" };
|
|
45299
|
+
if (a0 !== null && a1 !== null && (a1 < 0 || e.clase === "margen_bajo") && a0 - a1 > Math.max(Math.abs(a0) * CRECIMIENTO_QUE_REABRE, piso)) return { abrir: true, motivo: a1 < 0 ? "la p\xE9rdida por mes creci\xF3 m\xE1s de un 20 % desde que la acept\xF3 la direcci\xF3n" : "lo que deja por mes cay\xF3 m\xE1s de un 20 % desde que la acept\xF3 la direcci\xF3n" };
|
|
45292
45300
|
if (!ultimo.aceptada_hasta && antes === null) return { abrir: false, motivo: "la direcci\xF3n la acept\xF3 as\xED" };
|
|
45293
45301
|
return { abrir: false, motivo: `aceptada por la direcci\xF3n${ultimo.aceptada_hasta ? ` hasta el ${ultimo.aceptada_hasta}` : ""}` };
|
|
45294
45302
|
}
|
|
@@ -45672,6 +45680,7 @@ function describirOpciones(antes, despues) {
|
|
|
45672
45680
|
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)}`);
|
|
45673
45681
|
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)");
|
|
45674
45682
|
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")}`);
|
|
45683
|
+
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");
|
|
45675
45684
|
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`);
|
|
45676
45685
|
if (JSON.stringify(antes.descuentos_financiador ?? {}) !== JSON.stringify(despues.descuentos_financiador ?? {}) && despues.descuentos_financiador !== void 0) {
|
|
45677
45686
|
const d = Object.entries(despues.descuentos_financiador ?? {});
|
|
@@ -45730,6 +45739,22 @@ function pareceNoProfesional(nombre) {
|
|
|
45730
45739
|
const [apellido = "", nombres = ""] = n2.split(",").map((x) => x.trim().toLowerCase());
|
|
45731
45740
|
return /^[^a-záéíóúñü]/i.test(n2) || /\d/.test(n2) || apellido !== "" && apellido === nombres || /\b(admin|administrador|prueba|test|sistema|usuario)\b/i.test(n2);
|
|
45732
45741
|
}
|
|
45742
|
+
function imputarMinutos(lecturas) {
|
|
45743
|
+
const mediana2 = (xs) => {
|
|
45744
|
+
const o = [...xs].sort((a, b) => a - b);
|
|
45745
|
+
return o.length ? o[Math.floor(o.length / 2)] : null;
|
|
45746
|
+
};
|
|
45747
|
+
const porTipo = /* @__PURE__ */ new Map();
|
|
45748
|
+
for (const l of lecturas) if (l.minutos !== null && l.tipo) porTipo.set(l.tipo, [...porTipo.get(l.tipo) ?? [], l.minutos]);
|
|
45749
|
+
for (const l of lecturas) {
|
|
45750
|
+
if (l.minutos !== null || !l.tipo) continue;
|
|
45751
|
+
const m = mediana2(porTipo.get(l.tipo) ?? []);
|
|
45752
|
+
if (m !== null) {
|
|
45753
|
+
l.minutos = m;
|
|
45754
|
+
l.minutos_estimados = true;
|
|
45755
|
+
}
|
|
45756
|
+
}
|
|
45757
|
+
}
|
|
45733
45758
|
function resultadoPedido(d, e) {
|
|
45734
45759
|
const accion = str2(d.accion) ?? "pedir_aumento";
|
|
45735
45760
|
const pedido = typeof d.precio_propuesto === "number" ? d.precio_propuesto : null;
|
|
@@ -45737,14 +45762,15 @@ function resultadoPedido(d, e) {
|
|
|
45737
45762
|
if (accion === "revisar_honorario") {
|
|
45738
45763
|
const previo = d.evaluacion && typeof d.evaluacion === "object" ? d.evaluacion : {};
|
|
45739
45764
|
const objetivo = typeof previo.honorario_objetivo === "number" ? previo.honorario_objetivo : null;
|
|
45740
|
-
const
|
|
45741
|
-
const
|
|
45765
|
+
const profs = Array.isArray(previo.causa_profesionales) ? previo.causa_profesionales : [];
|
|
45766
|
+
const honorarios = profs.map((p) => e.reparto.find((r) => r.profesional_id === p.profesional_id)?.honorario ?? null);
|
|
45767
|
+
const ahora = honorarios.length && honorarios.every((h) => h !== null) ? Math.max(...honorarios) : null;
|
|
45742
45768
|
return { accion, pedido: objetivo, ahora, cumple: objetivo !== null && ahora !== null ? ahora <= objetivo + 0.5 : null };
|
|
45743
45769
|
}
|
|
45744
45770
|
return { accion, pedido: null, ahora: null, cumple: e.clase === "rentable" ? true : null };
|
|
45745
45771
|
}
|
|
45746
45772
|
function evaluarConOpciones(l, hoy, op, bases) {
|
|
45747
|
-
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) });
|
|
45773
|
+
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 });
|
|
45748
45774
|
}
|
|
45749
45775
|
async function evaluarRentabilidades(ctx, companyId, lecturas) {
|
|
45750
45776
|
if (!Array.isArray(lecturas)) throw new OpsError("validacion", "lecturas: tiene que ser una lista");
|
|
@@ -45755,6 +45781,7 @@ async function evaluarRentabilidades(ctx, companyId, lecturas) {
|
|
|
45755
45781
|
const leidas = lecturas.map((x) => leerLecturaRentabilidad(x));
|
|
45756
45782
|
const validas = leidas.flatMap((l) => "error" in l ? [] : [l.lectura]);
|
|
45757
45783
|
for (const l of validas) conInsumosDeOpera(l, insumos);
|
|
45784
|
+
imputarMinutos(validas);
|
|
45758
45785
|
const anteriores = new Map((await listarMediciones(ctx, companyId)).map((m) => [m.referencia, m]));
|
|
45759
45786
|
const completa = validas.length > 0 && validas.length >= anteriores.size * 0.8;
|
|
45760
45787
|
const bases = completa ? await guardarBases(ctx, companyId, validas, opciones.indirectos) : await leerBases(ctx, companyId);
|
|
@@ -45893,6 +45920,7 @@ async function cambiarPolitica(ctx, input2) {
|
|
|
45893
45920
|
if (v === null) throw new OpsError("validacion", "descuento_cobro: tiene que ser un porcentaje entre 0 y 60");
|
|
45894
45921
|
opciones.descuento_cobro = v;
|
|
45895
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");
|
|
45896
45924
|
if (opciones.descuentos_financiador !== void 0) opciones.descuentos_financiador = leerDescuentos(opciones.descuentos_financiador);
|
|
45897
45925
|
if (opciones.practicas_sin_insumos !== void 0) {
|
|
45898
45926
|
if (!Array.isArray(opciones.practicas_sin_insumos)) throw new OpsError("validacion", "practicas_sin_insumos: tiene que ser una lista de c\xF3digos");
|
|
@@ -46198,7 +46226,13 @@ async function contextoDe(ctx, companyId, caso, via, datos) {
|
|
|
46198
46226
|
if (via === "evaluar" || via === "comprobar") {
|
|
46199
46227
|
const op = opcionesRentabilidad(await obtenerOpciones(ctx, companyId, caso.workflow));
|
|
46200
46228
|
const l = leerLecturaRentabilidad(datos.lectura);
|
|
46201
|
-
if (!("error" in l))
|
|
46229
|
+
if (!("error" in l)) {
|
|
46230
|
+
conInsumosDeOpera(l.lectura, await insumosPorCodigo(ctx, companyId));
|
|
46231
|
+
if (l.lectura.minutos === null) {
|
|
46232
|
+
const guardada = (await listarMediciones(ctx, companyId)).find((m) => m.referencia === `rentabilidad:${l.lectura.prestacion_id}:${l.lectura.financiador_id}`);
|
|
46233
|
+
if (guardada?.minutos) Object.assign(l.lectura, { minutos: guardada.minutos, minutos_estimados: true });
|
|
46234
|
+
}
|
|
46235
|
+
}
|
|
46202
46236
|
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)) };
|
|
46203
46237
|
}
|
|
46204
46238
|
return contexto;
|
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.63",
|
|
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": [
|