// 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, } }