This commit is contained in:
Jennie Robinson Faber 2025-09-04 10:42:03 +01:00
parent fc2d9ed56b
commit 983aeca2dc
32 changed files with 1570 additions and 27266 deletions

View file

@ -314,6 +314,67 @@ export const useBudgetStore = defineStore(
}
}
// Refresh payroll in budget when policy or operating mode changes
function refreshPayrollInBudget() {
if (!isInitialized.value) return;
const coopStore = useCoopBuilderStore();
const payrollIndex = budgetWorksheet.value.expenses.findIndex(item => item.id === "expense-payroll");
if (payrollIndex === -1) return; // No existing payroll entry
const totalHours = coopStore.members.reduce((sum, m) => sum + (m.hoursPerMonth || 0), 0);
const hourlyWage = coopStore.equalHourlyWage || 0;
const oncostPct = coopStore.payrollOncostPct || 0;
const basePayrollBudget = totalHours * hourlyWage;
if (basePayrollBudget > 0 && coopStore.members.length > 0) {
// Use policy-driven allocation
const payPolicy = {
relationship: coopStore.policy.relationship,
roleBands: coopStore.policy.roleBands
};
const membersForAllocation = coopStore.members.map(m => ({
...m,
displayName: m.name,
monthlyPayPlanned: m.monthlyPayPlanned || 0,
minMonthlyNeeds: m.minMonthlyNeeds || 0,
targetMonthlyPay: m.targetMonthlyPay || 0,
role: m.role || '',
hoursPerMonth: m.hoursPerMonth || 0
}));
const allocatedMembers = allocatePayroll(membersForAllocation, payPolicy, basePayrollBudget);
// Sum with operating mode consideration
const totalAllocatedPayroll = allocatedMembers.reduce((sum, m) => {
const planned = m.monthlyPayPlanned || 0;
if (coopStore.operatingMode === 'min' && m.minMonthlyNeeds) {
return sum + Math.min(planned, m.minMonthlyNeeds);
}
return sum + planned;
}, 0);
const monthlyPayroll = totalAllocatedPayroll * (1 + oncostPct / 100);
// Update monthly values
const today = new Date();
for (let i = 0; i < 12; i++) {
const date = new Date(today.getFullYear(), today.getMonth() + i, 1);
const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}`;
budgetWorksheet.value.expenses[payrollIndex].monthlyValues[monthKey] = monthlyPayroll;
}
// Update annual values
budgetWorksheet.value.expenses[payrollIndex].values = {
year1: { best: monthlyPayroll * 12, worst: monthlyPayroll * 8, mostLikely: monthlyPayroll * 12 },
year2: { best: monthlyPayroll * 14, worst: monthlyPayroll * 10, mostLikely: monthlyPayroll * 13 },
year3: { best: monthlyPayroll * 16, worst: monthlyPayroll * 12, mostLikely: monthlyPayroll * 15 }
};
}
}
// Initialize worksheet from wizard data
async function initializeFromWizardData() {
if (isInitialized.value && budgetWorksheet.value.revenue.length > 0) {
@ -404,17 +465,48 @@ export const useBudgetStore = defineStore(
});
});
// Add payroll from wizard data using the allocatePayroll function
// Add payroll from wizard data using the policy-driven allocation system
const totalHours = coopStore.members.reduce((sum, m) => sum + (m.hoursPerMonth || 0), 0);
const hourlyWage = coopStore.equalHourlyWage || 0;
const oncostPct = coopStore.payrollOncostPct || 0;
// Calculate total payroll budget (before oncosts)
// Calculate total payroll budget using policy allocation
const basePayrollBudget = totalHours * hourlyWage;
if (basePayrollBudget > 0 && coopStore.members.length > 0) {
// Calculate total with oncosts
const monthlyPayroll = basePayrollBudget * (1 + oncostPct / 100);
// Use policy-driven allocation to get actual member pay amounts
const payPolicy = {
relationship: coopStore.policy.relationship,
roleBands: coopStore.policy.roleBands
};
// Convert coopStore members to the format expected by allocatePayroll
const membersForAllocation = coopStore.members.map(m => ({
...m,
displayName: m.name,
// Ensure all required fields exist
monthlyPayPlanned: m.monthlyPayPlanned || 0,
minMonthlyNeeds: m.minMonthlyNeeds || 0,
targetMonthlyPay: m.targetMonthlyPay || 0,
role: m.role || '',
hoursPerMonth: m.hoursPerMonth || 0
}));
// Allocate payroll based on policy
const allocatedMembers = allocatePayroll(membersForAllocation, payPolicy, basePayrollBudget);
// Sum the allocated amounts for total payroll, respecting operating mode
const totalAllocatedPayroll = allocatedMembers.reduce((sum, m) => {
const planned = m.monthlyPayPlanned || 0;
// In "minimum" mode, cap at min needs to show a lean runway scenario
if (coopStore.operatingMode === 'min' && m.minMonthlyNeeds) {
return sum + Math.min(planned, m.minMonthlyNeeds);
}
return sum + planned;
}, 0);
// Apply oncosts to the policy-allocated total
const monthlyPayroll = totalAllocatedPayroll * (1 + oncostPct / 100);
// Create monthly values for payroll
const monthlyValues: Record<string, number> = {};
@ -764,6 +856,7 @@ export const useBudgetStore = defineStore(
groupedExpenses,
isInitialized,
initializeFromWizardData,
refreshPayrollInBudget,
updateBudgetValue,
updateMonthlyValue,
addBudgetItem,