app/composables/useRunway.ts

65 lines
1.9 KiB
TypeScript

import { monthlyPayroll } from '~/types/members'
/**
* Computes months of runway from cash, reserves, and burn rate
* Formula: (cash + savings) ÷ average monthly burn in scenario
*/
export const useRunway = () => {
const membersStore = useMembersStore()
const policiesStore = usePoliciesStore()
const budgetStore = useBudgetStore()
const calculateRunway = (cash: number, savings: number, monthlyBurn: number): number => {
if (monthlyBurn <= 0) return Infinity
return (cash + savings) / monthlyBurn
}
// Calculate monthly burn based on operating mode
const getMonthlyBurn = (mode?: 'minimum' | 'target') => {
const operatingMode = mode || policiesStore.operatingMode || 'minimum'
// Get payroll costs based on mode
const payrollCost = monthlyPayroll(membersStore.members, operatingMode)
// Add oncosts
const oncostPct = policiesStore.payrollOncostPct || 0
const totalPayroll = Math.round(payrollCost * (1 + oncostPct / 100))
// Add overhead costs
const overheadCost = budgetStore.overheadCosts.reduce((sum, cost) => sum + (cost.amount || 0), 0)
return totalPayroll + overheadCost
}
// Calculate runway for both modes
const getDualModeRunway = (cash: number, savings: number) => {
const minBurn = getMonthlyBurn('minimum')
const targetBurn = getMonthlyBurn('target')
return {
minimum: calculateRunway(cash, savings, minBurn),
target: calculateRunway(cash, savings, targetBurn),
minBurn,
targetBurn
}
}
const getRunwayStatus = (months: number): 'green' | 'yellow' | 'red' => {
if (months >= 3) return 'green'
if (months >= 2) return 'yellow'
return 'red'
}
const formatRunway = (months: number): string => {
if (months === Infinity) return '∞ months'
return `${months.toFixed(1)} months`
}
return {
calculateRunway,
getRunwayStatus,
formatRunway,
getMonthlyBurn,
getDualModeRunway
}
}