109 lines
No EOL
3.3 KiB
TypeScript
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
|
|
}
|
|
} |