Add department budget % allocation

- Budgets page: new dept split section with % inputs, equal-split button, save
- Weekly/Monthly: per-dept budget column using dept pcts from actuals response
- Backend: dept_budget_pcts in ALLOWED_KEYS; actuals returns dept_pcts map;
  budgets route adds GET /dept-config + PUT /dept-pcts endpoints

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jtricerolph 2026-07-23 09:43:39 +00:00
parent f97772cb72
commit 3e1851fa6d
8 changed files with 204 additions and 33 deletions

View file

@ -26,6 +26,7 @@ export default function Monthly() {
const [scheduled, setScheduled] = useState<Record<string, Record<string, number>>>({})
const [netSales, setNetSales] = useState(0)
const [budget, setBudget] = useState<number | null>(null)
const [deptPcts, setDeptPcts] = useState<Record<string, number>>({})
const [showOncosts, setShowOncosts] = useState(true)
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
@ -51,6 +52,7 @@ export default function Monthly() {
])
setDepts(actRes.departments)
setShowOncosts(actRes.show_oncosts)
setDeptPcts(actRes.dept_pcts)
const schMap: Record<string, Record<string, number>> = {}
for (const dep of schRes.departments) {
@ -243,14 +245,17 @@ export default function Monthly() {
<tbody>
{deptSummary.map(dep => {
const displayCost = isCurrentMonth ? dep.forecast_eom : dep.actual_mtd
const dp = budget != null && budget > 0 ? (displayCost / budget) * 100 : null
const dv = budget != null ? displayCost - budget : null
const depBudg = budget != null && (deptPcts[dep.department_id] ?? 0) > 0
? budget * (deptPcts[dep.department_id] / 100)
: null
const dp = depBudg != null && depBudg > 0 ? (displayCost / depBudg) * 100 : null
const dv = depBudg != null ? displayCost - depBudg : null
return (
<tr key={dep.department_id}>
<td><span style={{ display: 'inline-block', width: 10, height: 10, borderRadius: '50%', background: dep.color, marginRight: 8 }} />{dep.department_name}</td>
<td className="right">{fmtMoney(dep.actual_mtd)}</td>
{isCurrentMonth && <td className="right">{fmtMoney(dep.forecast_eom)}</td>}
<td className="right"></td>
<td className="right">{depBudg != null ? fmtMoney(depBudg) : '—'}</td>
<td className="right">
{dp != null ? <span className={`pct-badge ${pctClass(dp)}`}>{dp.toFixed(1)}%</span> : '—'}
</td>