paperclip-plugin-autopilot-ops 0.1.45 → 0.1.47
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 +89 -20
- package/migrations/011_mediciones_no_son_trabajo.sql +3 -0
- 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.47";
|
|
39936
39936
|
var DB_NAMESPACE_SLUG = "autopilot_ops";
|
|
39937
39937
|
var PAGE_ROUTE = "autopilot";
|
|
39938
39938
|
var TOOL_NAMES = {
|
|
@@ -41432,7 +41432,8 @@ function leerRegla(v) {
|
|
|
41432
41432
|
if (!rid) return null;
|
|
41433
41433
|
const prof = num3(r.prof);
|
|
41434
41434
|
const inst = num3(r.inst);
|
|
41435
|
-
|
|
41435
|
+
const importe = num3(r.importe);
|
|
41436
|
+
return { id: rid, prof: prof === null ? null : redondear3(prof), inst: inst === null ? null : redondear3(inst), ...importe !== null ? { importe: redondear3(importe) } : {} };
|
|
41436
41437
|
}
|
|
41437
41438
|
function honorarioDe(input2) {
|
|
41438
41439
|
const { regla, monto, cantidad: cantidad2 } = input2;
|
|
@@ -43592,6 +43593,8 @@ var rentabilidadPractica = {
|
|
|
43592
43593
|
inicial: "detectado",
|
|
43593
43594
|
estados: ["detectado", "pendiente_aprobacion", "datos_incompletos", "completado", "en_renegociacion", "resuelta", "aceptada", "rentable", "descartado"],
|
|
43594
43595
|
finalesOk: ["resuelta", "rentable", "aceptada"],
|
|
43596
|
+
// «Rentable» es el resultado de una lectura: no es un trabajo terminado ni ahorra tiempo al equipo.
|
|
43597
|
+
finalesSinTrabajo: ["rentable"],
|
|
43595
43598
|
finalesNoOk: ["descartado"],
|
|
43596
43599
|
minutosAbsorbidos: 15,
|
|
43597
43600
|
transiciones: [
|
|
@@ -43708,6 +43711,8 @@ var rentabilidadInternacion = {
|
|
|
43708
43711
|
inicial: "detectado",
|
|
43709
43712
|
estados: ["detectado", "pendiente_aprobacion", "datos_incompletos", "completado", "en_revision", "resuelta", "aceptada", "rentable", "descartado"],
|
|
43710
43713
|
finalesOk: ["resuelta", "rentable", "aceptada"],
|
|
43714
|
+
// «Rentable» es el resultado de una lectura: no es un trabajo terminado ni ahorra tiempo al equipo.
|
|
43715
|
+
finalesSinTrabajo: ["rentable"],
|
|
43711
43716
|
finalesNoOk: ["descartado"],
|
|
43712
43717
|
minutosAbsorbidos: 20,
|
|
43713
43718
|
transiciones: [
|
|
@@ -43834,7 +43839,7 @@ function evaluarTransicion(input2) {
|
|
|
43834
43839
|
a,
|
|
43835
43840
|
via,
|
|
43836
43841
|
validaciones,
|
|
43837
|
-
minutosAbsorbidos: esFinalOk ? wf.minutosSegun ? wf.minutosSegun(caso) : wf.minutosAbsorbidos : 0,
|
|
43842
|
+
minutosAbsorbidos: esFinalOk && !wf.finalesSinTrabajo?.includes(a) ? wf.minutosSegun ? wf.minutosSegun(caso) : wf.minutosAbsorbidos : 0,
|
|
43838
43843
|
esFinal: esFinalOk || wf.finalesNoOk.includes(a),
|
|
43839
43844
|
autoaprobado
|
|
43840
43845
|
};
|
|
@@ -44281,6 +44286,19 @@ function dia3(v) {
|
|
|
44281
44286
|
const s = texto5(v);
|
|
44282
44287
|
return s && /^\d{4}-\d{2}-\d{2}/.test(s) ? s.slice(0, 10) : null;
|
|
44283
44288
|
}
|
|
44289
|
+
function honorarioDeRegla(regla, valor) {
|
|
44290
|
+
const pct = (n) => `${n.toLocaleString("es-AR", { maximumFractionDigits: 2 })} %`;
|
|
44291
|
+
const importe = regla.importe ?? null;
|
|
44292
|
+
if (importe !== null && importe > 0) {
|
|
44293
|
+
if (regla.prof !== null) return { honorario: redondear5(importe * regla.prof / 100), fuente: "importe_porcentaje", detalle: `honorario ${pesos2(importe)} \xD7 ${pct(regla.prof)} del profesional` };
|
|
44294
|
+
if (regla.inst !== null) return { honorario: redondear5(importe * (1 - regla.inst / 100)), fuente: "importe", detalle: `honorario ${pesos2(importe)} menos ${pct(regla.inst)} de la instituci\xF3n` };
|
|
44295
|
+
return { honorario: importe, fuente: "importe", detalle: `honorario fijo ${pesos2(importe)}` };
|
|
44296
|
+
}
|
|
44297
|
+
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 ${pesos2(regla.prof)} fijos` };
|
|
44298
|
+
if (regla.inst !== null) return { honorario: redondear5(valor * regla.inst / 100), fuente: "porcentaje", detalle: `${pct(regla.inst)} de lo que paga el convenio` };
|
|
44299
|
+
return null;
|
|
44300
|
+
}
|
|
44301
|
+
var HONORARIO_MINIMO_PCT = 1;
|
|
44284
44302
|
function leerLecturaRentabilidad(v) {
|
|
44285
44303
|
if (!v || typeof v !== "object" || Array.isArray(v)) return { error: "lectura: tiene que ser un objeto" };
|
|
44286
44304
|
const r = v;
|
|
@@ -44313,9 +44331,19 @@ function leerLecturaRentabilidad(v) {
|
|
|
44313
44331
|
const pid = texto5(x.profesional_id);
|
|
44314
44332
|
if (!pid) return { error: `lectura.honorarios[${i}].profesional_id: falta` };
|
|
44315
44333
|
if (x.regla !== null && x.regla !== void 0 && !leerRegla(x.regla)) return { error: `lectura.honorarios[${i}].regla: tiene que ser {id, prof, inst} o null` };
|
|
44316
|
-
|
|
44334
|
+
let liquidado = null;
|
|
44335
|
+
if (x.liquidado !== null && x.liquidado !== void 0) {
|
|
44336
|
+
const lq = x.liquidado ?? {};
|
|
44337
|
+
const h = num6(lq.honorario);
|
|
44338
|
+
const c2 = num6(lq.cantidad);
|
|
44339
|
+
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` };
|
|
44340
|
+
liquidado = { honorario: redondear5(h), cantidad: c2 };
|
|
44341
|
+
}
|
|
44342
|
+
honorarios.push({ profesional_id: pid, profesional: texto5(x.profesional), regla: leerRegla(x.regla), ...liquidado ? { liquidado } : {} });
|
|
44317
44343
|
}
|
|
44318
44344
|
const volumen = num6(r.volumen_90d);
|
|
44345
|
+
const volumenPeriodo = num6(r.volumen);
|
|
44346
|
+
const dias = num6(r.dias);
|
|
44319
44347
|
return {
|
|
44320
44348
|
lectura: {
|
|
44321
44349
|
prestacion_id,
|
|
@@ -44327,10 +44355,14 @@ function leerLecturaRentabilidad(v) {
|
|
|
44327
44355
|
componentes,
|
|
44328
44356
|
insumos,
|
|
44329
44357
|
honorarios,
|
|
44330
|
-
volumen_90d: volumen !== null && volumen > 0 ? Math.round(volumen) : 0
|
|
44358
|
+
volumen_90d: volumen !== null && volumen > 0 ? Math.round(volumen) : 0,
|
|
44359
|
+
// Un solo período: el «por mes» se calcula con el volumen de los mismos días que se midieron.
|
|
44360
|
+
volumen: volumenPeriodo !== null && dias !== null && dias >= 1 ? Math.max(0, Math.round(volumenPeriodo)) : volumen !== null && volumen > 0 ? Math.round(volumen) : 0,
|
|
44361
|
+
dias: volumenPeriodo !== null && dias !== null && dias >= 1 ? Math.round(dias) : 90
|
|
44331
44362
|
}
|
|
44332
44363
|
};
|
|
44333
44364
|
}
|
|
44365
|
+
var esParticular = (financiador) => /\b(sin cobertura|particular(es)?)\b/i.test(financiador);
|
|
44334
44366
|
function mesesEntre(desde, hasta) {
|
|
44335
44367
|
const [y1, m1] = desde.split("-").map(Number);
|
|
44336
44368
|
const [y2, m2] = hasta.split("-").map(Number);
|
|
@@ -44344,7 +44376,7 @@ function evaluarRentabilidad(input2) {
|
|
|
44344
44376
|
const avisos = [];
|
|
44345
44377
|
const insumosARevisar = [];
|
|
44346
44378
|
const valor = redondear5(l.componentes.reduce((a, c) => a + c.monto, 0));
|
|
44347
|
-
if (valor <= 0) faltas.push("sin_valor_convenio");
|
|
44379
|
+
if (valor <= 0) faltas.push(esParticular(l.financiador) ? "particular_sin_precio" : "sin_valor_convenio");
|
|
44348
44380
|
let insumos = 0;
|
|
44349
44381
|
for (const i of l.insumos) {
|
|
44350
44382
|
if (i.precio === null || i.precio <= 0) {
|
|
@@ -44362,20 +44394,38 @@ function evaluarRentabilidad(input2) {
|
|
|
44362
44394
|
if (l.insumos.length === 0) avisos.push("sin_insumos");
|
|
44363
44395
|
const debajo = [];
|
|
44364
44396
|
const honorarios = [];
|
|
44397
|
+
const detalle = [];
|
|
44398
|
+
const falta = (f) => {
|
|
44399
|
+
if (!faltas.includes(f)) faltas.push(f);
|
|
44400
|
+
};
|
|
44365
44401
|
if (l.honorarios.length === 0) faltas.push("sin_profesional");
|
|
44366
44402
|
for (const h of l.honorarios) {
|
|
44367
44403
|
const nombre = h.profesional ?? h.profesional_id;
|
|
44368
|
-
|
|
44369
|
-
|
|
44404
|
+
const deRegla = h.regla ? honorarioDeRegla(h.regla, valor) : null;
|
|
44405
|
+
let hon;
|
|
44406
|
+
if (h.liquidado) {
|
|
44407
|
+
hon = redondear5(h.liquidado.honorario / h.liquidado.cantidad);
|
|
44408
|
+
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");
|
|
44409
|
+
detalle.push({ profesional_id: h.profesional_id, profesional: nombre, honorario: hon, fuente: "liquidado", detalle: `liquidado por el HIS: ${pesos2(hon)} por pr\xE1ctica${deRegla ? ` (la regla da ${pesos2(deRegla.honorario)}: ${deRegla.detalle})` : ""}` });
|
|
44410
|
+
} else if (!h.regla) {
|
|
44411
|
+
falta("sin_regla_honorario");
|
|
44370
44412
|
debajo.push(`${nombre} (sin regla)`);
|
|
44413
|
+
detalle.push({ profesional_id: h.profesional_id, profesional: nombre, honorario: null, fuente: null, detalle: "sin regla de honorarios en el HIS" });
|
|
44371
44414
|
continue;
|
|
44372
|
-
}
|
|
44373
|
-
|
|
44374
|
-
if (!faltas.includes("regla_sin_valor")) faltas.push("regla_sin_valor");
|
|
44415
|
+
} else if (!deRegla) {
|
|
44416
|
+
falta("regla_sin_valor");
|
|
44375
44417
|
debajo.push(`${nombre} (regla sin valor)`);
|
|
44418
|
+
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` });
|
|
44419
|
+
continue;
|
|
44420
|
+
} else if (valor > 0 && deRegla.honorario < valor * HONORARIO_MINIMO_PCT / 100) {
|
|
44421
|
+
falta("honorario_a_confirmar");
|
|
44422
|
+
debajo.push(`${nombre} (honorario de ${pesos2(deRegla.honorario)}: a confirmar)`);
|
|
44423
|
+
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 paga el convenio: hay que confirmar si est\xE1 bien cargada` });
|
|
44376
44424
|
continue;
|
|
44425
|
+
} else {
|
|
44426
|
+
hon = deRegla.honorario;
|
|
44427
|
+
detalle.push({ profesional_id: h.profesional_id, profesional: nombre, honorario: hon, fuente: deRegla.fuente, detalle: `regla ${h.regla.id}: ${deRegla.detalle}` });
|
|
44377
44428
|
}
|
|
44378
|
-
const hon = honorarioDe({ regla: h.regla, monto: valor, cantidad: 1 });
|
|
44379
44429
|
honorarios.push(hon);
|
|
44380
44430
|
const m = valor - insumos - hon;
|
|
44381
44431
|
if (valor > 0 && m / valor * 100 < umbral) debajo.push(nombre);
|
|
@@ -44385,7 +44435,7 @@ function evaluarRentabilidad(input2) {
|
|
|
44385
44435
|
const margenPeor = honorarioMax !== null && valor > 0 ? redondear5(valor - insumos - honorarioMax) : null;
|
|
44386
44436
|
const margenMejor = honorarioMin !== null && valor > 0 ? redondear5(valor - insumos - honorarioMin) : null;
|
|
44387
44437
|
const margenPct = margenPeor !== null && valor > 0 ? Math.round(margenPeor / valor * 1e3) / 10 : null;
|
|
44388
|
-
const impacto = margenPeor !== null ? redondear5(margenPeor * l.
|
|
44438
|
+
const impacto = margenPeor !== null ? redondear5(margenPeor * l.volumen * 30 / l.dias) : null;
|
|
44389
44439
|
let clase2;
|
|
44390
44440
|
if (faltas.length > 0 || margenPct === null) clase2 = "datos_incompletos";
|
|
44391
44441
|
else if (margenPeor < 0) clase2 = "perdida";
|
|
@@ -44402,6 +44452,9 @@ function evaluarRentabilidad(input2) {
|
|
|
44402
44452
|
margen_pct: margenPct,
|
|
44403
44453
|
umbral,
|
|
44404
44454
|
impacto_mensual: impacto,
|
|
44455
|
+
volumen: l.volumen,
|
|
44456
|
+
dias: l.dias,
|
|
44457
|
+
honorarios: detalle,
|
|
44405
44458
|
faltas,
|
|
44406
44459
|
avisos,
|
|
44407
44460
|
insumos_a_revisar: insumosARevisar,
|
|
@@ -44410,6 +44463,8 @@ function evaluarRentabilidad(input2) {
|
|
|
44410
44463
|
}
|
|
44411
44464
|
var NOMBRE_FALTA = {
|
|
44412
44465
|
sin_valor_convenio: "el convenio no tiene valor para esta pr\xE1ctica",
|
|
44466
|
+
particular_sin_precio: "es un particular y la pr\xE1ctica no tiene precio de lista",
|
|
44467
|
+
honorario_a_confirmar: "el honorario que sale de la regla del HIS es menor al 1 % de lo que paga el convenio (hay que confirmar si la regla est\xE1 bien cargada)",
|
|
44413
44468
|
insumo_sin_precio: "hay insumos sin precio",
|
|
44414
44469
|
sin_regla_honorario: "hay profesionales sin regla de honorarios",
|
|
44415
44470
|
sin_profesional: "se factur\xF3 sin un profesional (si es un medicamento o un descartable, no corresponde medirla como pr\xE1ctica)",
|
|
@@ -44420,7 +44475,7 @@ function oracionRentabilidad(l, e) {
|
|
|
44420
44475
|
const practica = `${l.nombre} (${l.codigo}) con ${l.financiador}`;
|
|
44421
44476
|
if (e.clase === "datos_incompletos") return `${practica}: no se puede calcular la rentabilidad porque ${e.faltas.map((f) => NOMBRE_FALTA[f]).join(" y ")}.`;
|
|
44422
44477
|
const rango = e.margen_mejor !== null && e.margen_mejor !== e.margen_peor ? ` (entre ${pesos2(e.margen_peor)} y ${pesos2(e.margen_mejor)} seg\xFAn el profesional)` : "";
|
|
44423
|
-
const mensual = e.impacto_mensual !== null && e.impacto_mensual !== 0 ? ` Con el volumen de los \xFAltimos 90 d\xEDas, ${e.impacto_mensual < 0 ? "se pierden" : "deja"} unos ${pesos2(Math.abs(e.impacto_mensual))} por mes.` : "";
|
|
44478
|
+
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 ${pesos2(Math.abs(e.impacto_mensual))} por mes.` : "";
|
|
44424
44479
|
if (e.clase === "perdida") return `${practica} da p\xE9rdida: el convenio paga ${pesos2(e.valor)} y el honorario m\xE1s los insumos cuestan m\xE1s${rango}.${mensual}`;
|
|
44425
44480
|
if (e.clase === "margen_bajo") return `${practica} deja un margen de ${porcentaje(e.margen_pct)}, debajo del ${porcentaje(e.umbral)} de la instituci\xF3n${rango}.${mensual}`;
|
|
44426
44481
|
return `${practica} deja un margen de ${porcentaje(e.margen_pct)}${rango}.`;
|
|
@@ -44455,6 +44510,7 @@ function leerFilasInsumos(v) {
|
|
|
44455
44510
|
const errores = [];
|
|
44456
44511
|
const filas = [];
|
|
44457
44512
|
const vistas = /* @__PURE__ */ new Set();
|
|
44513
|
+
const repetidas = [];
|
|
44458
44514
|
for (const [i, x] of v.entries()) {
|
|
44459
44515
|
const r = x ?? {};
|
|
44460
44516
|
const n = i + 1;
|
|
@@ -44472,13 +44528,13 @@ function leerFilasInsumos(v) {
|
|
|
44472
44528
|
if (!original || !codigo || !articulo || cantidad2 === null || cantidad2 <= 0) continue;
|
|
44473
44529
|
const k = `${codigo}|${articulo.toLowerCase()}`;
|
|
44474
44530
|
if (vistas.has(k)) {
|
|
44475
|
-
|
|
44531
|
+
repetidas.push(`fila ${n}: \xAB${articulo}\xBB en la pr\xE1ctica ${original}${texto6(r.practica) ? ` (${texto6(r.practica)})` : ""}`);
|
|
44476
44532
|
continue;
|
|
44477
44533
|
}
|
|
44478
44534
|
vistas.add(k);
|
|
44479
44535
|
filas.push({ codigo, codigo_original: original, practica: texto6(r.practica)?.slice(0, 200) ?? null, articulo, cantidad: cantidad2, precio, precio_actualizado: leerFecha(r.precio_actualizado) });
|
|
44480
44536
|
}
|
|
44481
|
-
return errores.length ? { errores } : { filas };
|
|
44537
|
+
return errores.length ? { errores } : { filas, repetidas };
|
|
44482
44538
|
}
|
|
44483
44539
|
async function guardarInsumosPractica(ctx, input2) {
|
|
44484
44540
|
if (input2.actor.tipo !== "humano") throw new OpsError("actor_no_permitido", "los insumos de cada pr\xE1ctica los carga una persona de la instituci\xF3n desde el portal");
|
|
@@ -44493,7 +44549,7 @@ async function guardarInsumosPractica(ctx, input2) {
|
|
|
44493
44549
|
[randomUUID2(), input2.companyId, f.codigo, f.codigo_original, f.practica, f.articulo, f.cantidad, f.precio, f.precio === null ? null : f.precio_actualizado ?? input2.hoy, input2.actor.tipo, input2.actor.id]
|
|
44494
44550
|
);
|
|
44495
44551
|
}
|
|
44496
|
-
return { practicas: codigos.length, insumos: l.filas.length };
|
|
44552
|
+
return { practicas: codigos.length, insumos: l.filas.length, repetidas: l.repetidas.slice(0, 200) };
|
|
44497
44553
|
}
|
|
44498
44554
|
function aFila(r) {
|
|
44499
44555
|
const n = (v) => v === null || v === void 0 ? null : Number(v);
|
|
@@ -44647,8 +44703,9 @@ function evaluarCostoInternacion(input2) {
|
|
|
44647
44703
|
let sinRegla = 0;
|
|
44648
44704
|
for (const f of l.facturado) {
|
|
44649
44705
|
if (!f.profesional_id || f.liquidable === false) continue;
|
|
44650
|
-
|
|
44651
|
-
|
|
44706
|
+
const h = f.regla ? honorarioDeRegla(f.regla, f.monto) : null;
|
|
44707
|
+
if (!h) sinRegla += 1;
|
|
44708
|
+
else honorarios += h.honorario * f.cantidad;
|
|
44652
44709
|
}
|
|
44653
44710
|
honorarios = redondear6(honorarios);
|
|
44654
44711
|
if (sinRegla > 0) avisos.push("prestaciones_sin_regla");
|
|
@@ -46246,6 +46303,9 @@ async function registrarContacto(ctx, input2) {
|
|
|
46246
46303
|
function finalesOk() {
|
|
46247
46304
|
return [...new Set(listarWorkflows().flatMap((w) => w.finalesOk))];
|
|
46248
46305
|
}
|
|
46306
|
+
function finalesSinTrabajo() {
|
|
46307
|
+
return new Set(listarWorkflows().flatMap((w) => w.finalesSinTrabajo ?? []));
|
|
46308
|
+
}
|
|
46249
46309
|
function finalesNoOk() {
|
|
46250
46310
|
return [...new Set(listarWorkflows().flatMap((w) => w.finalesNoOk))];
|
|
46251
46311
|
}
|
|
@@ -46262,7 +46322,8 @@ async function tablero(ctx, companyId, diasInput) {
|
|
|
46262
46322
|
p2
|
|
46263
46323
|
);
|
|
46264
46324
|
const totalRows = await ctx.db.query(
|
|
46265
|
-
|
|
46325
|
+
// Los estados van escritos en la consulta (salen del código, no del usuario): el host no pasa listas como parámetro.
|
|
46326
|
+
`SELECT coalesce(sum(minutos_absorbidos), 0)::int AS minutos FROM ${t3(ctx, "caso")} WHERE company_id = $1${[...finalesSinTrabajo()].filter((e) => /^[a-z_]+$/.test(e)).map((e) => ` AND estado <> '${e}'`).join("")}`,
|
|
46266
46327
|
p1
|
|
46267
46328
|
);
|
|
46268
46329
|
const accionesRows = await ctx.db.query(
|
|
@@ -46275,12 +46336,15 @@ async function tablero(ctx, companyId, diasInput) {
|
|
|
46275
46336
|
listarPoliticas(ctx, companyId)
|
|
46276
46337
|
]);
|
|
46277
46338
|
const okSet = new Set(finalesOk());
|
|
46339
|
+
const sinTrabajo = finalesSinTrabajo();
|
|
46278
46340
|
const noOkSet = new Set(finalesNoOk());
|
|
46279
46341
|
const porEstado = porEstadoRows.map((r) => ({ workflow: String(r.workflow), estado: String(r.estado), cantidad: Number(r.cantidad ?? 0) }));
|
|
46280
46342
|
const abiertosPor = /* @__PURE__ */ new Map();
|
|
46281
46343
|
const pendientesPor = /* @__PURE__ */ new Map();
|
|
46282
46344
|
let conDiferencias = 0;
|
|
46345
|
+
let faltaUnDato = 0;
|
|
46283
46346
|
for (const r of porEstado) {
|
|
46347
|
+
if (r.estado === "datos_incompletos") faltaUnDato += r.cantidad;
|
|
46284
46348
|
if (!okSet.has(r.estado) && !noOkSet.has(r.estado)) abiertosPor.set(r.workflow, (abiertosPor.get(r.workflow) ?? 0) + r.cantidad);
|
|
46285
46349
|
if (r.estado === "pendiente_aprobacion") pendientesPor.set(r.workflow, (pendientesPor.get(r.workflow) ?? 0) + r.cantidad);
|
|
46286
46350
|
if (r.estado === "con_diferencias") conDiferencias += r.cantidad;
|
|
@@ -46291,6 +46355,7 @@ async function tablero(ctx, companyId, diasInput) {
|
|
|
46291
46355
|
for (const r of cerradosRows) {
|
|
46292
46356
|
const wf = String(r.workflow);
|
|
46293
46357
|
const estado = String(r.estado);
|
|
46358
|
+
if (sinTrabajo.has(estado)) continue;
|
|
46294
46359
|
if (okSet.has(estado)) {
|
|
46295
46360
|
cerradosOkPor.set(wf, (cerradosOkPor.get(wf) ?? 0) + Number(r.cantidad ?? 0));
|
|
46296
46361
|
minutosPor.set(wf, (minutosPor.get(wf) ?? 0) + Number(r.minutos ?? 0));
|
|
@@ -46311,6 +46376,8 @@ async function tablero(ctx, companyId, diasInput) {
|
|
|
46311
46376
|
cerradosOkPeriodo: sum(cerradosOkPor),
|
|
46312
46377
|
rechazadosPeriodo,
|
|
46313
46378
|
conDiferencias,
|
|
46379
|
+
// Abiertos que no avanzan hasta que el equipo cargue un dato en el HIS (no están «en curso»).
|
|
46380
|
+
faltaUnDato,
|
|
46314
46381
|
horasAbsorbidasPeriodo: Math.round(sum(minutosPor) / 60 * 10) / 10,
|
|
46315
46382
|
horasAbsorbidasTotal: Math.round(Number(totalRows[0]?.minutos ?? 0) / 60 * 10) / 10,
|
|
46316
46383
|
accionesPeriodo,
|
|
@@ -46351,6 +46418,7 @@ async function serie(ctx, companyId, diasInput) {
|
|
|
46351
46418
|
)
|
|
46352
46419
|
]);
|
|
46353
46420
|
const okSet = new Set(finalesOk());
|
|
46421
|
+
const sinTrabajo = finalesSinTrabajo();
|
|
46354
46422
|
const puntos = /* @__PURE__ */ new Map();
|
|
46355
46423
|
const desde = new Date(Date.now() - (dias - 1) * 864e5);
|
|
46356
46424
|
for (let i = 0; i < dias; i += 1) {
|
|
@@ -46367,6 +46435,7 @@ async function serie(ctx, companyId, diasInput) {
|
|
|
46367
46435
|
for (const r of abiertos) punto(r.dia).abiertos += Number(r.cantidad ?? 0);
|
|
46368
46436
|
for (const r of cerrados) {
|
|
46369
46437
|
const pt = punto(r.dia);
|
|
46438
|
+
if (sinTrabajo.has(String(r.estado))) continue;
|
|
46370
46439
|
if (okSet.has(String(r.estado))) {
|
|
46371
46440
|
pt.cerradosOk += Number(r.cantidad ?? 0);
|
|
46372
46441
|
pt.minutos += Number(r.minutos ?? 0);
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
-- Una medición que da «rentable» es una lectura, no un trabajo hecho: no ahorra minutos al equipo (prueba LALCEC-QA).
|
|
2
|
+
UPDATE plugin_autopilot_ops_1a68482ede.caso SET minutos_absorbidos = 0
|
|
3
|
+
WHERE workflow IN ('rentabilidad_practica', 'rentabilidad_internacion') AND estado = 'rentable';
|
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.47",
|
|
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": [
|