14. Representación de operaciones financieras con objetos y arreglos

Una operación bien modelada conserva el significado de sus datos: quién observa, qué unidad temporal utiliza y qué movimiento ocurre en cada momento.

14.1 Del enunciado a los datos

Antes de programar una fórmula conviene transformar el enunciado en una estructura explícita. Si guardamos importes, períodos y conceptos en variables sueltas, resulta fácil perder la relación entre ellos.

Los objetos agrupan propiedades de una misma entidad; los arreglos mantienen colecciones ordenadas. Juntos permiten representar una operación completa.

14.2 Un objeto para la operación

Las propiedades generales pueden reunirse en un objeto:

const operacion = {
  id: "op-001",
  nombre: "Proyecto de ejemplo",
  perspectiva: "inversor",
  unidadTiempo: "meses",
  flujos: []
};

console.log("Operación:", operacion.nombre);
console.log("Perspectiva:", operacion.perspectiva);
console.log("Unidad:", operacion.unidadTiempo);
console.log("Cantidad de flujos:", operacion.flujos.length);

Los nombres indican qué representa cada valor. Esto es más claro que depender de la posición de datos sin etiqueta.

14.3 Un arreglo para los movimientos

Una operación puede contener una cantidad variable de flujos. Los almacenamos dentro de un arreglo:

const operacion = { nombre: "Proyecto de ejemplo", flujos: [] };

operacion.flujos = [
  { periodo: 0, importe: -100000, concepto: "aporte inicial" },
  { periodo: 1, importe: 30000, concepto: "cobro" },
  { periodo: 2, importe: 80000, concepto: "cobro final" }
];

console.log("Cantidad de flujos:", operacion.flujos.length);
operacion.flujos.forEach(flujo => {
  console.log(`t=${flujo.periodo}: ${flujo.concepto}, importe=${flujo.importe}`);
});

Cada elemento tiene la misma forma, por lo que puede validarse y procesarse de manera uniforme.

14.4 Elegir propiedades y tipos

Contrato de datos elemental para este curso.
PropiedadTipoRegla
idtextoIdentificador estable y no vacío
nombretextoDescripción legible
perspectivatextoPunto de vista declarado
unidadTiempotextoUna unidad admitida
flujosarregloContiene objetos de movimiento
periodonúmero enteroMayor o igual que cero
importenúmeroPositivo, negativo o cero
conceptotextoNo vacío

14.5 Identidad y posición son diferentes

El índice de un elemento puede cambiar al ordenar o eliminar movimientos. Por eso no conviene utilizarlo como identidad permanente.

índice ≠ identificador

El índice dice dónde está ahora; el identificador dice qué registro es.

Un identificador estable permite actualizar el movimiento correcto incluso después de reorganizar el arreglo.

14.6 Mantener la perspectiva y la unidad

La perspectiva y la unidad temporal pertenecen a la operación completa cuando todos los flujos comparten esos criterios. Guardarlas una sola vez evita repetir texto y reduce contradicciones.

Si algunos movimientos usan fechas y otros períodos, conviene normalizarlos antes de aplicar algoritmos. Mezclar unidades sin una regla explícita debilita el modelo.

14.7 Los signos se conservan en los importes

El objeto no necesita una propiedad separada que diga “ingreso” o “egreso” si esa clasificación puede derivarse del signo:

function tipoDeFlujo(importe) {
  if (importe > 0) return "ingreso";
  if (importe < 0) return "egreso";
  return "sin movimiento";
}

console.log("5000:", tipoDeFlujo(5000));
console.log("-5000:", tipoDeFlujo(-5000));
console.log("0:", tipoDeFlujo(0));

Evitar datos duplicados impide situaciones inconsistentes como { importe: -5000, tipo: "ingreso" }.

14.8 Validar antes de calcular

Una validación básica comprueba estructura, tipos y límites:

function flujoValido(flujo) {
  return Number.isInteger(flujo.periodo) &&
    flujo.periodo >= 0 &&
    Number.isFinite(flujo.importe) &&
    typeof flujo.concepto === "string" &&
    flujo.concepto.trim() !== "";
}

const flujoCorrecto = { periodo: 2, importe: 75000, concepto: "cobro" };
const flujoIncorrecto = { periodo: -1, importe: Infinity, concepto: "" };

console.log("Flujo correcto:", flujoValido(flujoCorrecto));
console.log("Flujo incorrecto:", flujoValido(flujoIncorrecto));

Number.isFinite rechaza valores como NaN e Infinity, que no deben ingresar a un cálculo monetario.

14.9 Transformar sin perder los datos originales

Métodos como map y filter producen nuevos arreglos. Esto permite preparar vistas sin modificar la fuente:

const operacion = {
  flujos: [
    { periodo: 0, importe: -100000, concepto: "aporte inicial" },
    { periodo: 1, importe: 30000, concepto: "cobro" },
    { periodo: 2, importe: 80000, concepto: "cobro final" }
  ]
};

const ingresos = operacion.flujos
  .filter(flujo => flujo.importe > 0);

const etiquetas = operacion.flujos
  .map(flujo => `t=${flujo.periodo}: ${flujo.concepto}`);

console.log("Cantidad de ingresos:", ingresos.length);
etiquetas.forEach(etiqueta => console.log(etiqueta));

Mantener separadas la información original y sus presentaciones facilita comprobar resultados.

14.10 Ordenar por período

sort modifica el arreglo sobre el que actúa. Para conservar el orden recibido, primero creamos una copia:

const operacion = {
  flujos: [
    { periodo: 5, importe: 150000, concepto: "cobro final" },
    { periodo: 0, importe: -200000, concepto: "compra" },
    { periodo: 2, importe: 75000, concepto: "cobro parcial" }
  ]
};

const ordenados = [...operacion.flujos]
  .sort((a, b) => a.periodo - b.periodo);

console.log("Orden original:", operacion.flujos.map(f => f.periodo).join(", "));
console.log("Copia ordenada:", ordenados.map(f => f.periodo).join(", "));

Dos movimientos pueden compartir período. Si el orden entre ellos importa, necesitamos además un criterio secundario estable.

14.11 Agrupar movimientos del mismo período

Puede haber varios cobros y pagos en un mismo momento. Antes de combinarlos debemos decidir si necesitamos conservar el detalle.

Detalle completo

Mantiene cada concepto como registro independiente.

Agrupación

Suma importes del mismo período para obtener un flujo neto.

Ambas vistas

Conserva el origen y genera una agrupación derivada.

La tercera opción suele ser la más segura: no destruye información y permite mostrar resúmenes.

14.12 Convertir a JSON

JSON es un formato textual útil para guardar o intercambiar datos compatibles. JSON.stringify convierte el objeto en texto:

const operacion = {
  id: "op-001",
  nombre: "Proyecto de ejemplo",
  unidadTiempo: "meses",
  flujos: [{ periodo: 0, importe: -100000, concepto: "aporte" }]
};

const texto = JSON.stringify(operacion, null, 2);
const copia = JSON.parse(texto);

console.log(texto);
console.log("Nombre recuperado:", copia.nombre);
console.log("Es una copia diferente:", copia !== operacion);

JSON no conserva funciones, comentarios ni objetos Date como fechas especiales. Una fecha se guarda normalmente como texto y debe validarse al recuperarla.

JSON válido no significa operación válida: el formato puede ser correcto aunque falten propiedades o existan importes incoherentes.

14.13 Constructor de operaciones

Editá los datos generales y los movimientos. La herramienta valida, ordena por período y genera el objeto JSON resultante. Podés agregar hasta ocho flujos.

Objeto financiero estructurado

Modelo de datos sin fórmulas de interés.

Movimientos

PeríodoImporte con signoConceptoAcción
Movimientos válidos3
Último períodot = 2 meses
Suma nominal$10.000,00

JSON generado

    La estructura es válida y está ordenada por período.

    14.14 Ejercicio propuesto

    Modelá una operación llamada “Plan de equipamiento”, vista desde la perspectiva de una empresa y medida en meses. Contiene estos movimientos: −$200.000 en t = 0, +$75.000 en t = 2 y +$150.000 en t = 5.

    1. Creá el objeto con sus propiedades generales.
    2. Agregá un identificador a cada flujo.
    3. Validá período, importe y concepto.
    4. Obtené una copia ordenada por período.
    5. Convertí el resultado a JSON.
    Ver solución y código
    function flujoValido(flujo) {
      return Number.isInteger(flujo.periodo) &&
        flujo.periodo >= 0 &&
        Number.isFinite(flujo.importe) &&
        typeof flujo.concepto === "string" &&
        flujo.concepto.trim() !== "";
    }
    
    const operacion = {
      id: "op-equipamiento",
      nombre: "Plan de equipamiento",
      perspectiva: "empresa",
      unidadTiempo: "meses",
      flujos: [
        { id: "f-1", periodo: 0, importe: -200000, concepto: "compra" },
        { id: "f-2", periodo: 2, importe: 75000, concepto: "cobro parcial" },
        { id: "f-3", periodo: 5, importe: 150000, concepto: "cobro final" }
      ]
    };
    
    const esValida = operacion.flujos.every(flujoValido);
    const json = JSON.stringify(operacion, null, 2);
    
    console.log("Operación válida:", esValida);
    console.log(json);

    La suma nominal es $25.000, pero no se interpreta como rentabilidad porque los movimientos están ubicados en fechas diferentes.

    14.15 Ideas para recordar

    • Un objeto agrupa propiedades de una operación.
    • Un arreglo representa una colección variable y ordenable de movimientos.
    • Los nombres de propiedades deben expresar su significado y unidad.
    • Los identificadores no deben depender de la posición del arreglo.
    • La validación ocurre antes del cálculo.
    • Ordenar una copia evita modificar accidentalmente los datos originales.
    • JSON conserva datos, pero no garantiza que sean correctos.

    En el próximo tema comenzaremos el módulo de modelos estudiando el interés como precio por usar dinero.