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 CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/manifest.ts
2
2
  var PLUGIN_ID = "alephoo.autopilot-ops";
3
- var PLUGIN_VERSION = "0.1.32";
3
+ var PLUGIN_VERSION = "0.1.33";
4
4
  var DB_NAMESPACE_SLUG = "autopilot_ops";
5
5
  var PAGE_ROUTE = "autopilot";
6
6
  var TOOL_NAMES = {
package/dist/ui/index.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
 
9
9
  // src/manifest.ts
10
10
  var PLUGIN_ID = "alephoo.autopilot-ops";
11
- var PLUGIN_VERSION = "0.1.32";
11
+ var PLUGIN_VERSION = "0.1.33";
12
12
  var DB_NAMESPACE_SLUG = "autopilot_ops";
13
13
  var PAGE_ROUTE = "autopilot";
14
14
  var TOOL_NAMES = {
package/dist/worker.js CHANGED
@@ -39932,7 +39932,7 @@ function startWorkerRpcHost(options) {
39932
39932
 
39933
39933
  // src/manifest.ts
39934
39934
  var PLUGIN_ID = "alephoo.autopilot-ops";
39935
- var PLUGIN_VERSION = "0.1.32";
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: "Rentabilidad por pr\xE1ctica",
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
- var WORKFLOWS_SOLO_LECTURA = /* @__PURE__ */ new Set([cuentaCompleta.clave, facturacionPendiente.clave, credencialProfesional.clave, internacionAlDia.clave, honorarioEnCero.clave, rentabilidadPractica.clave]);
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, texto9) {
43581
- return { version: version2, fecha: (/* @__PURE__ */ new Date()).toISOString(), actorTipo: actor.tipo, actorId: actor.id, cambio: texto9 };
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 texto7(v) {
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 ${texto7(d.bono_numero) ?? texto7(d.bono_id) ?? "sin n\xFAmero"}${texto7(d.obra_social) ? ` \xB7 ${texto7(d.obra_social)}` : ""}`;
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 = texto7(d.internacion_id) ? `Internaci\xF3n ${texto7(d.internacion_id)}` : bono;
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 = texto7(d.profesional) ?? (d.profesional && typeof d.profesional === "object" ? texto7(d.profesional.nombre) : null);
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 ${texto7(d.obra_social) ?? "la obra social"})`, responsable: rol("honorarios") }];
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 ${texto7(d.internacion_id) ?? "sin n\xFAmero"}${texto7(d.servicio) ? ` (${texto7(d.servicio)})` : ""}`;
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 ? texto7(resp.id) : null;
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 = texto7(p.regla);
45806
+ const regla = texto8(p.regla);
45505
45807
  if (regla === "evolucion_de_hoy") {
45506
- const dia5 = texto7(p.dia);
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: texto7(resp?.nombre) ?? `Profesional ${medicoId}`, reserva: "rol:internacion" } : rol("internacion"),
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 = texto7(d.vence_el);
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 = texto7(d.profesional_id);
45528
- const nombre = texto7(d.profesional) ?? (id3 ? `Profesional ${id3}` : "Profesional");
45529
- const obs = texto7(d.observacion);
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: texto7(r.mail), whatsapp: texto7(r.whatsapp), activo: r.activo !== false, actualizadoEn: iso3(r.updated_at) };
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 = texto7(input2.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 = texto7(input2.mail)?.toLowerCase() ?? null;
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 = texto7(input2.nombre)?.slice(0, 120) ?? (existente ? String(existente.nombre) : clave.startsWith("rol:") ? ROLES[clave.slice(4)] ?? clave : null);
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: texto7(r.error),
45592
- proveedorId: texto7(r.proveedor_id),
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 texto9 = [
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: texto9 };
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", texto7(input2.proveedorId)]
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: texto7(input2.proveedorId), enviadoEn: (/* @__PURE__ */ new Date()).toISOString(), intentosEnvio: aviso.intentosEnvio + 1, error: null };
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 = texto7(input2.error)?.slice(0, 300) ?? "no se pudo enviar";
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 texto8(v, max, campo, problemas, obligatorio = false) {
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 = texto8(o.etiqueta, 80, `${ref}.etiqueta`, problemas, true) ?? "";
45931
- const valor = typeof o.valor === "number" && Number.isFinite(o.valor) ? o.valor : texto8(o.valor, 40, `${ref}.valor`, problemas, true) ?? "";
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 = texto8(o.unidad, 16, `${ref}.unidad`, problemas);
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: texto8(o.contexto, 160, `${ref}.contexto`, problemas), tono, ancho: a, ...calculo ? { calculo } : {}, origen });
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 = texto8(o.titulo, 120, `${ref}.titulo`, problemas, true) ?? "";
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 = texto8(o.unidad, 16, `${ref}.unidad`, problemas);
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: texto8(so.etiqueta, 60, `${ref}.series[${j}].etiqueta`, problemas, true) ?? "", valor: numero2(so.valor, `${ref}.series[${j}].valor`, problemas) };
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 = texto8(o.titulo, 120, `${ref}.titulo`, problemas, true) ?? "";
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 = texto8(o.unidad, 16, `${ref}.unidad`, problemas);
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: texto8(po.x, 40, `${ref}.puntos[${j}].x`, problemas, true) ?? "", y: numero2(po.y, `${ref}.puntos[${j}].y`, problemas) };
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 = texto8(o.titulo, 120, `${ref}.titulo`, problemas, true) ?? "";
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) => texto8(c, 60, `${ref}.columnas[${j}]`, problemas, true) ?? "");
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: texto8(o.titulo, 120, `${ref}.titulo`, problemas), texto: texto8(o.texto, 4e3, `${ref}.texto`, problemas, true) ?? "", ancho: a });
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 texto9 = 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";
46200
- return { content: `Escritura ${r.accion.id}: ${texto9}`, data: r };
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.32",
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": [