app/composables/useDeferredMetrics.ts

89 lines
2.8 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.

/**
* Calculates deferred balance and ratio vs monthly payroll
* Formula: total deferred wage liability ÷ one month of payroll
*/
export const useDeferredMetrics = () => {
const calculateDeferredBalance = (
members: Array<{ deferredHours: number }>,
hourlyWage: number
): number => {
const totalDeferredHours = members.reduce((sum, member) => sum + (member.deferredHours || 0), 0)
return totalDeferredHours * hourlyWage
}
const calculateMonthlyPayroll = (
members: Array<{ targetHours: number }>,
hourlyWage: number,
oncostPct: number
): number => {
const totalTargetHours = members.reduce((sum, member) => sum + (member.targetHours || 0), 0)
const grossPayroll = totalTargetHours * hourlyWage
return Math.round(grossPayroll * (1 + oncostPct / 100))
}
const calculateDeferredRatio = (
deferredBalance: number,
monthlyPayroll: number
): number => {
if (monthlyPayroll <= 0) return 0
return deferredBalance / monthlyPayroll
}
const getDeferredRatioStatus = (ratio: number): 'green' | 'yellow' | 'red' => {
if (ratio > 1.5) return 'red' // Flag red if >1.5× monthly payroll
if (ratio > 1.0) return 'yellow'
return 'green'
}
const getDeferredRatioMessage = (status: 'green' | 'yellow' | 'red'): string => {
switch (status) {
case 'red':
return 'Deferred balance is high. Consider repaying or reducing scope.'
case 'yellow':
return 'Deferred balance is building up. Monitor closely.'
case 'green':
return 'Deferred balance is manageable.'
}
}
const checkDeferredCap = (
memberHours: number,
capHoursPerQtr: number,
quarterProgress: number
): { withinCap: boolean; remainingHours: number } => {
const quarterlyLimit = capHoursPerQtr * quarterProgress
const withinCap = memberHours <= quarterlyLimit
const remainingHours = Math.max(0, quarterlyLimit - memberHours)
return { withinCap, remainingHours }
}
const analyzeDeferredMetrics = (
members: Array<{ deferredHours?: number; targetHours?: number }>,
hourlyWage: number,
oncostPct: number
) => {
const deferredBalance = calculateDeferredBalance(members, hourlyWage)
const monthlyPayroll = calculateMonthlyPayroll(members, hourlyWage, oncostPct)
const ratio = calculateDeferredRatio(deferredBalance, monthlyPayroll)
const status = getDeferredRatioStatus(ratio)
return {
deferredBalance,
monthlyPayroll,
ratio: Number(ratio.toFixed(2)),
status,
message: getDeferredRatioMessage(status)
}
}
return {
calculateDeferredBalance,
calculateMonthlyPayroll,
calculateDeferredRatio,
getDeferredRatioStatus,
getDeferredRatioMessage,
checkDeferredCap,
analyzeDeferredMetrics
}
}