app/composables/useScenarios.ts

109 lines
No EOL
3.3 KiB
TypeScript

import { monthlyPayroll } from '~/types/members'
export function useScenarios() {
const membersStore = useMembersStore()
const streamsStore = useStreamsStore()
const policiesStore = usePoliciesStore()
const budgetStore = useBudgetStore()
const cashStore = useCashStore()
// Base runway calculation
function calculateScenarioRunway(
members: any[],
streams: any[],
operatingMode: 'minimum' | 'target' = 'minimum'
) {
// Calculate payroll for scenario
const payrollCost = monthlyPayroll(members, operatingMode)
const oncostPct = policiesStore.payrollOncostPct || 0
const totalPayroll = payrollCost * (1 + oncostPct / 100)
// Calculate revenue
const totalRevenue = streams.reduce((sum, s) => sum + (s.targetMonthlyAmount || 0), 0)
// Add overhead
const overheadCost = budgetStore.overheadCosts.reduce((sum, cost) => sum + (cost.amount || 0), 0)
// Net monthly
const monthlyNet = totalRevenue - totalPayroll - overheadCost
// Cash + savings
const cash = cashStore.currentCash || 50000
const savings = cashStore.currentSavings || 15000
const totalLiquid = cash + savings
// Runway calculation
const monthlyBurn = totalPayroll + overheadCost
const runway = monthlyBurn > 0 ? totalLiquid / monthlyBurn : Infinity
return {
runway: Math.max(0, runway),
monthlyNet,
monthlyBurn,
totalRevenue,
totalPayroll
}
}
// Scenario transformations per CLAUDE.md
const scenarioTransforms = {
current: () => ({
members: [...membersStore.members],
streams: [...streamsStore.streams]
}),
quitJobs: () => ({
// Set external income to 0 for members who have day jobs
members: membersStore.members.map(m => ({
...m,
externalMonthlyIncome: 0 // Assume everyone quits their day job
})),
streams: [...streamsStore.streams]
}),
startProduction: () => ({
members: [...membersStore.members],
// Reduce service revenue, increase production costs
streams: streamsStore.streams.map(s => {
// Reduce service contracts by 30%
if (s.category?.toLowerCase().includes('service') || s.name.toLowerCase().includes('service')) {
return { ...s, targetMonthlyAmount: (s.targetMonthlyAmount || 0) * 0.7 }
}
return s
})
})
}
// Calculate all scenarios
const scenarios = computed(() => {
const currentMode = policiesStore.operatingMode || 'minimum'
const current = scenarioTransforms.current()
const quitJobs = scenarioTransforms.quitJobs()
const startProduction = scenarioTransforms.startProduction()
return {
current: {
name: 'Operate Current',
status: 'Active',
...calculateScenarioRunway(current.members, current.streams, currentMode)
},
quitJobs: {
name: 'Quit Day Jobs',
status: 'Scenario',
...calculateScenarioRunway(quitJobs.members, quitJobs.streams, currentMode)
},
startProduction: {
name: 'Start Production',
status: 'Scenario',
...calculateScenarioRunway(startProduction.members, startProduction.streams, currentMode)
}
}
})
return {
scenarios,
calculateScenarioRunway,
scenarioTransforms
}
}