paperclip-plugin-autopilot-ops 0.1.32 → 0.1.33
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 +349 -47
- 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.33";
|
|
39936
39936
|
var DB_NAMESPACE_SLUG = "autopilot_ops";
|
|
39937
39937
|
var PAGE_ROUTE = "autopilot";
|
|
39938
39938
|
var TOOL_NAMES = {
|
|
@@ -43110,7 +43110,7 @@ var destinoRentabilidad = (caso, _datos, contexto) => {
|
|
|
43110
43110
|
var rentabilidadPractica = {
|
|
43111
43111
|
clave: "rentabilidad_practica",
|
|
43112
43112
|
nombre: "Rentabilidad por pr\xE1ctica",
|
|
43113
|
-
puesto: "
|
|
43113
|
+
puesto: "Costos y rentabilidad",
|
|
43114
43114
|
descripcion: "Calcula, para cada pr\xE1ctica y cada financiador, lo que deja: lo que paga el convenio menos el honorario del profesional (con la regla del HIS) y los insumos. Marca las que dan p\xE9rdida o quedan debajo del umbral de la instituci\xF3n, y las que no se pueden calcular porque faltan datos en el HIS. Decide la direcci\xF3n: renegociar con el financiador o aceptarla. El puesto no escribe en el sistema.",
|
|
43115
43115
|
inicial: "detectado",
|
|
43116
43116
|
estados: ["detectado", "pendiente_aprobacion", "datos_incompletos", "completado", "en_renegociacion", "resuelta", "aceptada", "rentable", "descartado"],
|
|
@@ -43165,7 +43165,82 @@ var rentabilidadPractica = {
|
|
|
43165
43165
|
}
|
|
43166
43166
|
]
|
|
43167
43167
|
};
|
|
43168
|
-
|
|
43168
|
+
function validarLecturaCostoInternacion(caso, datos, contexto) {
|
|
43169
|
+
const r = contexto.costoInternacion;
|
|
43170
|
+
const propia = texto4(caso.datos.internacion_id) ?? "";
|
|
43171
|
+
return [
|
|
43172
|
+
!r ? falla("lectura_valida", "falta lectura: la internaci\xF3n tal como la devuelve leer-internaciones.mjs") : "error" in r ? falla("lectura_valida", r.error) : ok("lectura_valida", `margen ${r.evaluacion.margen_pct ?? "sin calcular"} %`),
|
|
43173
|
+
r && !("error" in r) && r.internacionId !== propia ? falla("misma_internacion", `la lectura es de la internaci\xF3n ${r.internacionId} y el caso es de la ${propia}`) : ok("misma_internacion"),
|
|
43174
|
+
texto4(datos.lectura_id) ? ok("lectura_registrada") : falla("lectura_registrada", "hace falta lectura_id: la lectura de la base registrada con ops_registrar_accion")
|
|
43175
|
+
];
|
|
43176
|
+
}
|
|
43177
|
+
var destinoCostoInternacion = (caso, _datos, contexto) => {
|
|
43178
|
+
const r = contexto.costoInternacion;
|
|
43179
|
+
if (!r || "error" in r) return caso.estado;
|
|
43180
|
+
const clase2 = r.evaluacion.clase;
|
|
43181
|
+
if (clase2 === "datos_incompletos") return "datos_incompletos";
|
|
43182
|
+
if (clase2 === "rentable") return caso.estado === "detectado" ? "rentable" : "resuelta";
|
|
43183
|
+
return caso.estado === "en_revision" ? "en_revision" : "pendiente_aprobacion";
|
|
43184
|
+
};
|
|
43185
|
+
var rentabilidadInternacion = {
|
|
43186
|
+
clave: "rentabilidad_internacion",
|
|
43187
|
+
nombre: "Rentabilidad por internaci\xF3n",
|
|
43188
|
+
puesto: "Costos y rentabilidad",
|
|
43189
|
+
descripcion: "Calcula, para cada internaci\xF3n egresada, lo que dej\xF3: lo facturado contra lo que cost\xF3 (honorarios con la regla del HIS, insumos que farmacia le entreg\xF3 al paciente a costo de compra, d\xEDas cama y quir\xF3fano con los costos que fija la instituci\xF3n). Las que dan p\xE9rdida o quedan debajo del umbral van a la direcci\xF3n: revisar la cuenta (consumos sin facturar, renegociar el m\xF3dulo con el financiador) o aceptarla as\xED. El puesto no escribe en el sistema.",
|
|
43190
|
+
inicial: "detectado",
|
|
43191
|
+
estados: ["detectado", "pendiente_aprobacion", "datos_incompletos", "completado", "en_revision", "resuelta", "aceptada", "rentable", "descartado"],
|
|
43192
|
+
finalesOk: ["resuelta"],
|
|
43193
|
+
finalesNoOk: ["aceptada", "rentable", "descartado"],
|
|
43194
|
+
minutosAbsorbidos: 20,
|
|
43195
|
+
transiciones: [
|
|
43196
|
+
{
|
|
43197
|
+
via: "evaluar",
|
|
43198
|
+
de: ["detectado"],
|
|
43199
|
+
a: destinoCostoInternacion,
|
|
43200
|
+
actor: ["agente", "humano", "sistema"],
|
|
43201
|
+
nivelRequerido: "observa",
|
|
43202
|
+
descripcion: "Primera lectura de la internaci\xF3n. El plugin calcula el resultado y decide: pendiente de aprobaci\xF3n (p\xE9rdida o margen bajo), datos incompletos o rentable.",
|
|
43203
|
+
validar: validarLecturaCostoInternacion
|
|
43204
|
+
},
|
|
43205
|
+
{ via: "aprobar", de: ["pendiente_aprobacion"], a: "en_revision", actor: ["humano"], descripcion: "La direcci\xF3n pide revisar la cuenta: consumos que no se facturaron o el m\xF3dulo con el financiador. El puesto vuelve a medir." },
|
|
43206
|
+
{
|
|
43207
|
+
via: "rechazar",
|
|
43208
|
+
de: ["pendiente_aprobacion", "en_revision"],
|
|
43209
|
+
a: "aceptada",
|
|
43210
|
+
actor: ["humano"],
|
|
43211
|
+
descripcion: "La direcci\xF3n acepta el resultado de la internaci\xF3n as\xED. Queda con el motivo.",
|
|
43212
|
+
validar: (_caso, datos) => [texto4(datos.motivo) ? ok("motivo_presente") : falla("motivo_presente", "rechazar requiere motivo: por qu\xE9 se acepta el resultado as\xED")]
|
|
43213
|
+
},
|
|
43214
|
+
{
|
|
43215
|
+
via: "completar",
|
|
43216
|
+
de: ["datos_incompletos"],
|
|
43217
|
+
a: "completado",
|
|
43218
|
+
actor: ["humano"],
|
|
43219
|
+
cargaManual: true,
|
|
43220
|
+
descripcion: "Una persona de la instituci\xF3n carg\xF3 lo que faltaba (el costo de un lote en el HIS, o el costo del d\xEDa cama o del quir\xF3fano en Opera). El puesto lo comprueba volviendo a leer.",
|
|
43221
|
+
validar: (_caso, datos) => [texto4(datos.accion_id) ? ok("accion_registrada") : falla("accion_registrada", "completar requiere accion_id de la carga registrada")]
|
|
43222
|
+
},
|
|
43223
|
+
{
|
|
43224
|
+
via: "comprobar",
|
|
43225
|
+
de: ["pendiente_aprobacion", "datos_incompletos", "completado", "en_revision"],
|
|
43226
|
+
a: destinoCostoInternacion,
|
|
43227
|
+
actor: ["agente", "humano", "sistema"],
|
|
43228
|
+
nivelRequerido: "observa",
|
|
43229
|
+
descripcion: "Volver a leer la internaci\xF3n. El plugin decide: resuelta (ya deja margen, por ejemplo porque se factur\xF3 lo que faltaba), sigue pendiente o en revisi\xF3n, o datos incompletos.",
|
|
43230
|
+
validar: validarLecturaCostoInternacion
|
|
43231
|
+
},
|
|
43232
|
+
{
|
|
43233
|
+
via: "descartar",
|
|
43234
|
+
de: ["detectado", "pendiente_aprobacion", "datos_incompletos", "completado", "en_revision"],
|
|
43235
|
+
a: "descartado",
|
|
43236
|
+
actor: ["agente", "humano"],
|
|
43237
|
+
nivelRequerido: "observa",
|
|
43238
|
+
descripcion: "La internaci\xF3n se anul\xF3 o no corresponde medirla.",
|
|
43239
|
+
validar: (_caso, datos) => [texto4(datos.motivo) ? ok("motivo_presente") : falla("motivo_presente", "descartar requiere motivo")]
|
|
43240
|
+
}
|
|
43241
|
+
]
|
|
43242
|
+
};
|
|
43243
|
+
var WORKFLOWS_SOLO_LECTURA = /* @__PURE__ */ new Set([cuentaCompleta.clave, facturacionPendiente.clave, credencialProfesional.clave, internacionAlDia.clave, honorarioEnCero.clave, rentabilidadPractica.clave, rentabilidadInternacion.clave]);
|
|
43169
43244
|
var WORKFLOWS = {
|
|
43170
43245
|
[actualizacionArancel.clave]: actualizacionArancel,
|
|
43171
43246
|
[tareaOperativa.clave]: tareaOperativa,
|
|
@@ -43180,7 +43255,8 @@ var WORKFLOWS = {
|
|
|
43180
43255
|
[honorarioEnCero.clave]: honorarioEnCero,
|
|
43181
43256
|
[debitoProfesional.clave]: debitoProfesional,
|
|
43182
43257
|
[liquidacionProfesional.clave]: liquidacionProfesional,
|
|
43183
|
-
[rentabilidadPractica.clave]: rentabilidadPractica
|
|
43258
|
+
[rentabilidadPractica.clave]: rentabilidadPractica,
|
|
43259
|
+
[rentabilidadInternacion.clave]: rentabilidadInternacion
|
|
43184
43260
|
};
|
|
43185
43261
|
function getWorkflow(clave) {
|
|
43186
43262
|
return WORKFLOWS[clave] ?? null;
|
|
@@ -43577,8 +43653,8 @@ async function requerirRegla(ctx, companyId, id3) {
|
|
|
43577
43653
|
if (!r) throw new OpsError("regla_inexistente", `no existe la regla ${id3} en esta instituci\xF3n`);
|
|
43578
43654
|
return r;
|
|
43579
43655
|
}
|
|
43580
|
-
function cambio(version2, actor,
|
|
43581
|
-
return { version: version2, fecha: (/* @__PURE__ */ new Date()).toISOString(), actorTipo: actor.tipo, actorId: actor.id, cambio:
|
|
43656
|
+
function cambio(version2, actor, texto10) {
|
|
43657
|
+
return { version: version2, fecha: (/* @__PURE__ */ new Date()).toISOString(), actorTipo: actor.tipo, actorId: actor.id, cambio: texto10 };
|
|
43582
43658
|
}
|
|
43583
43659
|
function soloPersonas(actor, que) {
|
|
43584
43660
|
if (actor.tipo !== "humano") throw new OpsError("actor_no_permitido", `${que} lo hace una persona de la instituci\xF3n desde el portal; el agente solo lee las reglas e informa la prueba`);
|
|
@@ -43921,6 +43997,162 @@ async function insumosPorCodigo(ctx, companyId) {
|
|
|
43921
43997
|
return out;
|
|
43922
43998
|
}
|
|
43923
43999
|
|
|
44000
|
+
// src/costos_internacion.ts
|
|
44001
|
+
var texto7 = (v) => typeof v === "string" && v.trim() ? v.trim() : typeof v === "number" && Number.isFinite(v) ? String(v) : null;
|
|
44002
|
+
var num7 = (v) => {
|
|
44003
|
+
const n = typeof v === "number" ? v : typeof v === "string" && v.trim() ? Number(v) : NaN;
|
|
44004
|
+
return Number.isFinite(n) ? n : null;
|
|
44005
|
+
};
|
|
44006
|
+
var redondear5 = (n) => Math.round(n * 100) / 100;
|
|
44007
|
+
var fechaHora2 = (v) => {
|
|
44008
|
+
const s = texto7(v);
|
|
44009
|
+
return s && /^\d{4}-\d{2}-\d{2}([ T]\d{2}:\d{2}(:\d{2})?)?/.test(s) ? s.replace(" ", "T").slice(0, 19) : null;
|
|
44010
|
+
};
|
|
44011
|
+
function leerLecturaCostoInternacion(v) {
|
|
44012
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return { error: "lectura: tiene que ser un objeto" };
|
|
44013
|
+
const r = v;
|
|
44014
|
+
const internacion_id = texto7(r.internacion_id);
|
|
44015
|
+
const financiador_id = texto7(r.financiador_id);
|
|
44016
|
+
const ingreso = fechaHora2(r.ingreso);
|
|
44017
|
+
if (!internacion_id) return { error: "lectura: falta internacion_id" };
|
|
44018
|
+
if (!financiador_id) return { error: "lectura: falta financiador_id" };
|
|
44019
|
+
if (!ingreso) return { error: "lectura.ingreso: fecha y hora AAAA-MM-DD HH:MM" };
|
|
44020
|
+
for (const k of ["eventos", "facturado", "consumos", "cirugias"]) if (!Array.isArray(r[k])) return { error: `lectura.${k}: tiene que ser una lista` };
|
|
44021
|
+
const eventos = [];
|
|
44022
|
+
for (const [i, x] of r.eventos.entries()) {
|
|
44023
|
+
const e = x ?? {};
|
|
44024
|
+
const fecha5 = fechaHora2(e.fecha);
|
|
44025
|
+
const tipo = texto7(e.tipo);
|
|
44026
|
+
if (!fecha5 || !tipo) return { error: `lectura.eventos[${i}]: faltan fecha o tipo` };
|
|
44027
|
+
eventos.push({ fecha: fecha5, tipo, servicio: texto7(e.servicio) });
|
|
44028
|
+
}
|
|
44029
|
+
const facturado = [];
|
|
44030
|
+
for (const [i, x] of r.facturado.entries()) {
|
|
44031
|
+
const f = x ?? {};
|
|
44032
|
+
const cantidad2 = num7(f.cantidad);
|
|
44033
|
+
const monto = num7(f.monto);
|
|
44034
|
+
if (cantidad2 === null || cantidad2 < 0 || monto === null || monto < 0) return { error: `lectura.facturado[${i}]: cantidad y monto tienen que ser n\xFAmeros \u2265 0` };
|
|
44035
|
+
if (f.regla !== null && f.regla !== void 0 && !leerRegla(f.regla)) return { error: `lectura.facturado[${i}].regla: tiene que ser {id, prof, inst} o null` };
|
|
44036
|
+
facturado.push({ prestacion_id: texto7(f.prestacion_id) ?? String(i), nombre: texto7(f.nombre) ?? "", cantidad: cantidad2, monto: redondear5(monto), profesional_id: texto7(f.profesional_id), regla: leerRegla(f.regla) });
|
|
44037
|
+
}
|
|
44038
|
+
const consumos = [];
|
|
44039
|
+
for (const [i, x] of r.consumos.entries()) {
|
|
44040
|
+
const c = x ?? {};
|
|
44041
|
+
const dispensado = num7(c.dispensado) ?? 0;
|
|
44042
|
+
const registrado = num7(c.registrado) ?? 0;
|
|
44043
|
+
const costo = num7(c.costo_unitario);
|
|
44044
|
+
if (dispensado < 0 && registrado <= 0) continue;
|
|
44045
|
+
if (costo !== null && costo < 0) return { error: `lectura.consumos[${i}].costo_unitario: no puede ser negativo` };
|
|
44046
|
+
consumos.push({ articulo_id: texto7(c.articulo_id) ?? String(i), articulo: texto7(c.articulo) ?? "", dispensado: Math.max(0, dispensado), registrado: Math.max(0, registrado), costo_unitario: costo === null || costo === 0 ? null : costo });
|
|
44047
|
+
}
|
|
44048
|
+
const cirugias = r.cirugias.map((x) => {
|
|
44049
|
+
const m = num7((x ?? {}).minutos);
|
|
44050
|
+
return { minutos: m !== null && m > 0 && m <= 24 * 60 ? Math.round(m) : null };
|
|
44051
|
+
});
|
|
44052
|
+
return { lectura: { internacion_id, financiador_id, financiador: texto7(r.financiador) ?? financiador_id, ingreso, egreso: fechaHora2(r.egreso), eventos, facturado, consumos, cirugias } };
|
|
44053
|
+
}
|
|
44054
|
+
var DIA_MS = 864e5;
|
|
44055
|
+
var ABREN = /* @__PURE__ */ new Set(["ingreso", "pase_cama", "reingreso"]);
|
|
44056
|
+
var CIERRAN = /* @__PURE__ */ new Set(["egreso"]);
|
|
44057
|
+
function diasPorServicio(l, hoy) {
|
|
44058
|
+
const evs = l.eventos.filter((e) => ABREN.has(e.tipo) || CIERRAN.has(e.tipo)).sort((a, b) => a.fecha.localeCompare(b.fecha));
|
|
44059
|
+
const fin = Date.parse(l.egreso ?? `${hoy}T23:59:59`);
|
|
44060
|
+
const tramos = /* @__PURE__ */ new Map();
|
|
44061
|
+
let abierto = null;
|
|
44062
|
+
let ultimoServicio = "Sin servicio";
|
|
44063
|
+
for (const e of evs) {
|
|
44064
|
+
const t6 = Date.parse(e.fecha);
|
|
44065
|
+
if (abierto) tramos.set(abierto.servicio, (tramos.get(abierto.servicio) ?? 0) + Math.max(0, t6 - abierto.desde));
|
|
44066
|
+
abierto = null;
|
|
44067
|
+
if (ABREN.has(e.tipo)) {
|
|
44068
|
+
ultimoServicio = e.servicio ?? ultimoServicio;
|
|
44069
|
+
abierto = { desde: t6, servicio: ultimoServicio };
|
|
44070
|
+
}
|
|
44071
|
+
}
|
|
44072
|
+
if (abierto) tramos.set(abierto.servicio, (tramos.get(abierto.servicio) ?? 0) + Math.max(0, fin - abierto.desde));
|
|
44073
|
+
if (tramos.size === 0) tramos.set(ultimoServicio, Math.max(0, fin - Date.parse(l.ingreso)));
|
|
44074
|
+
const totalMs = [...tramos.values()].reduce((a, b) => a + b, 0);
|
|
44075
|
+
const total = Math.max(1, Math.ceil(totalMs / DIA_MS - 0.01));
|
|
44076
|
+
const out = /* @__PURE__ */ new Map();
|
|
44077
|
+
for (const [s, ms] of tramos) out.set(s, totalMs > 0 ? Math.round(ms / totalMs * total * 10) / 10 : total / tramos.size);
|
|
44078
|
+
return { tramos: out, total };
|
|
44079
|
+
}
|
|
44080
|
+
function evaluarCostoInternacion(input2) {
|
|
44081
|
+
const { lectura: l, hoy } = input2;
|
|
44082
|
+
const umbral = input2.umbral ?? UMBRAL_MARGEN_POR_DEFECTO;
|
|
44083
|
+
const faltas = [];
|
|
44084
|
+
const avisos = [];
|
|
44085
|
+
const facturado = redondear5(l.facturado.reduce((a, f) => a + f.monto * f.cantidad, 0));
|
|
44086
|
+
if (facturado <= 0) faltas.push("sin_facturar");
|
|
44087
|
+
if (!l.egreso) avisos.push("en_curso");
|
|
44088
|
+
let honorarios = 0;
|
|
44089
|
+
let sinRegla = 0;
|
|
44090
|
+
for (const f of l.facturado) {
|
|
44091
|
+
if (!f.profesional_id) continue;
|
|
44092
|
+
if (!f.regla || f.regla.prof === null && f.regla.inst === null) sinRegla += 1;
|
|
44093
|
+
honorarios += honorarioDe({ regla: f.regla, monto: f.monto, cantidad: f.cantidad });
|
|
44094
|
+
}
|
|
44095
|
+
honorarios = redondear5(honorarios);
|
|
44096
|
+
if (sinRegla > 0) avisos.push("prestaciones_sin_regla");
|
|
44097
|
+
let insumos = 0;
|
|
44098
|
+
const sinCosto = [];
|
|
44099
|
+
for (const c of l.consumos) {
|
|
44100
|
+
const cantidad2 = Math.max(c.dispensado, c.registrado);
|
|
44101
|
+
if (cantidad2 <= 0) continue;
|
|
44102
|
+
if (c.costo_unitario === null) {
|
|
44103
|
+
sinCosto.push(c.articulo || c.articulo_id);
|
|
44104
|
+
continue;
|
|
44105
|
+
}
|
|
44106
|
+
insumos += cantidad2 * c.costo_unitario;
|
|
44107
|
+
}
|
|
44108
|
+
insumos = redondear5(insumos);
|
|
44109
|
+
if (sinCosto.length) faltas.push("insumo_sin_costo");
|
|
44110
|
+
const { tramos, total } = diasPorServicio(l, hoy);
|
|
44111
|
+
const porServicio = input2.costoDiaPorServicio ?? {};
|
|
44112
|
+
const general = input2.costoDiaCama ?? null;
|
|
44113
|
+
let cama = 0;
|
|
44114
|
+
let faltaCama = false;
|
|
44115
|
+
const dias_por_servicio = [...tramos.entries()].map(([servicio, dias]) => {
|
|
44116
|
+
const costo_dia = porServicio[servicio] ?? general;
|
|
44117
|
+
if (costo_dia === null || costo_dia === void 0) faltaCama = true;
|
|
44118
|
+
else cama += dias * costo_dia;
|
|
44119
|
+
return { servicio, dias, costo_dia: costo_dia ?? null };
|
|
44120
|
+
});
|
|
44121
|
+
cama = redondear5(cama);
|
|
44122
|
+
if (faltaCama) faltas.push("sin_costo_cama");
|
|
44123
|
+
const minutos = l.cirugias.reduce((a, c) => a + (c.minutos ?? 0), 0);
|
|
44124
|
+
if (l.cirugias.some((c) => c.minutos === null)) avisos.push("cirugia_sin_duracion");
|
|
44125
|
+
let quirofano = 0;
|
|
44126
|
+
if (minutos > 0) {
|
|
44127
|
+
if (input2.costoHoraQuirofano === null || input2.costoHoraQuirofano === void 0) faltas.push("sin_costo_quirofano");
|
|
44128
|
+
else quirofano = redondear5(minutos / 60 * input2.costoHoraQuirofano);
|
|
44129
|
+
}
|
|
44130
|
+
const costo = redondear5(honorarios + insumos + cama + quirofano);
|
|
44131
|
+
const calculable = faltas.length === 0;
|
|
44132
|
+
const margen = facturado > 0 ? redondear5(facturado - costo) : null;
|
|
44133
|
+
const margen_pct = margen !== null && facturado > 0 ? Math.round(margen / facturado * 1e3) / 10 : null;
|
|
44134
|
+
let clase2;
|
|
44135
|
+
if (!calculable || margen_pct === null) clase2 = "datos_incompletos";
|
|
44136
|
+
else if (margen < 0) clase2 = "perdida";
|
|
44137
|
+
else if (margen_pct < umbral) clase2 = "margen_bajo";
|
|
44138
|
+
else clase2 = "rentable";
|
|
44139
|
+
return { clase: clase2, facturado, honorarios, insumos, cama, quirofano, costo, margen, margen_pct, umbral, dias: total, dias_por_servicio, minutos_quirofano: minutos, faltas, avisos, insumos_sin_costo: sinCosto, prestaciones_sin_regla: sinRegla };
|
|
44140
|
+
}
|
|
44141
|
+
var NOMBRE_FALTA2 = {
|
|
44142
|
+
sin_facturar: "no tiene nada facturado",
|
|
44143
|
+
insumo_sin_costo: "hay insumos sin costo de compra en el HIS",
|
|
44144
|
+
sin_costo_cama: "falta el costo del d\xEDa cama",
|
|
44145
|
+
sin_costo_quirofano: "falta el costo de la hora de quir\xF3fano"
|
|
44146
|
+
};
|
|
44147
|
+
var porcentaje2 = (n) => `${n.toLocaleString("es-AR", { maximumFractionDigits: 1 })} %`;
|
|
44148
|
+
function oracionCostoInternacion(l, e) {
|
|
44149
|
+
const quien = `La internaci\xF3n ${l.internacion_id} con ${l.financiador} (${e.dias} ${e.dias === 1 ? "d\xEDa" : "d\xEDas"})`;
|
|
44150
|
+
if (e.clase === "datos_incompletos") return `${quien}: no se puede calcular el resultado porque ${e.faltas.map((f) => NOMBRE_FALTA2[f]).join(" y ")}.`;
|
|
44151
|
+
if (e.clase === "perdida") return `${quien} dio p\xE9rdida: se facturaron ${pesos2(e.facturado)} y cost\xF3 ${pesos2(e.costo)} (${pesos2(Math.abs(e.margen))} de p\xE9rdida).`;
|
|
44152
|
+
if (e.clase === "margen_bajo") return `${quien} dej\xF3 un margen de ${porcentaje2(e.margen_pct)}, debajo del ${porcentaje2(e.umbral)} de la instituci\xF3n: se facturaron ${pesos2(e.facturado)} y cost\xF3 ${pesos2(e.costo)}.`;
|
|
44153
|
+
return `${quien} dej\xF3 un margen de ${porcentaje2(e.margen_pct)}.`;
|
|
44154
|
+
}
|
|
44155
|
+
|
|
43924
44156
|
// src/ops.ts
|
|
43925
44157
|
var OpsError = class extends Error {
|
|
43926
44158
|
constructor(codigo, motivo, detalle) {
|
|
@@ -44062,6 +44294,11 @@ function describirOpciones(antes, despues) {
|
|
|
44062
44294
|
if (antes.hora_corte !== despues.hora_corte && despues.hora_corte !== void 0) partes.push(`hora de corte de la evoluci\xF3n: ${String(despues.hora_corte)}`);
|
|
44063
44295
|
if (antes.dias_sin_actividad !== despues.dias_sin_actividad && despues.dias_sin_actividad !== void 0) partes.push(`d\xEDas sin actividad para sospechar un egreso sin registrar: ${String(despues.dias_sin_actividad)}`);
|
|
44064
44296
|
if (antes.tope_variacion !== despues.tope_variacion && despues.tope_variacion !== void 0) partes.push(`tope de variaci\xF3n de las circulares: ${String(despues.tope_variacion)} %`);
|
|
44297
|
+
if (antes.umbral_margen !== despues.umbral_margen && despues.umbral_margen !== void 0) partes.push(`margen m\xEDnimo: ${String(despues.umbral_margen)} %`);
|
|
44298
|
+
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)}`);
|
|
44299
|
+
if (antes.costo_dia_cama !== despues.costo_dia_cama && despues.costo_dia_cama !== void 0) partes.push(`costo del d\xEDa cama: ${despues.costo_dia_cama === null ? "sin fijar" : `$ ${String(despues.costo_dia_cama)}`}`);
|
|
44300
|
+
if (antes.costo_hora_quirofano !== despues.costo_hora_quirofano && despues.costo_hora_quirofano !== void 0) partes.push(`costo de la hora de quir\xF3fano: ${despues.costo_hora_quirofano === null ? "sin fijar" : `$ ${String(despues.costo_hora_quirofano)}`}`);
|
|
44301
|
+
if (JSON.stringify(antes.costo_dia_servicio ?? {}) !== JSON.stringify(despues.costo_dia_servicio ?? {}) && despues.costo_dia_servicio !== void 0) partes.push(`costo del d\xEDa cama por servicio: ${Object.entries(despues.costo_dia_servicio).map(([s, m]) => `${s} $ ${m}`).join(", ") || "ninguno"}`);
|
|
44065
44302
|
if (Boolean(antes.ofrecer_primero) !== Boolean(despues.ofrecer_primero)) partes.push(`consultar al paciente antes de asignar: ${despues.ofrecer_primero ? "s\xED" : "no"}`);
|
|
44066
44303
|
const plazosAntes = JSON.stringify(leerPlazos(antes.plazos));
|
|
44067
44304
|
const plazosDespues = leerPlazos(despues.plazos);
|
|
@@ -44074,6 +44311,22 @@ function describirOpciones(antes, despues) {
|
|
|
44074
44311
|
function conInsumosDeOpera(l, insumos) {
|
|
44075
44312
|
l.insumos = insumos.get(normalizarCodigo(l.codigo)) ?? [];
|
|
44076
44313
|
}
|
|
44314
|
+
function montoNoNegativo(v) {
|
|
44315
|
+
const n = typeof v === "number" ? v : typeof v === "string" && v.trim() ? Number(v) : NaN;
|
|
44316
|
+
return Number.isFinite(n) && n >= 0 ? Math.round(n * 100) / 100 : null;
|
|
44317
|
+
}
|
|
44318
|
+
function leerCostosPorServicio(v) {
|
|
44319
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return {};
|
|
44320
|
+
const out = {};
|
|
44321
|
+
for (const [k, x] of Object.entries(v)) {
|
|
44322
|
+
const m = montoNoNegativo(x);
|
|
44323
|
+
if (k.trim() && k.length <= 80 && m !== null) out[k.trim()] = m;
|
|
44324
|
+
}
|
|
44325
|
+
return out;
|
|
44326
|
+
}
|
|
44327
|
+
function opcionesCostos(op) {
|
|
44328
|
+
return { umbral: enteroEntre(op.umbral_margen, 0, 90) ?? UMBRAL_MARGEN_POR_DEFECTO, costoDiaCama: montoNoNegativo(op.costo_dia_cama), costoDiaPorServicio: leerCostosPorServicio(op.costo_dia_servicio), costoHoraQuirofano: montoNoNegativo(op.costo_hora_quirofano) };
|
|
44329
|
+
}
|
|
44077
44330
|
function opcionesRentabilidad(op) {
|
|
44078
44331
|
return { umbral: enteroEntre(op.umbral_margen, 0, 90) ?? UMBRAL_MARGEN_POR_DEFECTO, mesesPrecio: enteroEntre(op.meses_precio, 1, 36) ?? MESES_PRECIO_POR_DEFECTO };
|
|
44079
44332
|
}
|
|
@@ -44143,6 +44396,23 @@ async function cambiarPolitica(ctx, input2) {
|
|
|
44143
44396
|
if (tope === null) throw new OpsError("validacion", `tope_variacion: tiene que ser un n\xFAmero entre ${LIMITES_TOPE[0]} y ${LIMITES_TOPE[1]}`);
|
|
44144
44397
|
opciones.tope_variacion = tope;
|
|
44145
44398
|
}
|
|
44399
|
+
for (const clave of ["umbral_margen", "meses_precio"]) {
|
|
44400
|
+
if (opciones[clave] === void 0) continue;
|
|
44401
|
+
const [min, max] = clave === "umbral_margen" ? [0, 90] : [1, 36];
|
|
44402
|
+
const v = enteroEntre(opciones[clave], min, max);
|
|
44403
|
+
if (v === null) throw new OpsError("validacion", `${clave}: tiene que ser un n\xFAmero entero entre ${min} y ${max}`);
|
|
44404
|
+
opciones[clave] = v;
|
|
44405
|
+
}
|
|
44406
|
+
for (const clave of ["costo_dia_cama", "costo_hora_quirofano"]) {
|
|
44407
|
+
if (opciones[clave] === void 0 || opciones[clave] === null || opciones[clave] === "") {
|
|
44408
|
+
if (opciones[clave] === "") opciones[clave] = null;
|
|
44409
|
+
continue;
|
|
44410
|
+
}
|
|
44411
|
+
const v = montoNoNegativo(opciones[clave]);
|
|
44412
|
+
if (v === null) throw new OpsError("validacion", `${clave}: tiene que ser un monto mayor o igual a 0`);
|
|
44413
|
+
opciones[clave] = v;
|
|
44414
|
+
}
|
|
44415
|
+
if (opciones.costo_dia_servicio !== void 0) opciones.costo_dia_servicio = leerCostosPorServicio(opciones.costo_dia_servicio);
|
|
44146
44416
|
const cambioOpciones = describirOpciones(opcionesAntes, opciones);
|
|
44147
44417
|
const motivo = [input2.motivo ?? null, cambioOpciones].filter(Boolean).join(" \xB7 ") || null;
|
|
44148
44418
|
const existe = await ctx.db.query(
|
|
@@ -44280,6 +44550,11 @@ async function abrirCaso(ctx, input2) {
|
|
|
44280
44550
|
if (!pid || !/^\d{1,12}$/.test(pid) || !fid || !/^\d{1,12}$/.test(fid)) throw new OpsError("validacion", "rentabilidad_practica necesita prestacion_id y financiador_id (los ids en el HIS)");
|
|
44281
44551
|
if (referencia !== `rentabilidad:${pid}:${fid}`) throw new OpsError("referencia_requerida", `en rentabilidad_practica la referencia es obligatoria: rentabilidad:${pid}:${fid}`);
|
|
44282
44552
|
}
|
|
44553
|
+
if (wf.clave === "rentabilidad_internacion") {
|
|
44554
|
+
const id4 = str2(input2.datos?.internacion_id);
|
|
44555
|
+
if (!id4 || !/^\d{1,12}$/.test(id4)) throw new OpsError("validacion", "rentabilidad_internacion necesita internacion_id (el id de la internaci\xF3n en el HIS)");
|
|
44556
|
+
if (referencia !== `rentabilidad_internacion:${id4}`) throw new OpsError("referencia_requerida", `en rentabilidad_internacion la referencia es obligatoria: rentabilidad_internacion:${id4}`);
|
|
44557
|
+
}
|
|
44283
44558
|
if (wf.clave === "circular_aranceles") {
|
|
44284
44559
|
const d = input2.datos ?? {};
|
|
44285
44560
|
if (!referencia || !/^circular:\S+$/.test(referencia)) throw new OpsError("referencia_requerida", "en circular_aranceles la referencia es obligatoria: circular:<obra social>:<hash corto del archivo>");
|
|
@@ -44402,6 +44677,14 @@ async function contextoDe(ctx, companyId, caso, via, datos) {
|
|
|
44402
44677
|
}
|
|
44403
44678
|
return contexto;
|
|
44404
44679
|
}
|
|
44680
|
+
if (caso.workflow === "rentabilidad_internacion") {
|
|
44681
|
+
if (via === "evaluar" || via === "comprobar") {
|
|
44682
|
+
const op = await obtenerOpciones(ctx, companyId, caso.workflow);
|
|
44683
|
+
const l = leerLecturaCostoInternacion(datos.lectura);
|
|
44684
|
+
contexto.costoInternacion = "error" in l ? { error: l.error } : { internacionId: l.lectura.internacion_id, evaluacion: evaluarCostoInternacion({ lectura: l.lectura, hoy, ...opcionesCostos(op) }) };
|
|
44685
|
+
}
|
|
44686
|
+
return contexto;
|
|
44687
|
+
}
|
|
44405
44688
|
if (caso.workflow === "cobranza_financiador") {
|
|
44406
44689
|
const op = await obtenerOpciones(ctx, companyId, caso.workflow);
|
|
44407
44690
|
contexto.plazosPago = leerPlazos(op.plazos_pago);
|
|
@@ -44640,6 +44923,25 @@ async function transicionar(ctx, input2) {
|
|
|
44640
44923
|
}
|
|
44641
44924
|
delete datos.lectura;
|
|
44642
44925
|
}
|
|
44926
|
+
if (caso.workflow === "rentabilidad_internacion") {
|
|
44927
|
+
for (const k of ["evaluacion", "oracion", "financiador", "ingreso", "egreso", "facturado", "consumos", "cirugias", "comprobado_el"]) delete datos[k];
|
|
44928
|
+
const r = contexto.costoInternacion;
|
|
44929
|
+
const l = leerLecturaCostoInternacion(datos.lectura);
|
|
44930
|
+
if (r && !("error" in r) && !("error" in l)) {
|
|
44931
|
+
Object.assign(datos, {
|
|
44932
|
+
financiador: l.lectura.financiador,
|
|
44933
|
+
ingreso: l.lectura.ingreso,
|
|
44934
|
+
egreso: l.lectura.egreso,
|
|
44935
|
+
facturado: l.lectura.facturado,
|
|
44936
|
+
consumos: l.lectura.consumos,
|
|
44937
|
+
cirugias: l.lectura.cirugias,
|
|
44938
|
+
evaluacion: r.evaluacion,
|
|
44939
|
+
oracion: oracionCostoInternacion(l.lectura, r.evaluacion),
|
|
44940
|
+
comprobado_el: contexto.hoy ?? null
|
|
44941
|
+
});
|
|
44942
|
+
}
|
|
44943
|
+
delete datos.lectura;
|
|
44944
|
+
}
|
|
44643
44945
|
if (caso.workflow === "cobranza_financiador") {
|
|
44644
44946
|
for (const k of ["items", "resumen", "reclamo", "reclamos", "ultimo_reclamo", "proximo_reclamo", "nc_al_reclamar", "cobrado_el"]) delete datos[k];
|
|
44645
44947
|
const hoy = contexto.hoy ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
@@ -45403,7 +45705,7 @@ import { randomUUID as randomUUID4 } from "node:crypto";
|
|
|
45403
45705
|
function t4(ctx, table) {
|
|
45404
45706
|
return `${ctx.db.namespace}.${table}`;
|
|
45405
45707
|
}
|
|
45406
|
-
function
|
|
45708
|
+
function texto8(v) {
|
|
45407
45709
|
return typeof v === "string" && v.trim().length > 0 ? v.trim() : typeof v === "number" ? String(v) : null;
|
|
45408
45710
|
}
|
|
45409
45711
|
function iso3(v) {
|
|
@@ -45462,7 +45764,7 @@ function pendientesDe(caso, hoy) {
|
|
|
45462
45764
|
const estados = ESPERAN_PERSONA[caso.workflow];
|
|
45463
45765
|
if (!estados || !estados.includes(caso.estado)) return [];
|
|
45464
45766
|
const d = caso.datos;
|
|
45465
|
-
const bono = `Bono ${
|
|
45767
|
+
const bono = `Bono ${texto8(d.bono_numero) ?? texto8(d.bono_id) ?? "sin n\xFAmero"}${texto8(d.obra_social) ? ` \xB7 ${texto8(d.obra_social)}` : ""}`;
|
|
45466
45768
|
const base = { casoId: caso.id, workflow: caso.workflow, titulo: caso.titulo };
|
|
45467
45769
|
if (caso.workflow === "cuenta_completa") {
|
|
45468
45770
|
const out = [];
|
|
@@ -45470,7 +45772,7 @@ function pendientesDe(caso, hoy) {
|
|
|
45470
45772
|
const nombreRegla = REGLAS_CUENTA[f.regla]?.nombre ?? f.regla;
|
|
45471
45773
|
if (f.regla === "epicrisis_faltante") {
|
|
45472
45774
|
const p = f.responsable?.tipo === "profesional" && f.responsable.id ? f.responsable : null;
|
|
45473
|
-
const internacion =
|
|
45775
|
+
const internacion = texto8(d.internacion_id) ? `Internaci\xF3n ${texto8(d.internacion_id)}` : bono;
|
|
45474
45776
|
out.push({
|
|
45475
45777
|
...base,
|
|
45476
45778
|
que: `${internacion}: falta la epicrisis`,
|
|
@@ -45484,7 +45786,7 @@ function pendientesDe(caso, hoy) {
|
|
|
45484
45786
|
}
|
|
45485
45787
|
return out;
|
|
45486
45788
|
}
|
|
45487
|
-
const profesionalH =
|
|
45789
|
+
const profesionalH = texto8(d.profesional) ?? (d.profesional && typeof d.profesional === "object" ? texto8(d.profesional.nombre) : null);
|
|
45488
45790
|
if (caso.workflow === "honorario_en_cero") {
|
|
45489
45791
|
const r = d.resumen && typeof d.resumen === "object" ? d.resumen : {};
|
|
45490
45792
|
const n = typeof r.prestaciones === "number" ? r.prestaciones : 0;
|
|
@@ -45493,22 +45795,22 @@ function pendientesDe(caso, hoy) {
|
|
|
45493
45795
|
if (caso.workflow === "debito_profesional") {
|
|
45494
45796
|
const r = d.resumen && typeof d.resumen === "object" ? d.resumen : {};
|
|
45495
45797
|
const total = typeof r.total === "number" ? r.total.toLocaleString("es-AR", { minimumFractionDigits: 2, maximumFractionDigits: 2 }) : "";
|
|
45496
|
-
return [{ ...base, que: `${profesionalH ?? "Un profesional"}: cargar un d\xE9bito de $ ${total} aprobado por la direcci\xF3n (nota de cr\xE9dito de ${
|
|
45798
|
+
return [{ ...base, que: `${profesionalH ?? "Un profesional"}: cargar un d\xE9bito de $ ${total} aprobado por la direcci\xF3n (nota de cr\xE9dito de ${texto8(d.obra_social) ?? "la obra social"})`, responsable: rol("honorarios") }];
|
|
45497
45799
|
}
|
|
45498
45800
|
if (caso.workflow === "internacion_al_dia") {
|
|
45499
|
-
const internacion = `Internaci\xF3n ${
|
|
45801
|
+
const internacion = `Internaci\xF3n ${texto8(d.internacion_id) ?? "sin n\xFAmero"}${texto8(d.servicio) ? ` (${texto8(d.servicio)})` : ""}`;
|
|
45500
45802
|
const resp = d.responsable && typeof d.responsable === "object" ? d.responsable : null;
|
|
45501
|
-
const medicoId = resp ?
|
|
45803
|
+
const medicoId = resp ? texto8(resp.id) : null;
|
|
45502
45804
|
const out = [];
|
|
45503
45805
|
for (const p of Array.isArray(d.pendientes) ? d.pendientes : []) {
|
|
45504
|
-
const regla =
|
|
45806
|
+
const regla = texto8(p.regla);
|
|
45505
45807
|
if (regla === "evolucion_de_hoy") {
|
|
45506
|
-
const dia5 =
|
|
45808
|
+
const dia5 = texto8(p.dia);
|
|
45507
45809
|
if (!dia5 || hoy && dia5 !== hoy) continue;
|
|
45508
45810
|
out.push({
|
|
45509
45811
|
...base,
|
|
45510
45812
|
que: `${internacion}: todav\xEDa no tiene evoluci\xF3n hoy`,
|
|
45511
|
-
responsable: medicoId ? { clave: `profesional:${medicoId}`, nombre:
|
|
45813
|
+
responsable: medicoId ? { clave: `profesional:${medicoId}`, nombre: texto8(resp?.nombre) ?? `Profesional ${medicoId}`, reserva: "rol:internacion" } : rol("internacion"),
|
|
45512
45814
|
marca: `evolucion:${dia5}`
|
|
45513
45815
|
});
|
|
45514
45816
|
} else if (regla === "alta_sin_registrar") {
|
|
@@ -45520,13 +45822,13 @@ function pendientesDe(caso, hoy) {
|
|
|
45520
45822
|
return out;
|
|
45521
45823
|
}
|
|
45522
45824
|
if (caso.workflow === "facturacion_pendiente") {
|
|
45523
|
-
const vence =
|
|
45825
|
+
const vence = texto8(d.vence_el);
|
|
45524
45826
|
return [{ ...base, que: `${bono}: sin presentar${vence ? `, vence el ${vence.slice(8, 10)}/${vence.slice(5, 7)}` : ""}`, responsable: rol("facturacion") }];
|
|
45525
45827
|
}
|
|
45526
45828
|
if (caso.workflow === "credencial_profesional") {
|
|
45527
|
-
const id3 =
|
|
45528
|
-
const nombre =
|
|
45529
|
-
const obs =
|
|
45829
|
+
const id3 = texto8(d.profesional_id);
|
|
45830
|
+
const nombre = texto8(d.profesional) ?? (id3 ? `Profesional ${id3}` : "Profesional");
|
|
45831
|
+
const obs = texto8(d.observacion);
|
|
45530
45832
|
const que = `${nombre}: ${(obs && OBSERVACIONES_MATRICULA[obs]) ?? "la matr\xEDcula tiene una observaci\xF3n del registro nacional"}`;
|
|
45531
45833
|
return [{ ...base, que, responsable: id3 ? { clave: `profesional:${id3}`, nombre, reserva: "rol:personal" } : rol("personal") }];
|
|
45532
45834
|
}
|
|
@@ -45536,7 +45838,7 @@ var CLAVE_RE = /^(profesional:[A-Za-z0-9_-]{1,40}|financiador:\d{1,12}|rol:(admi
|
|
|
45536
45838
|
var MAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/;
|
|
45537
45839
|
var COLS_CONTACTO = "id, clave, nombre, mail, whatsapp, activo, updated_at";
|
|
45538
45840
|
function mapContacto(r) {
|
|
45539
|
-
return { id: String(r.id), clave: String(r.clave), nombre: String(r.nombre), mail:
|
|
45841
|
+
return { id: String(r.id), clave: String(r.clave), nombre: String(r.nombre), mail: texto8(r.mail), whatsapp: texto8(r.whatsapp), activo: r.activo !== false, actualizadoEn: iso3(r.updated_at) };
|
|
45540
45842
|
}
|
|
45541
45843
|
async function listarContactos(ctx, companyId) {
|
|
45542
45844
|
const rows = await ctx.db.query(`SELECT ${COLS_CONTACTO} FROM ${t4(ctx, "contacto")} WHERE company_id = $1 ORDER BY nombre ASC`, [companyId]);
|
|
@@ -45550,15 +45852,15 @@ function leerWhatsapp(v) {
|
|
|
45550
45852
|
}
|
|
45551
45853
|
async function guardarContacto(ctx, input2) {
|
|
45552
45854
|
if (input2.actor.tipo !== "humano") throw new OpsError("actor_no_permitido", "los contactos los carga una persona de la instituci\xF3n desde el portal");
|
|
45553
|
-
const clave =
|
|
45855
|
+
const clave = texto8(input2.clave) ?? "";
|
|
45554
45856
|
if (!CLAVE_RE.test(clave)) throw new OpsError("validacion", "clave: tiene que ser profesional:<id>, financiador:<id> o rol:<admision|aranceles|facturacion|internacion|personal>");
|
|
45555
|
-
const mail =
|
|
45857
|
+
const mail = texto8(input2.mail)?.toLowerCase() ?? null;
|
|
45556
45858
|
if (mail && !MAIL_RE.test(mail)) throw new OpsError("validacion", "mail: no tiene formato de correo");
|
|
45557
45859
|
const whatsapp = leerWhatsapp(input2.whatsapp);
|
|
45558
45860
|
const activo = input2.activo !== false;
|
|
45559
45861
|
if (activo && !mail && !whatsapp) throw new OpsError("validacion", "hace falta un mail o un WhatsApp para poder avisar");
|
|
45560
45862
|
const existente = (await ctx.db.query(`SELECT ${COLS_CONTACTO} FROM ${t4(ctx, "contacto")} WHERE company_id = $1 AND clave = $2 LIMIT 1`, [input2.companyId, clave]))[0];
|
|
45561
|
-
const nombre =
|
|
45863
|
+
const nombre = texto8(input2.nombre)?.slice(0, 120) ?? (existente ? String(existente.nombre) : clave.startsWith("rol:") ? ROLES[clave.slice(4)] ?? clave : null);
|
|
45562
45864
|
if (!nombre) throw new OpsError("validacion", "nombre: es obligatorio para un profesional nuevo");
|
|
45563
45865
|
if (existente) {
|
|
45564
45866
|
await ctx.db.execute(
|
|
@@ -45588,8 +45890,8 @@ function mapAviso(r) {
|
|
|
45588
45890
|
texto: String(r.texto),
|
|
45589
45891
|
estado: ["pendiente", "enviado", "fallido", "cancelado"].includes(String(r.estado)) ? String(r.estado) : "pendiente",
|
|
45590
45892
|
intentosEnvio: Number(r.intentos_envio) || 0,
|
|
45591
|
-
error:
|
|
45592
|
-
proveedorId:
|
|
45893
|
+
error: texto8(r.error),
|
|
45894
|
+
proveedorId: texto8(r.proveedor_id),
|
|
45593
45895
|
enviadoEn: iso3(r.enviado_en),
|
|
45594
45896
|
creadoEn: iso3(r.created_at)
|
|
45595
45897
|
};
|
|
@@ -45633,7 +45935,7 @@ function redactarAviso(nombre, institucion, pendientes) {
|
|
|
45633
45935
|
const repetidos = pendientes.filter((p) => p.intento > 1).length;
|
|
45634
45936
|
const asunto = `${n === 1 ? "1 pendiente" : `${n} pendientes`} para resolver en el HIS \xB7 ${institucion}`;
|
|
45635
45937
|
const lineas = pendientes.map((p) => `- ${p.que}${p.intento > 1 ? " (segundo aviso)" : ""}`);
|
|
45636
|
-
const
|
|
45938
|
+
const texto10 = [
|
|
45637
45939
|
`Hola ${nombre}:`,
|
|
45638
45940
|
"",
|
|
45639
45941
|
`Desde ${institucion}, Autopilot Opera te avisa que ${n === 1 ? "ten\xE9s 1 pendiente" : `ten\xE9s ${n} pendientes`} para resolver en el HIS:`,
|
|
@@ -45644,7 +45946,7 @@ function redactarAviso(nombre, institucion, pendientes) {
|
|
|
45644
45946
|
"",
|
|
45645
45947
|
"Autopilot Opera \xB7 avisos autom\xE1ticos de la administraci\xF3n"
|
|
45646
45948
|
].join("\n");
|
|
45647
|
-
return { asunto, texto:
|
|
45949
|
+
return { asunto, texto: texto10 };
|
|
45648
45950
|
}
|
|
45649
45951
|
async function generarAvisos(ctx, input2) {
|
|
45650
45952
|
const { hoy, hora } = ahoraEnHospital(input2.ahora);
|
|
@@ -45721,7 +46023,7 @@ async function informarResultadoAviso(ctx, input2) {
|
|
|
45721
46023
|
if (input2.resultado === "enviado") {
|
|
45722
46024
|
await ctx.db.execute(
|
|
45723
46025
|
`UPDATE ${t4(ctx, "aviso")} SET estado = $3, proveedor_id = $4, enviado_en = now(), intentos_envio = intentos_envio + 1, error = NULL, updated_at = now() WHERE company_id = $1 AND id = $2`,
|
|
45724
|
-
[input2.companyId, aviso.id, "enviado",
|
|
46026
|
+
[input2.companyId, aviso.id, "enviado", texto8(input2.proveedorId)]
|
|
45725
46027
|
);
|
|
45726
46028
|
for (const c of aviso.casos) {
|
|
45727
46029
|
await registrarAccion(ctx, {
|
|
@@ -45735,11 +46037,11 @@ async function informarResultadoAviso(ctx, input2) {
|
|
|
45735
46037
|
actor
|
|
45736
46038
|
});
|
|
45737
46039
|
}
|
|
45738
|
-
return { ...aviso, estado: "enviado", proveedorId:
|
|
46040
|
+
return { ...aviso, estado: "enviado", proveedorId: texto8(input2.proveedorId), enviadoEn: (/* @__PURE__ */ new Date()).toISOString(), intentosEnvio: aviso.intentosEnvio + 1, error: null };
|
|
45739
46041
|
}
|
|
45740
46042
|
const intentos = aviso.intentosEnvio + 1;
|
|
45741
46043
|
const estado = intentos >= MAX_INTENTOS_ENVIO ? "fallido" : "pendiente";
|
|
45742
|
-
const error62 =
|
|
46044
|
+
const error62 = texto8(input2.error)?.slice(0, 300) ?? "no se pudo enviar";
|
|
45743
46045
|
await ctx.db.execute(
|
|
45744
46046
|
`UPDATE ${t4(ctx, "aviso")} SET estado = $3, error = $4, intentos_envio = $5, updated_at = now() WHERE company_id = $1 AND id = $2`,
|
|
45745
46047
|
[input2.companyId, aviso.id, estado, error62, intentos]
|
|
@@ -45816,7 +46118,7 @@ function arr(v) {
|
|
|
45816
46118
|
}
|
|
45817
46119
|
return [];
|
|
45818
46120
|
}
|
|
45819
|
-
function
|
|
46121
|
+
function texto9(v, max, campo, problemas, obligatorio = false) {
|
|
45820
46122
|
if (v === void 0 || v === null || v === "") {
|
|
45821
46123
|
if (obligatorio) problemas.push(`${campo} es obligatorio`);
|
|
45822
46124
|
return void 0;
|
|
@@ -45927,24 +46229,24 @@ function validarWidgets(entrada) {
|
|
|
45927
46229
|
const a = ancho(o.ancho);
|
|
45928
46230
|
switch (tipo) {
|
|
45929
46231
|
case "indicador": {
|
|
45930
|
-
const etiqueta =
|
|
45931
|
-
const valor = typeof o.valor === "number" && Number.isFinite(o.valor) ? o.valor :
|
|
46232
|
+
const etiqueta = texto9(o.etiqueta, 80, `${ref}.etiqueta`, problemas, true) ?? "";
|
|
46233
|
+
const valor = typeof o.valor === "number" && Number.isFinite(o.valor) ? o.valor : texto9(o.valor, 40, `${ref}.valor`, problemas, true) ?? "";
|
|
45932
46234
|
const tono = typeof o.tono === "string" && TONOS.includes(o.tono) ? o.tono : void 0;
|
|
45933
46235
|
if (o.tono !== void 0 && !tono) problemas.push(`${ref}.tono debe ser uno de ${TONOS.join(", ")}`);
|
|
45934
|
-
const unidad =
|
|
46236
|
+
const unidad = texto9(o.unidad, 16, `${ref}.unidad`, problemas);
|
|
45935
46237
|
if (unidad === "%" && !ES_VARIACION.test(etiqueta)) controlarPorcentaje(valor, `${ref}.valor`, problemas);
|
|
45936
46238
|
const { calculo, origen } = leerCalculo(o.calculo, ref, problemas);
|
|
45937
|
-
salida.push({ tipo, etiqueta, valor, unidad, contexto:
|
|
46239
|
+
salida.push({ tipo, etiqueta, valor, unidad, contexto: texto9(o.contexto, 160, `${ref}.contexto`, problemas), tono, ancho: a, ...calculo ? { calculo } : {}, origen });
|
|
45938
46240
|
break;
|
|
45939
46241
|
}
|
|
45940
46242
|
case "barras": {
|
|
45941
|
-
const titulo =
|
|
46243
|
+
const titulo = texto9(o.titulo, 120, `${ref}.titulo`, problemas, true) ?? "";
|
|
45942
46244
|
const series = arr(o.series);
|
|
45943
46245
|
if (series.length === 0 || series.length > 40) problemas.push(`${ref}.series debe tener entre 1 y 40 barras`);
|
|
45944
|
-
const unidad =
|
|
46246
|
+
const unidad = texto9(o.unidad, 16, `${ref}.unidad`, problemas);
|
|
45945
46247
|
const barras = series.slice(0, 40).map((s, j) => {
|
|
45946
46248
|
const so = s && typeof s === "object" ? s : {};
|
|
45947
|
-
return { etiqueta:
|
|
46249
|
+
return { etiqueta: texto9(so.etiqueta, 60, `${ref}.series[${j}].etiqueta`, problemas, true) ?? "", valor: numero2(so.valor, `${ref}.series[${j}].valor`, problemas) };
|
|
45948
46250
|
});
|
|
45949
46251
|
if (unidad === "%" && !ES_VARIACION.test(titulo)) barras.forEach((b, j) => controlarPorcentaje(b.valor, `${ref}.series[${j}].valor`, problemas));
|
|
45950
46252
|
const { calculo, origen } = leerCalculo(o.calculo, ref, problemas);
|
|
@@ -45952,13 +46254,13 @@ function validarWidgets(entrada) {
|
|
|
45952
46254
|
break;
|
|
45953
46255
|
}
|
|
45954
46256
|
case "linea": {
|
|
45955
|
-
const titulo =
|
|
46257
|
+
const titulo = texto9(o.titulo, 120, `${ref}.titulo`, problemas, true) ?? "";
|
|
45956
46258
|
const puntos = arr(o.puntos);
|
|
45957
46259
|
if (puntos.length === 0 || puntos.length > 400) problemas.push(`${ref}.puntos debe tener entre 1 y 400 puntos`);
|
|
45958
|
-
const unidad =
|
|
46260
|
+
const unidad = texto9(o.unidad, 16, `${ref}.unidad`, problemas);
|
|
45959
46261
|
const serie2 = puntos.slice(0, 400).map((p, j) => {
|
|
45960
46262
|
const po = p && typeof p === "object" ? p : {};
|
|
45961
|
-
return { x:
|
|
46263
|
+
return { x: texto9(po.x, 40, `${ref}.puntos[${j}].x`, problemas, true) ?? "", y: numero2(po.y, `${ref}.puntos[${j}].y`, problemas) };
|
|
45962
46264
|
});
|
|
45963
46265
|
if (unidad === "%" && !ES_VARIACION.test(titulo)) serie2.forEach((p, j) => controlarPorcentaje(p.y, `${ref}.puntos[${j}].y`, problemas));
|
|
45964
46266
|
const { calculo, origen } = leerCalculo(o.calculo, ref, problemas);
|
|
@@ -45966,12 +46268,12 @@ function validarWidgets(entrada) {
|
|
|
45966
46268
|
break;
|
|
45967
46269
|
}
|
|
45968
46270
|
case "tabla": {
|
|
45969
|
-
const titulo =
|
|
46271
|
+
const titulo = texto9(o.titulo, 120, `${ref}.titulo`, problemas, true) ?? "";
|
|
45970
46272
|
const columnas = arr(o.columnas);
|
|
45971
46273
|
if (columnas.length === 0 || columnas.length > 12) problemas.push(`${ref}.columnas debe tener entre 1 y 12 columnas`);
|
|
45972
46274
|
const filas = arr(o.filas);
|
|
45973
46275
|
if (filas.length > 200) problemas.push(`${ref}.filas admite hasta 200 filas`);
|
|
45974
|
-
const cols = columnas.slice(0, 12).map((c, j) =>
|
|
46276
|
+
const cols = columnas.slice(0, 12).map((c, j) => texto9(c, 60, `${ref}.columnas[${j}]`, problemas, true) ?? "");
|
|
45975
46277
|
const celdasTabla = filas.slice(0, 200).map((f, j) => {
|
|
45976
46278
|
const celdas = arr(f);
|
|
45977
46279
|
if (celdas.length !== cols.length) problemas.push(`${ref}.filas[${j}] tiene ${celdas.length} celdas y la tabla ${cols.length} columnas`);
|
|
@@ -45987,7 +46289,7 @@ function validarWidgets(entrada) {
|
|
|
45987
46289
|
break;
|
|
45988
46290
|
}
|
|
45989
46291
|
case "texto": {
|
|
45990
|
-
salida.push({ tipo, titulo:
|
|
46292
|
+
salida.push({ tipo, titulo: texto9(o.titulo, 120, `${ref}.titulo`, problemas), texto: texto9(o.texto, 4e3, `${ref}.texto`, problemas, true) ?? "", ancho: a });
|
|
45991
46293
|
break;
|
|
45992
46294
|
}
|
|
45993
46295
|
}
|
|
@@ -46196,8 +46498,8 @@ Transiciones registradas: ${detalle.transiciones.length}; acciones registradas:
|
|
|
46196
46498
|
const p = obj(params);
|
|
46197
46499
|
const quedo = p.quedo === true ? true : p.quedo === false ? false : null;
|
|
46198
46500
|
const r = await verificarEscritura(ctx, { companyId: runCtx.companyId, accionId: str3(p.accionId) ?? "", quedo, leido: obj(p.leido), detalle: str3(p.detalle), actor: actorAgente(runCtx) });
|
|
46199
|
-
const
|
|
46200
|
-
return { content: `Escritura ${r.accion.id}: ${
|
|
46501
|
+
const texto10 = quedo === true ? "qued\xF3 escrita: se da por hecha" : quedo === false ? "no se escribi\xF3: se puede reintentar" : r.paraPersona ? "no se pudo saber: pasa a una persona" : "no se pudo saber: volv\xE9 a verificar m\xE1s tarde";
|
|
46502
|
+
return { content: `Escritura ${r.accion.id}: ${texto10}`, data: r };
|
|
46201
46503
|
}));
|
|
46202
46504
|
ctx.tools.register(TOOL_NAMES.listarReglas, toolSchema(TOOL_NAMES.listarReglas), async (params, runCtx) => conErrores(async () => {
|
|
46203
46505
|
const p = obj(params);
|
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.33",
|
|
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": [
|