Files
inq-roi-simulador-web/src/domain/roi.ts

80 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Cálculos de ROI de automatización — funciones puras, sin dependencias de UI ni persistencia.
// Fórmulas nominales simples (ADR-014): sin VPN, TIR ni tasa de descuento.
// Los edge cases (inversión cero, ahorro negativo, división por cero) están explicitados.
export interface RoiInput {
costPerExecutionActual: number // costo total de una ejecución en escenario actual
costPerExecutionAutomated: number // costo total de una ejecución en escenario automatizado
annualFrequency: number // ejecuciones por año
analysisHorizonYears: number // horizonte de análisis en años
automationInvestment: number // inversión total única del proyecto de automatización
}
export interface RoiResult {
savingsPerExecution: number // ahorro por ejecución individual (actual - automatizado)
savingsPerExecutionPercent: number // ahorro % sobre el costo actual (0 si actual = 0)
annualSavings: number // ahorro anual = ahorro/ejecución × frecuencia
paybackMonths: number // meses para recuperar inversión (Infinity si ahorro <= 0)
paybackYears: number // años para recuperar inversión
cumulativeSavingsHorizon: number // ahorro acumulado neto al final del horizonte
breaksEvenInHorizon: boolean // ¿el payback ocurre dentro del horizonte?
roiAnnualPercent: number // ROI anualizado % = (ahorroAnual / inversión) × 100
// Infinity si inversión = 0 y ahorro > 0
}
export function calculateRoi(input: RoiInput): RoiResult {
const {
costPerExecutionActual,
costPerExecutionAutomated,
annualFrequency,
analysisHorizonYears,
automationInvestment,
} = input
const savingsPerExecution = costPerExecutionActual - costPerExecutionAutomated
// Evitar división por cero cuando el costo actual es 0
const savingsPerExecutionPercent =
costPerExecutionActual > 0
? (savingsPerExecution / costPerExecutionActual) * 100
: 0
const annualSavings = savingsPerExecution * annualFrequency
// Payback: si no hay ahorro (o es negativo), nunca se recupera la inversión
let paybackMonths: number
if (annualSavings <= 0) {
paybackMonths = Infinity
} else if (automationInvestment === 0) {
// Inversión cero con ahorro positivo: recuperación instantánea
paybackMonths = 0
} else {
paybackMonths = (automationInvestment / annualSavings) * 12
}
const paybackYears = paybackMonths / 12 // Infinity / 12 = Infinity, 0 / 12 = 0
const cumulativeSavingsHorizon = annualSavings * analysisHorizonYears - automationInvestment
const breaksEvenInHorizon = isFinite(paybackYears) && paybackYears <= analysisHorizonYears
// ROI anualizado: Infinity si inversión = 0 con ahorro > 0; 0 si inversión = 0 y ahorro = 0
let roiAnnualPercent: number
if (automationInvestment === 0) {
roiAnnualPercent = annualSavings > 0 ? Infinity : 0
} else {
roiAnnualPercent = (annualSavings / automationInvestment) * 100
}
return {
savingsPerExecution,
savingsPerExecutionPercent,
annualSavings,
paybackMonths,
paybackYears,
cumulativeSavingsHorizon,
breaksEvenInHorizon,
roiAnnualPercent,
}
}