From 6fe1e3943c4d5f492d15a577c6303feb1068efd3 Mon Sep 17 00:00:00 2001 From: renee-png Date: Tue, 9 Jun 2026 22:43:45 -0400 Subject: [PATCH] Budget Workbook: editable unit count for per-unit assessment rate The per-unit assessment (annual expenses / 12 / units) used the live association unit count only. Add an override so the rate can use weighted/excluded units, persisted on accounting.budget_workbooks.unit_override (null = live count). New "# Units" control with a reset link; summary card and CSV use the effective count. Co-Authored-By: Claude Opus 4.8 --- src/pages/BudgetWorkbookPage.tsx | 21 ++++++++++++++++--- ...09120000_budget_workbook_unit_override.sql | 5 +++++ 2 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 supabase/migrations/20260609120000_budget_workbook_unit_override.sql diff --git a/src/pages/BudgetWorkbookPage.tsx b/src/pages/BudgetWorkbookPage.tsx index 0397040..9d68cce 100644 --- a/src/pages/BudgetWorkbookPage.tsx +++ b/src/pages/BudgetWorkbookPage.tsx @@ -54,6 +54,7 @@ export default function BudgetWorkbookPage() { const [ytdOv, setYtdOv] = useState>({}); const [infl, setInfl] = useState>({}); const [projOv, setProjOv] = useState>({}); + const [unitOv, setUnitOv] = useState(""); // "" = use the live unit count const [saving, setSaving] = useState(false); const [pushing, setPushing] = useState(false); @@ -114,6 +115,7 @@ export default function BudgetWorkbookPage() { } setYtdOv(y); setInfl(i); setProjOv(p); if ((workbook.head as any)?.through_month) setThrough((workbook.head as any).through_month); + setUnitOv((workbook.head as any)?.unit_override != null ? String((workbook.head as any).unit_override) : ""); }, [workbook]); // Computed YTD actual per account from the GL @@ -146,7 +148,10 @@ export default function BudgetWorkbookPage() { const expenseAnnual = sum(sections.exp); const incomeAnnual = sum(sections.inc); const monthly = expenseAnnual / 12; - const perUnit = unitCount > 0 ? monthly / unitCount : 0; + // Per-unit assessment rate = annual expenses / 12 / units. Units default to the + // live association count but can be overridden (weighted/excluded units). + const effUnits = unitOv.trim() ? Number(unitOv) : unitCount; + const perUnit = effUnits > 0 ? monthly / effUnits : 0; const resetRow = (id: string) => { setYtdOv((m) => ({ ...m, [id]: "" })); @@ -159,7 +164,7 @@ export default function BudgetWorkbookPage() { setSaving(true); try { const { data: head, error: hErr } = await accounting.from("budget_workbooks") - .upsert({ company_id: cid, fiscal_year: fy, through_month: through, updated_at: new Date().toISOString() }, + .upsert({ company_id: cid, fiscal_year: fy, through_month: through, unit_override: unitOv.trim() ? Math.round(Number(unitOv)) : null, updated_at: new Date().toISOString() }, { onConflict: "company_id,fiscal_year" }) .select("id").single(); if (hErr || !head) throw new Error(hErr?.message || "save failed"); @@ -333,6 +338,16 @@ export default function BudgetWorkbookPage() { +
+ + setUnitOv(e.target.value)} /> + {unitOv.trim() && Number(unitOv) !== unitCount && ( + + )} +