paperclip-plugin-autopilot-ops 0.1.57 → 0.1.58
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 +9 -1
- package/dist/ui/index.js +9 -1
- package/dist/worker.js +173 -46
- 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.58";
|
|
4
4
|
var DB_NAMESPACE_SLUG = "autopilot_ops";
|
|
5
5
|
var PAGE_ROUTE = "autopilot";
|
|
6
6
|
var TOOL_NAMES = {
|
|
@@ -577,6 +577,14 @@ var manifest = {
|
|
|
577
577
|
capability: "api.routes.register",
|
|
578
578
|
companyResolution: { from: "body", key: "companyId" }
|
|
579
579
|
},
|
|
580
|
+
{
|
|
581
|
+
routeKey: "no-se-pudo",
|
|
582
|
+
method: "POST",
|
|
583
|
+
path: "/casos/:casoId/no-se-pudo",
|
|
584
|
+
auth: "board",
|
|
585
|
+
capability: "api.routes.register",
|
|
586
|
+
companyResolution: { from: "body", key: "companyId" }
|
|
587
|
+
},
|
|
580
588
|
{
|
|
581
589
|
routeKey: "contactos",
|
|
582
590
|
method: "GET",
|
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.58";
|
|
12
12
|
var DB_NAMESPACE_SLUG = "autopilot_ops";
|
|
13
13
|
var PAGE_ROUTE = "autopilot";
|
|
14
14
|
var TOOL_NAMES = {
|
|
@@ -585,6 +585,14 @@ var manifest = {
|
|
|
585
585
|
capability: "api.routes.register",
|
|
586
586
|
companyResolution: { from: "body", key: "companyId" }
|
|
587
587
|
},
|
|
588
|
+
{
|
|
589
|
+
routeKey: "no-se-pudo",
|
|
590
|
+
method: "POST",
|
|
591
|
+
path: "/casos/:casoId/no-se-pudo",
|
|
592
|
+
auth: "board",
|
|
593
|
+
capability: "api.routes.register",
|
|
594
|
+
companyResolution: { from: "body", key: "companyId" }
|
|
595
|
+
},
|
|
588
596
|
{
|
|
589
597
|
routeKey: "contactos",
|
|
590
598
|
method: "GET",
|
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.58";
|
|
39936
39936
|
var DB_NAMESPACE_SLUG = "autopilot_ops";
|
|
39937
39937
|
var PAGE_ROUTE = "autopilot";
|
|
39938
39938
|
var TOOL_NAMES = {
|
|
@@ -40509,6 +40509,14 @@ var manifest = {
|
|
|
40509
40509
|
capability: "api.routes.register",
|
|
40510
40510
|
companyResolution: { from: "body", key: "companyId" }
|
|
40511
40511
|
},
|
|
40512
|
+
{
|
|
40513
|
+
routeKey: "no-se-pudo",
|
|
40514
|
+
method: "POST",
|
|
40515
|
+
path: "/casos/:casoId/no-se-pudo",
|
|
40516
|
+
auth: "board",
|
|
40517
|
+
capability: "api.routes.register",
|
|
40518
|
+
companyResolution: { from: "body", key: "companyId" }
|
|
40519
|
+
},
|
|
40512
40520
|
{
|
|
40513
40521
|
routeKey: "contactos",
|
|
40514
40522
|
method: "GET",
|
|
@@ -41275,9 +41283,9 @@ function textoIndices(detalle, desde, hasta) {
|
|
|
41275
41283
|
const mes = (p) => (/* @__PURE__ */ new Date(`${p}-15T12:00:00Z`)).toLocaleDateString("es-AR", { month: "long", year: "numeric", timeZone: "UTC" });
|
|
41276
41284
|
const pct = (n2) => `${n2.toLocaleString("es-AR", { maximumFractionDigits: 2 })} %`;
|
|
41277
41285
|
const partes = detalle.map((d) => `${d.nombre} ${pct(d.variacion)}`);
|
|
41278
|
-
const
|
|
41286
|
+
const pesos5 = detalle.length > 1 ? ` (ponderados ${detalle.map((d) => `${d.peso} %`).join(" y ")})` : "";
|
|
41279
41287
|
const periodo = desde && hasta ? `, de ${mes(desde)} a ${mes(hasta)}` : "";
|
|
41280
|
-
return `${partes.join(" y ")}${
|
|
41288
|
+
return `${partes.join(" y ")}${pesos5}${periodo}`;
|
|
41281
41289
|
}
|
|
41282
41290
|
var NOTA_POR_DEFECTO = {
|
|
41283
41291
|
asunto: "Actualizaci\xF3n de aranceles \xB7 {convenio} \xB7 {institucion}",
|
|
@@ -43755,6 +43763,14 @@ var rentabilidadPractica = {
|
|
|
43755
43763
|
MESES_ACEPTAR.includes(Number(datos.revisar_meses)) ? ok("plazo_valido") : falla("plazo_valido", `revisar_meses tiene que ser ${MESES_ACEPTAR.join(", ")}: en cu\xE1ntos meses se vuelve a mirar`)
|
|
43756
43764
|
]
|
|
43757
43765
|
},
|
|
43766
|
+
{
|
|
43767
|
+
via: "no_se_pudo",
|
|
43768
|
+
de: ["en_renegociacion"],
|
|
43769
|
+
a: "pendiente_aprobacion",
|
|
43770
|
+
actor: ["humano"],
|
|
43771
|
+
descripcion: "El equipo no pudo hacer lo que pidi\xF3 la direcci\xF3n (el HIS no lo deja, el profesional no acepta, el financiador no recibe pedidos): vuelve a la direcci\xF3n con el motivo para que decida otra cosa.",
|
|
43772
|
+
validar: (_caso, datos) => [texto4(datos.motivo) ? ok("motivo_presente") : falla("motivo_presente", "\xABno se pudo\xBB requiere el motivo: qu\xE9 lo impidi\xF3")]
|
|
43773
|
+
},
|
|
43758
43774
|
{
|
|
43759
43775
|
via: "cuestionar",
|
|
43760
43776
|
de: ["pendiente_aprobacion"],
|
|
@@ -44418,6 +44434,7 @@ async function informarPruebaRegla(ctx, input2) {
|
|
|
44418
44434
|
}
|
|
44419
44435
|
|
|
44420
44436
|
// src/rentabilidad.ts
|
|
44437
|
+
var pesos4 = (n2) => `${n2 < 0 ? "\u2212" : ""}$ ${Math.round(Math.abs(n2)).toLocaleString("es-AR")}`;
|
|
44421
44438
|
var UMBRAL_MARGEN_POR_DEFECTO = 15;
|
|
44422
44439
|
var MESES_PRECIO_POR_DEFECTO = 6;
|
|
44423
44440
|
var redondear5 = (n2) => Math.round(n2 * 100) / 100;
|
|
@@ -44433,19 +44450,26 @@ function dia3(v) {
|
|
|
44433
44450
|
const s = texto5(v);
|
|
44434
44451
|
return s && /^\d{4}-\d{2}-\d{2}/.test(s) ? s.slice(0, 10) : null;
|
|
44435
44452
|
}
|
|
44453
|
+
var LIQUIDACIONES_MINIMAS = 3;
|
|
44436
44454
|
function honorarioDeRegla(regla, valor) {
|
|
44437
44455
|
const pct = (n2) => `${n2.toLocaleString("es-AR", { maximumFractionDigits: 2 })} %`;
|
|
44438
44456
|
const importe = regla.importe ?? null;
|
|
44439
44457
|
if (importe !== null && importe > 0) {
|
|
44440
|
-
if (regla.prof !== null) return { honorario: redondear5(importe * regla.prof / 100), fuente: "importe_porcentaje", detalle: `honorario ${
|
|
44441
|
-
if (regla.inst !== null) return { honorario: redondear5(importe * (1 - regla.inst / 100)), fuente: "importe", detalle: `honorario ${
|
|
44442
|
-
return { honorario: importe, fuente: "importe", detalle: `honorario fijo ${
|
|
44458
|
+
if (regla.prof !== null) return { honorario: redondear5(importe * regla.prof / 100), fuente: "importe_porcentaje", detalle: `honorario ${pesos4(importe)} \xD7 ${pct(regla.prof)} del profesional` };
|
|
44459
|
+
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` };
|
|
44460
|
+
return { honorario: importe, fuente: "importe", detalle: `honorario fijo ${pesos4(importe)}` };
|
|
44443
44461
|
}
|
|
44444
|
-
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 ${
|
|
44462
|
+
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` };
|
|
44445
44463
|
if (regla.inst !== null) return { honorario: redondear5(valor * regla.inst / 100), fuente: "porcentaje", detalle: `${pct(regla.inst)} de lo que paga el convenio` };
|
|
44446
44464
|
return null;
|
|
44447
44465
|
}
|
|
44448
44466
|
var HONORARIO_MINIMO_PCT = 1;
|
|
44467
|
+
var HONORARIO_FIJO_MINIMO_PCT = 10;
|
|
44468
|
+
var INSUMOS_A_VERIFICAR_PCT = 50;
|
|
44469
|
+
function noEsPractica(l) {
|
|
44470
|
+
if (l.honorarios.length > 0) return false;
|
|
44471
|
+
return /medicament|descartable|insumo|material|farmac/i.test(l.tipo ?? "") || /\b(comprimid|ampolla|frasco|sachet|cateter|catéter|guantes?|jeringa|gasas?|capsula|cápsula|mg\b)/i.test(l.nombre);
|
|
44472
|
+
}
|
|
44449
44473
|
function leerLecturaRentabilidad(v) {
|
|
44450
44474
|
if (!v || typeof v !== "object" || Array.isArray(v)) return { error: "lectura: tiene que ser un objeto" };
|
|
44451
44475
|
const r = v;
|
|
@@ -44484,7 +44508,8 @@ function leerLecturaRentabilidad(v) {
|
|
|
44484
44508
|
const h = num6(lq.honorario);
|
|
44485
44509
|
const c2 = num6(lq.cantidad);
|
|
44486
44510
|
if (h === null || h < 0 || c2 === null || c2 <= 0) return { error: `lectura.honorarios[${i}].liquidado: tiene que ser {honorario \u2265 0, cantidad > 0} o null` };
|
|
44487
|
-
|
|
44511
|
+
const muestra = num6(lq.muestra);
|
|
44512
|
+
liquidado = { honorario: redondear5(h), cantidad: c2, ...muestra !== null && muestra >= 0 ? { muestra: Math.round(muestra) } : {} };
|
|
44488
44513
|
}
|
|
44489
44514
|
const vol = num6(x.volumen);
|
|
44490
44515
|
honorarios.push({ profesional_id: pid, profesional: texto5(x.profesional), regla: leerRegla(x.regla), ...liquidado ? { liquidado } : {}, volumen: vol !== null && vol >= 0 ? Math.round(vol) : null });
|
|
@@ -44537,7 +44562,10 @@ function indirectosDe(l, valor, costos, bases) {
|
|
|
44537
44562
|
continue;
|
|
44538
44563
|
}
|
|
44539
44564
|
const base = bases[c.id] ?? 0;
|
|
44540
|
-
if (base <= 0)
|
|
44565
|
+
if (base <= 0) {
|
|
44566
|
+
if (!avisos.includes("indirecto_sin_base")) avisos.push("indirecto_sin_base");
|
|
44567
|
+
continue;
|
|
44568
|
+
}
|
|
44541
44569
|
detalle.push({ id: c.id, nombre: c.nombre, monto: redondear5(c.mensual * uso / base) });
|
|
44542
44570
|
}
|
|
44543
44571
|
return { total: redondear5(detalle.reduce((a, d) => a + d.monto, 0)), detalle, avisos };
|
|
@@ -44594,10 +44622,11 @@ function evaluarRentabilidad(input2) {
|
|
|
44594
44622
|
const nombre = h.profesional ?? h.profesional_id;
|
|
44595
44623
|
const deRegla = h.regla ? honorarioDeRegla(h.regla, valor) : null;
|
|
44596
44624
|
let hon;
|
|
44597
|
-
|
|
44625
|
+
const pocasLiquidaciones = Boolean(h.liquidado && deRegla && typeof h.liquidado.muestra === "number" && h.liquidado.muestra < LIQUIDACIONES_MINIMAS);
|
|
44626
|
+
if (h.liquidado && !pocasLiquidaciones) {
|
|
44598
44627
|
hon = redondear5(h.liquidado.honorario / h.liquidado.cantidad);
|
|
44599
44628
|
if (deRegla && Math.abs(deRegla.honorario - hon) > Math.max(1, hon * 0.01) && !avisos.includes("regla_distinta_de_lo_liquidado")) avisos.push("regla_distinta_de_lo_liquidado");
|
|
44600
|
-
detalle.push({ profesional_id: h.profesional_id, profesional: nombre, honorario: hon, fuente: "liquidado", detalle: `liquidado por el HIS: ${
|
|
44629
|
+
detalle.push({ profesional_id: h.profesional_id, profesional: nombre, honorario: hon, fuente: "liquidado", detalle: `liquidado por el HIS: ${pesos4(hon)} por pr\xE1ctica${deRegla ? ` (la regla da ${pesos4(deRegla.honorario)}: ${deRegla.detalle})` : ""}` });
|
|
44601
44630
|
} else if (!h.regla) {
|
|
44602
44631
|
falta("sin_regla_honorario");
|
|
44603
44632
|
debajo.push(`${nombre} (sin regla)`);
|
|
@@ -44608,10 +44637,10 @@ function evaluarRentabilidad(input2) {
|
|
|
44608
44637
|
debajo.push(`${nombre} (regla sin valor)`);
|
|
44609
44638
|
detalle.push({ profesional_id: h.profesional_id, profesional: nombre, honorario: null, fuente: null, detalle: `la regla ${h.regla.id} del HIS no tiene honorario, \xABprof\xBB ni \xABinst\xBB` });
|
|
44610
44639
|
continue;
|
|
44611
|
-
} else if (valor > 0 && deRegla.honorario < valor * HONORARIO_MINIMO_PCT / 100) {
|
|
44640
|
+
} else if (valor > 0 && (deRegla.honorario < valor * HONORARIO_MINIMO_PCT / 100 || deRegla.fuente === "monto_fijo" && deRegla.honorario < valor * HONORARIO_FIJO_MINIMO_PCT / 100)) {
|
|
44612
44641
|
falta("honorario_a_confirmar");
|
|
44613
|
-
debajo.push(`${nombre} (honorario de ${
|
|
44614
|
-
detalle.push({ profesional_id: h.profesional_id, profesional: nombre, honorario: deRegla.honorario, fuente: deRegla.fuente, detalle: `regla ${h.regla.id}: ${deRegla.detalle}. Es menos del ${HONORARIO_MINIMO_PCT} % de lo que
|
|
44642
|
+
debajo.push(`${nombre} (honorario de ${pesos4(deRegla.honorario)}: a confirmar)`);
|
|
44643
|
+
detalle.push({ profesional_id: h.profesional_id, profesional: nombre, honorario: deRegla.honorario, fuente: deRegla.fuente, detalle: `regla ${h.regla.id}: ${deRegla.detalle}. Es menos del ${deRegla.fuente === "monto_fijo" ? HONORARIO_FIJO_MINIMO_PCT : HONORARIO_MINIMO_PCT} % de lo que se cobra: hay que confirmar si est\xE1 bien cargada (\xBFun porcentaje cargado como pesos?)` });
|
|
44615
44644
|
continue;
|
|
44616
44645
|
} else {
|
|
44617
44646
|
hon = deRegla.honorario;
|
|
@@ -44625,13 +44654,15 @@ function evaluarRentabilidad(input2) {
|
|
|
44625
44654
|
const totalVol = conHonorario.reduce((a, h) => a + (h.volumen ?? 0), 0);
|
|
44626
44655
|
const segunQuien = conHonorario.length > 0 && conHonorario.every((h) => h.volumen !== null && h.volumen > 0) && totalVol > 0;
|
|
44627
44656
|
const honorarioBase = !honorarios.length ? null : segunQuien ? redondear5(conHonorario.reduce((a, h) => a + h.honorario * h.volumen, 0) / totalVol) : honorarioMax;
|
|
44657
|
+
const descuentoPct = input2.descuento !== void 0 && input2.descuento > 0 && input2.descuento < 100 ? input2.descuento : 0;
|
|
44658
|
+
const descuento = redondear5(valor * descuentoPct / 100);
|
|
44628
44659
|
const ind = indirectosDe(l, valor, input2.indirectos ?? [], input2.bases ?? null);
|
|
44629
44660
|
for (const a of ind.avisos) if (!avisos.includes(a)) avisos.push(a);
|
|
44630
44661
|
const medidoCon = input2.medirCon === "total" && input2.bases ? "total" : "directo";
|
|
44631
44662
|
const indUsado = medidoCon === "total" ? ind.total : 0;
|
|
44632
|
-
const margen = honorarioBase !== null && valor > 0 ? redondear5(valor - insumos - honorarioBase) : null;
|
|
44633
|
-
const margenPeor = honorarioMax !== null && valor > 0 ? redondear5(valor - insumos - honorarioMax) : null;
|
|
44634
|
-
const margenMejor = honorarioMin !== null && valor > 0 ? redondear5(valor - insumos - honorarioMin) : null;
|
|
44663
|
+
const margen = honorarioBase !== null && valor > 0 ? redondear5(valor - descuento - insumos - honorarioBase) : null;
|
|
44664
|
+
const margenPeor = honorarioMax !== null && valor > 0 ? redondear5(valor - descuento - insumos - honorarioMax) : null;
|
|
44665
|
+
const margenMejor = honorarioMin !== null && valor > 0 ? redondear5(valor - descuento - insumos - honorarioMin) : null;
|
|
44635
44666
|
const margenTotal = margen !== null ? redondear5(margen - ind.total) : null;
|
|
44636
44667
|
const medido = margen !== null ? redondear5(margen - indUsado) : null;
|
|
44637
44668
|
const pctDe = (m) => m !== null && valor > 0 ? Math.round(m / valor * 1e3) / 10 : null;
|
|
@@ -44640,23 +44671,31 @@ function evaluarRentabilidad(input2) {
|
|
|
44640
44671
|
const impacto = porMes(medido, l.volumen);
|
|
44641
44672
|
const impactoPeor = margenPeor !== null ? porMes(margenPeor - indUsado, l.volumen) : null;
|
|
44642
44673
|
for (const h of conHonorario) {
|
|
44643
|
-
const m = valor - insumos - indUsado - h.honorario;
|
|
44674
|
+
const m = valor - descuento - insumos - indUsado - h.honorario;
|
|
44644
44675
|
if (valor > 0 && m / valor * 100 < umbral) debajo.push(h.profesional);
|
|
44645
44676
|
}
|
|
44646
44677
|
let clase2;
|
|
44647
44678
|
if (faltas.length > 0 || margenPct === null) clase2 = "datos_incompletos";
|
|
44648
44679
|
else if (medido < 0) clase2 = "perdida";
|
|
44649
44680
|
else if (margenPct < umbral) clase2 = "margen_bajo";
|
|
44650
|
-
else
|
|
44681
|
+
else if (l.insumos.length === 0 && !input2.sinInsumos) {
|
|
44682
|
+
clase2 = "datos_incompletos";
|
|
44683
|
+
faltas.push("insumos_sin_cargar");
|
|
44684
|
+
} else clase2 = "rentable";
|
|
44685
|
+
if (input2.sinInsumos) {
|
|
44686
|
+
const i = avisos.indexOf("sin_insumos");
|
|
44687
|
+
if (i >= 0) avisos.splice(i, 1);
|
|
44688
|
+
}
|
|
44689
|
+
if (valor > 0 && (insumos / valor * 100 > INSUMOS_A_VERIFICAR_PCT || honorarioMax !== null && honorarioMax > valor)) avisos.push("dato_a_verificar");
|
|
44651
44690
|
const reparto = valor > 0 ? conHonorario.map((h) => {
|
|
44652
|
-
const m = redondear5(valor - insumos - indUsado - h.honorario);
|
|
44691
|
+
const m = redondear5(valor - descuento - insumos - indUsado - h.honorario);
|
|
44653
44692
|
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 };
|
|
44654
44693
|
}) : [];
|
|
44655
44694
|
let causa = null;
|
|
44656
44695
|
let causaProfesionales = [];
|
|
44657
44696
|
const minimo = valor * umbral / 100;
|
|
44658
44697
|
if ((clase2 === "perdida" || clase2 === "margen_bajo") && honorarioBase !== null) {
|
|
44659
|
-
const sinInsumos = valor - honorarioBase - indUsado;
|
|
44698
|
+
const sinInsumos = valor - descuento - honorarioBase - indUsado;
|
|
44660
44699
|
const llegan = reparto.filter((r) => r.margen >= minimo);
|
|
44661
44700
|
const noLlegan = reparto.filter((r) => r.margen < minimo);
|
|
44662
44701
|
if (insumos > 0 && sinInsumos >= minimo) causa = "insumos";
|
|
@@ -44667,8 +44706,9 @@ function evaluarRentabilidad(input2) {
|
|
|
44667
44706
|
else causa = esParticular(l.financiador) ? "precio_particular" : "valor_convenio";
|
|
44668
44707
|
}
|
|
44669
44708
|
const costos = honorarioBase !== null ? honorarioBase + insumos + indUsado : null;
|
|
44670
|
-
const valorObjetivo = costos !== null && umbral < 100 ? Math.ceil(costos / (1 - umbral / 100)) : null;
|
|
44671
|
-
const honorarioObjetivo = valor > 0 ? Math.max(0, Math.floor(valor * (1 - umbral / 100) - insumos - indUsado)) : null;
|
|
44709
|
+
const valorObjetivo = costos !== null && umbral + descuentoPct < 100 ? Math.ceil(costos / (1 - (umbral + descuentoPct) / 100)) : null;
|
|
44710
|
+
const honorarioObjetivo = valor > 0 ? Math.max(0, Math.floor(valor * (1 - umbral / 100) - descuento - insumos - indUsado)) : null;
|
|
44711
|
+
const insumosObjetivo = valor > 0 && honorarioBase !== null ? Math.max(0, Math.floor(valor * (1 - umbral / 100) - descuento - honorarioBase - indUsado)) : null;
|
|
44672
44712
|
const e = {
|
|
44673
44713
|
clase: clase2,
|
|
44674
44714
|
valor,
|
|
@@ -44696,6 +44736,10 @@ function evaluarRentabilidad(input2) {
|
|
|
44696
44736
|
causa_profesionales: causaProfesionales,
|
|
44697
44737
|
valor_objetivo: clase2 === "perdida" || clase2 === "margen_bajo" ? valorObjetivo : null,
|
|
44698
44738
|
honorario_objetivo: causa === "honorario_profesional" ? honorarioObjetivo : null,
|
|
44739
|
+
insumos_objetivo: causa === "insumos" ? insumosObjetivo : null,
|
|
44740
|
+
descuento,
|
|
44741
|
+
descuento_pct: descuentoPct,
|
|
44742
|
+
no_es_practica: noEsPractica(l),
|
|
44699
44743
|
perdida_menor: impacto !== null && impacto < 0 && Math.abs(impacto) < perdidaMenor,
|
|
44700
44744
|
porque: null,
|
|
44701
44745
|
faltas,
|
|
@@ -44713,7 +44757,8 @@ var NOMBRE_FALTA = {
|
|
|
44713
44757
|
insumo_sin_precio: "hay insumos sin precio",
|
|
44714
44758
|
sin_regla_honorario: "hay profesionales sin regla de honorarios",
|
|
44715
44759
|
sin_profesional: "se factur\xF3 sin un profesional (si es un medicamento o un descartable, no corresponde medirla como pr\xE1ctica)",
|
|
44716
|
-
regla_sin_valor: "hay reglas de honorarios sin valor"
|
|
44760
|
+
regla_sin_valor: "hay reglas de honorarios sin valor",
|
|
44761
|
+
insumos_sin_cargar: "no tiene los insumos cargados (sin ellos no se puede afirmar que deje margen; si no usa insumos, marcalo as\xED)"
|
|
44717
44762
|
};
|
|
44718
44763
|
var porcentaje = (n2) => `${n2.toLocaleString("es-AR", { maximumFractionDigits: 1 })} %`;
|
|
44719
44764
|
function porqueRentabilidad(l, e) {
|
|
@@ -44723,16 +44768,16 @@ function porqueRentabilidad(l, e) {
|
|
|
44723
44768
|
const otro = [...e.reparto].sort((a, b) => b.margen - a.margen)[0];
|
|
44724
44769
|
if (!p) return null;
|
|
44725
44770
|
const quienes = e.causa_profesionales.length > 1 ? `${p.profesional} y ${e.causa_profesionales.length - 1} m\xE1s` : p.profesional;
|
|
44726
|
-
return `Con ${quienes} (honorario ${
|
|
44771
|
+
return `Con ${quienes} (honorario ${pesos4(p.honorario)}) no llega al m\xEDnimo${otro && otro.profesional_id !== p.profesional_id ? `; con ${otro.profesional} (${pesos4(otro.honorario)}) la misma pr\xE1ctica deja ${pesos4(otro.margen)}` : ""}.`;
|
|
44727
44772
|
}
|
|
44728
44773
|
case "insumos":
|
|
44729
|
-
return `Los insumos (${
|
|
44774
|
+
return `Los insumos (${pesos4(e.insumos)}) son el ${porcentaje(Math.round(e.insumos / e.valor * 1e3) / 10)} de lo que se cobra: sin ellos dejar\xEDa margen${e.insumos_objetivo !== null ? `; para el ${porcentaje(e.umbral)} tendr\xEDan que costar hasta ${pesos4(e.insumos_objetivo)}` : ""}.`;
|
|
44730
44775
|
case "indirectos":
|
|
44731
|
-
return `Deja margen antes de los costos indirectos; los ${
|
|
44776
|
+
return `Deja margen antes de los costos indirectos; los ${pesos4(e.indirectos)} que le tocan por pr\xE1ctica la llevan debajo del m\xEDnimo.`;
|
|
44732
44777
|
case "precio_particular":
|
|
44733
|
-
return `El precio particular (${
|
|
44778
|
+
return `El precio particular (${pesos4(e.valor)}) no cubre el honorario y los insumos${e.valor_objetivo !== null ? `: para el ${porcentaje(e.umbral)} hace falta cobrar ${pesos4(e.valor_objetivo)}` : ""}.`;
|
|
44734
44779
|
case "valor_convenio":
|
|
44735
|
-
return `Lo que paga ${l.financiador} (${
|
|
44780
|
+
return `Lo que paga ${l.financiador} (${pesos4(e.valor)}) no alcanza con ning\xFAn profesional${e.valor_objetivo !== null ? `: para el ${porcentaje(e.umbral)} tendr\xEDa que pagar ${pesos4(e.valor_objetivo)}` : ""}.`;
|
|
44736
44781
|
default:
|
|
44737
44782
|
return null;
|
|
44738
44783
|
}
|
|
@@ -44741,7 +44786,7 @@ function oracionRentabilidad(l, e) {
|
|
|
44741
44786
|
const practica = `${l.nombre} (${l.codigo}) con ${l.financiador}`;
|
|
44742
44787
|
if (e.clase === "datos_incompletos") return `${practica}: no se puede calcular la rentabilidad porque ${e.faltas.map((f) => NOMBRE_FALTA[f]).join(" y ")}.`;
|
|
44743
44788
|
const como = e.calculo === "segun_quien" ? "seg\xFAn qui\xE9n la hizo" : "peor caso: como si todas las hiciera el profesional que m\xE1s cobra";
|
|
44744
|
-
const mensual = e.impacto_mensual !== null && e.impacto_mensual !== 0 ? ` Con el volumen de los \xFAltimos ${e.dias ?? 90} d\xEDas, ${e.impacto_mensual < 0 ? "se pierden" : "deja"} unos ${
|
|
44789
|
+
const mensual = e.impacto_mensual !== null && e.impacto_mensual !== 0 ? ` Con el volumen de los \xFAltimos ${e.dias ?? 90} d\xEDas, ${e.impacto_mensual < 0 ? "se pierden" : "deja"} unos ${pesos4(Math.abs(e.impacto_mensual))} por mes (${como}).` : "";
|
|
44745
44790
|
const porque = porqueRentabilidad(l, e);
|
|
44746
44791
|
const total = e.medido_con === "total" ? " despu\xE9s de los costos indirectos" : "";
|
|
44747
44792
|
if (e.clase === "perdida") return `${practica} da p\xE9rdida${total}.${porque ? ` ${porque}` : ""}${mensual}`;
|
|
@@ -45108,19 +45153,51 @@ function erroresIndirectos(v) {
|
|
|
45108
45153
|
if (new Set(ids).size !== ids.length) errores.push("indirectos: hay dos costos con el mismo id");
|
|
45109
45154
|
return errores;
|
|
45110
45155
|
}
|
|
45156
|
+
function porcentajeDescuento(v) {
|
|
45157
|
+
const n2 = typeof v === "number" ? v : typeof v === "string" && v.trim() !== "" ? Number(v.replace(",", ".")) : NaN;
|
|
45158
|
+
return Number.isFinite(n2) && n2 >= 0 && n2 <= 60 ? Math.round(n2 * 10) / 10 : null;
|
|
45159
|
+
}
|
|
45160
|
+
function leerDescuentos(v) {
|
|
45161
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return {};
|
|
45162
|
+
const out = {};
|
|
45163
|
+
for (const [k, x] of Object.entries(v)) {
|
|
45164
|
+
const p = porcentajeDescuento(x);
|
|
45165
|
+
if (k.trim() && k.length <= 120 && p !== null) out[k.trim()] = p;
|
|
45166
|
+
}
|
|
45167
|
+
return out;
|
|
45168
|
+
}
|
|
45169
|
+
function descuentoDe(op, financiador) {
|
|
45170
|
+
return op.descuentoPorFinanciador[financiador] ?? op.descuento;
|
|
45171
|
+
}
|
|
45172
|
+
var validarDescuento = (v) => porcentajeDescuento(v);
|
|
45111
45173
|
function opcionesRentabilidad(op) {
|
|
45112
45174
|
return {
|
|
45113
45175
|
umbral: enteroEntre(op.umbral_margen, 0, 90) ?? UMBRAL_MARGEN_POR_DEFECTO,
|
|
45114
45176
|
mesesPrecio: enteroEntre(op.meses_precio, 1, 36) ?? MESES_PRECIO_POR_DEFECTO,
|
|
45115
45177
|
indirectos: leerIndirectos(op.indirectos),
|
|
45116
45178
|
medirCon: op.medir_con === "total" ? "total" : "directo",
|
|
45117
|
-
perdidaMenor: enteroEntre(op.perdida_menor, 0, 1e7) ?? PERDIDA_MENOR_POR_DEFECTO
|
|
45179
|
+
perdidaMenor: enteroEntre(op.perdida_menor, 0, 1e7) ?? PERDIDA_MENOR_POR_DEFECTO,
|
|
45180
|
+
sinInsumos: Array.isArray(op.practicas_sin_insumos) ? op.practicas_sin_insumos.map(String).slice(0, 5e3) : [],
|
|
45181
|
+
descuento: porcentajeDescuento(op.descuento_cobro) ?? 0,
|
|
45182
|
+
descuentoPorFinanciador: leerDescuentos(op.descuentos_financiador)
|
|
45118
45183
|
};
|
|
45119
45184
|
}
|
|
45120
45185
|
async function leerBases(ctx, companyId) {
|
|
45121
45186
|
const r = (await ctx.db.query(`SELECT bases FROM ${t3(ctx, "rentabilidad_base")} WHERE company_id = $1 LIMIT 1`, [companyId]))[0];
|
|
45122
45187
|
return r && r.bases && typeof r.bases === "object" ? r.bases : null;
|
|
45123
45188
|
}
|
|
45189
|
+
async function recalcularBases(ctx, companyId, costos) {
|
|
45190
|
+
const ms = await listarMediciones(ctx, companyId);
|
|
45191
|
+
if (!ms.length) return null;
|
|
45192
|
+
const lecturas = ms.map((m) => ({ minutos: m.minutos, tipo: m.tipo, volumen: m.evaluacion.volumen ?? 0, dias: m.evaluacion.dias ?? 90, componentes: m.componentes }));
|
|
45193
|
+
const bases = basesIndirectos(lecturas, costos);
|
|
45194
|
+
await ctx.db.execute(
|
|
45195
|
+
`INSERT INTO ${t3(ctx, "rentabilidad_base")} (company_id, bases, practicas, calculada_el) VALUES ($1, $2::jsonb, $3, now())
|
|
45196
|
+
ON CONFLICT (company_id) DO UPDATE SET bases = EXCLUDED.bases, practicas = EXCLUDED.practicas, calculada_el = now()`,
|
|
45197
|
+
[companyId, JSON.stringify(bases), lecturas.length]
|
|
45198
|
+
);
|
|
45199
|
+
return bases;
|
|
45200
|
+
}
|
|
45124
45201
|
async function guardarBases(ctx, companyId, lecturas, costos) {
|
|
45125
45202
|
const bases = basesIndirectos(lecturas, costos);
|
|
45126
45203
|
await ctx.db.execute(
|
|
@@ -45155,7 +45232,7 @@ async function listarMediciones(ctx, companyId) {
|
|
|
45155
45232
|
}
|
|
45156
45233
|
async function ultimosPedidos(ctx, companyId) {
|
|
45157
45234
|
const filas = await ctx.db.query(
|
|
45158
|
-
`SELECT DISTINCT ON (referencia) referencia, estado, cerrado_at, datos->>'aceptada_hasta' AS aceptada_hasta, datos->>'perdida_aceptada' AS perdida_aceptada
|
|
45235
|
+
`SELECT DISTINCT ON (referencia) referencia, estado, cerrado_at, datos->>'aceptada_hasta' AS aceptada_hasta, datos->>'perdida_aceptada' AS perdida_aceptada, datos->>'calculo_aceptado' AS calculo_aceptado
|
|
45159
45236
|
FROM ${t3(ctx, "caso")} WHERE company_id = $1 AND workflow = 'rentabilidad_practica' AND referencia IS NOT NULL
|
|
45160
45237
|
ORDER BY referencia, created_at DESC`,
|
|
45161
45238
|
[companyId]
|
|
@@ -45163,11 +45240,12 @@ ORDER BY referencia, created_at DESC`,
|
|
|
45163
45240
|
const out = /* @__PURE__ */ new Map();
|
|
45164
45241
|
for (const r of filas) {
|
|
45165
45242
|
const perdida = r.perdida_aceptada === null || r.perdida_aceptada === void 0 ? null : Number(r.perdida_aceptada);
|
|
45166
|
-
out.set(String(r.referencia), { estado: String(r.estado), abierto: !r.cerrado_at, aceptada_hasta: r.aceptada_hasta ?? null, perdida_aceptada: Number.isFinite(perdida) ? perdida : null });
|
|
45243
|
+
out.set(String(r.referencia), { estado: String(r.estado), abierto: !r.cerrado_at, aceptada_hasta: r.aceptada_hasta ?? null, perdida_aceptada: Number.isFinite(perdida) ? perdida : null, calculo_aceptado: r.calculo_aceptado ?? null });
|
|
45167
45244
|
}
|
|
45168
45245
|
return out;
|
|
45169
45246
|
}
|
|
45170
45247
|
function decidirApertura(e, ultimo, hoy) {
|
|
45248
|
+
if (e.no_es_practica) return { abrir: false, motivo: "es un medicamento o un descartable facturado suelto: no se mide como pr\xE1ctica" };
|
|
45171
45249
|
const mala = e.clase === "perdida" || e.clase === "margen_bajo";
|
|
45172
45250
|
if (!ultimo) return { abrir: true, motivo: "no tiene pedido" };
|
|
45173
45251
|
if (ultimo.abierto) return { abrir: false, motivo: "tiene un pedido abierto: se vuelve a medir ese" };
|
|
@@ -45176,7 +45254,8 @@ function decidirApertura(e, ultimo, hoy) {
|
|
|
45176
45254
|
if (ultimo.aceptada_hasta && hoy >= ultimo.aceptada_hasta) return { abrir: true, motivo: `venci\xF3 la aceptaci\xF3n (${ultimo.aceptada_hasta})` };
|
|
45177
45255
|
const antes = ultimo.perdida_aceptada;
|
|
45178
45256
|
const ahora = e.impacto_mensual;
|
|
45179
|
-
|
|
45257
|
+
const mismoCalculo = !ultimo.calculo_aceptado || !e.calculo || ultimo.calculo_aceptado === e.calculo;
|
|
45258
|
+
if (mismoCalculo && antes !== null && antes < 0 && ahora !== null && ahora < antes * (1 + CRECIMIENTO_QUE_REABRE)) return { abrir: true, motivo: "la p\xE9rdida por mes creci\xF3 m\xE1s de un 20 % desde que la acept\xF3 la direcci\xF3n" };
|
|
45180
45259
|
if (!ultimo.aceptada_hasta && antes === null) return { abrir: false, motivo: "la direcci\xF3n la acept\xF3 as\xED" };
|
|
45181
45260
|
return { abrir: false, motivo: `aceptada por la direcci\xF3n${ultimo.aceptada_hasta ? ` hasta el ${ultimo.aceptada_hasta}` : ""}` };
|
|
45182
45261
|
}
|
|
@@ -45248,12 +45327,12 @@ var ABREN = /* @__PURE__ */ new Set(["ingreso", "pase_cama", "reingreso"]);
|
|
|
45248
45327
|
var CIERRAN = /* @__PURE__ */ new Set(["egreso"]);
|
|
45249
45328
|
var ORDEN_EVENTO = { egreso: 0, ingreso: 1, reingreso: 1, pase_cama: 2 };
|
|
45250
45329
|
var claveServicio = (s) => s.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/\s+/g, " ").trim();
|
|
45251
|
-
function repartir2(
|
|
45252
|
-
const suma =
|
|
45253
|
-
const exactas =
|
|
45330
|
+
function repartir2(pesos5, unidades) {
|
|
45331
|
+
const suma = pesos5.reduce((a, b) => a + b, 0);
|
|
45332
|
+
const exactas = pesos5.map((p) => suma > 0 ? p / suma * unidades : 0);
|
|
45254
45333
|
const enteras = exactas.map((x) => Math.floor(x));
|
|
45255
45334
|
let resto = unidades - enteras.reduce((a, b) => a + b, 0);
|
|
45256
|
-
const orden =
|
|
45335
|
+
const orden = pesos5.map((_, i) => i).sort((a, b) => exactas[b] - enteras[b] - (exactas[a] - enteras[a]) || pesos5[b] - pesos5[a] || a - b);
|
|
45257
45336
|
for (const i of orden) {
|
|
45258
45337
|
if (resto <= 0) break;
|
|
45259
45338
|
enteras[i] += 1;
|
|
@@ -45261,12 +45340,12 @@ function repartir2(pesos4, unidades) {
|
|
|
45261
45340
|
}
|
|
45262
45341
|
return enteras;
|
|
45263
45342
|
}
|
|
45264
|
-
function repartirDecimas(
|
|
45343
|
+
function repartirDecimas(pesos5, total) {
|
|
45265
45344
|
const decimas = Math.round(total * 10);
|
|
45266
|
-
if (
|
|
45267
|
-
const suma =
|
|
45268
|
-
const excedentes =
|
|
45269
|
-
return repartir2(excedentes, decimas -
|
|
45345
|
+
if (pesos5.length > decimas) return repartir2(pesos5, decimas);
|
|
45346
|
+
const suma = pesos5.reduce((a, b) => a + b, 0);
|
|
45347
|
+
const excedentes = pesos5.map((p) => Math.max(0, p / suma * decimas - 1));
|
|
45348
|
+
return repartir2(excedentes, decimas - pesos5.length).map((d) => d + 1);
|
|
45270
45349
|
}
|
|
45271
45350
|
function diasPorServicio(l, hoy) {
|
|
45272
45351
|
const inicio = Date.parse(l.ingreso);
|
|
@@ -45560,6 +45639,19 @@ function describirOpciones(antes, despues) {
|
|
|
45560
45639
|
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)}`);
|
|
45561
45640
|
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)");
|
|
45562
45641
|
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")}`);
|
|
45642
|
+
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`);
|
|
45643
|
+
if (JSON.stringify(antes.descuentos_financiador ?? {}) !== JSON.stringify(despues.descuentos_financiador ?? {}) && despues.descuentos_financiador !== void 0) {
|
|
45644
|
+
const d = Object.entries(despues.descuentos_financiador ?? {});
|
|
45645
|
+
partes.push(`descuento por financiador: ${d.length ? d.map(([f, p]) => `${f} ${p.toLocaleString("es-AR")} %`).join(", ") : "ninguno"}`);
|
|
45646
|
+
}
|
|
45647
|
+
if (JSON.stringify(antes.practicas_sin_insumos ?? []) !== JSON.stringify(despues.practicas_sin_insumos ?? []) && despues.practicas_sin_insumos !== void 0) {
|
|
45648
|
+
const a = new Set(Array.isArray(antes.practicas_sin_insumos) ? antes.practicas_sin_insumos : []);
|
|
45649
|
+
const b = Array.isArray(despues.practicas_sin_insumos) ? despues.practicas_sin_insumos : [];
|
|
45650
|
+
const nuevas = b.filter((c) => !a.has(c));
|
|
45651
|
+
const quitadas = [...a].filter((c) => !b.includes(c));
|
|
45652
|
+
if (nuevas.length) partes.push(`no usan insumos: ${nuevas.join(", ")}`);
|
|
45653
|
+
if (quitadas.length) partes.push(`vuelven a necesitar insumos: ${quitadas.join(", ")}`);
|
|
45654
|
+
}
|
|
45563
45655
|
if (JSON.stringify(antes.indirectos ?? []) !== JSON.stringify(despues.indirectos ?? []) && despues.indirectos !== void 0) {
|
|
45564
45656
|
const lista3 = Array.isArray(despues.indirectos) ? despues.indirectos : [];
|
|
45565
45657
|
partes.push(`costos indirectos: ${lista3.length ? lista3.map((c) => `${String(c.nombre)} $ ${Number(c.mensual).toLocaleString("es-AR")} por mes${c.activo === false ? " (apagado)" : ""}`).join(", ") : "ninguno"}`);
|
|
@@ -45605,8 +45697,21 @@ function pareceNoProfesional(nombre) {
|
|
|
45605
45697
|
const [apellido = "", nombres = ""] = n2.split(",").map((x) => x.trim().toLowerCase());
|
|
45606
45698
|
return /^[^a-záéíóúñü]/i.test(n2) || /\d/.test(n2) || apellido !== "" && apellido === nombres || /\b(admin|administrador|prueba|test|sistema|usuario)\b/i.test(n2);
|
|
45607
45699
|
}
|
|
45700
|
+
function resultadoPedido(d, e) {
|
|
45701
|
+
const accion = str2(d.accion) ?? "pedir_aumento";
|
|
45702
|
+
const pedido = typeof d.precio_propuesto === "number" ? d.precio_propuesto : null;
|
|
45703
|
+
if (accion === "subir_precio" || accion === "pedir_aumento") return { accion, pedido, ahora: e.valor, cumple: pedido !== null ? e.valor >= pedido - 0.5 : null };
|
|
45704
|
+
if (accion === "revisar_honorario") {
|
|
45705
|
+
const previo = d.evaluacion && typeof d.evaluacion === "object" ? d.evaluacion : {};
|
|
45706
|
+
const objetivo = typeof previo.honorario_objetivo === "number" ? previo.honorario_objetivo : null;
|
|
45707
|
+
const prof = Array.isArray(previo.causa_profesionales) ? previo.causa_profesionales[0] : void 0;
|
|
45708
|
+
const ahora = prof ? e.reparto.find((r) => r.profesional_id === prof.profesional_id)?.honorario ?? null : null;
|
|
45709
|
+
return { accion, pedido: objetivo, ahora, cumple: objetivo !== null && ahora !== null ? ahora <= objetivo + 0.5 : null };
|
|
45710
|
+
}
|
|
45711
|
+
return { accion, pedido: null, ahora: null, cumple: e.clase === "rentable" ? true : null };
|
|
45712
|
+
}
|
|
45608
45713
|
function evaluarConOpciones(l, hoy, op, bases) {
|
|
45609
|
-
return evaluarRentabilidad({ lectura: l, hoy, umbral: op.umbral, mesesPrecio: op.mesesPrecio, indirectos: op.indirectos, bases, medirCon: op.medirCon, perdidaMenor: op.perdidaMenor });
|
|
45714
|
+
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) });
|
|
45610
45715
|
}
|
|
45611
45716
|
async function evaluarRentabilidades(ctx, companyId, lecturas) {
|
|
45612
45717
|
if (!Array.isArray(lecturas)) throw new OpsError("validacion", "lecturas: tiene que ser una lista");
|
|
@@ -45638,7 +45743,7 @@ async function evaluarRentabilidades(ctx, companyId, lecturas) {
|
|
|
45638
45743
|
antes: previa ? { valor: previa.evaluacion.valor, honorarios: (previa.evaluacion.honorarios ?? []).map((h) => ({ profesional_id: h.profesional_id, profesional: h.profesional, honorario: h.honorario })) } : null,
|
|
45639
45744
|
despues: { valor: evaluacion.valor, honorarios: evaluacion.honorarios.map((h) => ({ profesional_id: h.profesional_id, profesional: h.profesional, honorario: h.honorario })) }
|
|
45640
45745
|
});
|
|
45641
|
-
medidas.push({ lectura: l.lectura, evaluacion });
|
|
45746
|
+
if (!evaluacion.no_es_practica) medidas.push({ lectura: l.lectura, evaluacion });
|
|
45642
45747
|
const apertura = decidirApertura(evaluacion, pedidos.get(referencia) ?? null, hoy);
|
|
45643
45748
|
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 });
|
|
45644
45749
|
}
|
|
@@ -45747,7 +45852,18 @@ async function cambiarPolitica(ctx, input2) {
|
|
|
45747
45852
|
if (v === null) throw new OpsError("validacion", "perdida_menor: tiene que ser un monto entero entre 0 y 10.000.000");
|
|
45748
45853
|
opciones.perdida_menor = v;
|
|
45749
45854
|
}
|
|
45855
|
+
if (opciones.descuento_cobro !== void 0) {
|
|
45856
|
+
const v = validarDescuento(opciones.descuento_cobro);
|
|
45857
|
+
if (v === null) throw new OpsError("validacion", "descuento_cobro: tiene que ser un porcentaje entre 0 y 60");
|
|
45858
|
+
opciones.descuento_cobro = v;
|
|
45859
|
+
}
|
|
45860
|
+
if (opciones.descuentos_financiador !== void 0) opciones.descuentos_financiador = leerDescuentos(opciones.descuentos_financiador);
|
|
45861
|
+
if (opciones.practicas_sin_insumos !== void 0) {
|
|
45862
|
+
if (!Array.isArray(opciones.practicas_sin_insumos)) throw new OpsError("validacion", "practicas_sin_insumos: tiene que ser una lista de c\xF3digos");
|
|
45863
|
+
opciones.practicas_sin_insumos = [...new Set(opciones.practicas_sin_insumos.map((c) => normalizarCodigo(String(c ?? ""))).filter((c) => Boolean(c)))].slice(0, 5e3);
|
|
45864
|
+
}
|
|
45750
45865
|
}
|
|
45866
|
+
if (input2.workflow === "rentabilidad_practica" && JSON.stringify(opcionesAntes.indirectos ?? []) !== JSON.stringify(opciones.indirectos ?? [])) await recalcularBases(ctx, input2.companyId, leerIndirectos(opciones.indirectos));
|
|
45751
45867
|
const cambioOpciones = describirOpciones(opcionesAntes, opciones);
|
|
45752
45868
|
const motivo = [input2.motivo ?? null, cambioOpciones].filter(Boolean).join(" \xB7 ") || null;
|
|
45753
45869
|
const existe = await ctx.db.query(
|
|
@@ -46368,7 +46484,13 @@ async function transicionar(ctx, input2) {
|
|
|
46368
46484
|
delete datos.lectura;
|
|
46369
46485
|
}
|
|
46370
46486
|
if (caso.workflow === "rentabilidad_practica") {
|
|
46371
|
-
for (const k of ["evaluacion", "oracion", "practica", "financiador", "codigo", "componentes", "insumos", "honorarios", "volumen_90d", "comprobado_el", "tipo", "minutos", "aceptada_hasta", "perdida_aceptada", "hecho_el", "pedido_enviado_el"]) delete datos[k];
|
|
46487
|
+
for (const k of ["evaluacion", "oracion", "practica", "financiador", "codigo", "componentes", "insumos", "honorarios", "volumen_90d", "comprobado_el", "tipo", "minutos", "aceptada_hasta", "perdida_aceptada", "calculo_aceptado", "hecho_el", "pedido_enviado_el", "resultado_pedido"]) delete datos[k];
|
|
46488
|
+
if (resultado.via === "no_se_pudo") {
|
|
46489
|
+
datos.no_se_pudo = { motivo: str2(datos.motivo) ?? "", accion: str2(caso.datos.accion) ?? null, el: contexto.hoy ?? ahoraEnHospital().hoy };
|
|
46490
|
+
datos.accion = null;
|
|
46491
|
+
datos.precio_propuesto = null;
|
|
46492
|
+
}
|
|
46493
|
+
if (resultado.via === "aprobar") datos.no_se_pudo = null;
|
|
46372
46494
|
if (resultado.via === "hecho") {
|
|
46373
46495
|
datos.hecho_el = contexto.hoy ?? ahoraEnHospital().hoy;
|
|
46374
46496
|
if (str2(caso.datos.accion) === "pedir_aumento" || !str2(caso.datos.accion)) datos.pedido_enviado_el = datos.hecho_el;
|
|
@@ -46378,6 +46500,7 @@ async function transicionar(ctx, input2) {
|
|
|
46378
46500
|
const ev = caso.datos.evaluacion && typeof caso.datos.evaluacion === "object" ? caso.datos.evaluacion : {};
|
|
46379
46501
|
datos.aceptada_hasta = sumarMeses(contexto.hoy ?? ahoraEnHospital().hoy, meses);
|
|
46380
46502
|
datos.perdida_aceptada = typeof ev.impacto_mensual === "number" ? ev.impacto_mensual : null;
|
|
46503
|
+
datos.calculo_aceptado = typeof ev.calculo === "string" ? ev.calculo : null;
|
|
46381
46504
|
}
|
|
46382
46505
|
const r = contexto.rentabilidad;
|
|
46383
46506
|
const l = leerLecturaRentabilidad(datos.lectura);
|
|
@@ -46394,6 +46517,8 @@ async function transicionar(ctx, input2) {
|
|
|
46394
46517
|
tipo: l.lectura.tipo,
|
|
46395
46518
|
minutos: l.lectura.minutos,
|
|
46396
46519
|
evaluacion: r.evaluacion,
|
|
46520
|
+
// Si el equipo ya hizo lo que pidió la dirección: ¿el HIS tiene el valor pedido? (no solo «cambió la clase»).
|
|
46521
|
+
...caso.datos.hecho_el ? { resultado_pedido: resultadoPedido(caso.datos, r.evaluacion) } : {},
|
|
46397
46522
|
oracion: oracionRentabilidad(l.lectura, r.evaluacion),
|
|
46398
46523
|
comprobado_el: contexto.hoy ?? null
|
|
46399
46524
|
});
|
|
@@ -48519,6 +48644,8 @@ async function manejarApi(ctx, input2) {
|
|
|
48519
48644
|
}
|
|
48520
48645
|
case "cuestionar":
|
|
48521
48646
|
return respuesta(200, await transicionar(ctx, { companyId, casoId: input2.params.casoId ?? "", via: "cuestionar", motivo: motivoConPersona(str3(body.motivo)), datos: conPersona({}), actor }));
|
|
48647
|
+
case "no-se-pudo":
|
|
48648
|
+
return respuesta(200, await transicionar(ctx, { companyId, casoId: input2.params.casoId ?? "", via: "no_se_pudo", motivo: motivoConPersona(str3(body.motivo)), datos: conPersona({ motivo: str3(body.motivo) }), actor }));
|
|
48522
48649
|
case "contactos":
|
|
48523
48650
|
return respuesta(200, await responsablesPendientes(ctx, companyId, (qs("workflows") ?? "").split(",").map((w) => w.trim()).filter(Boolean)));
|
|
48524
48651
|
case "guardar-contacto": {
|
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.58",
|
|
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": [
|