0 UNION SELECT $next_year"); $view = in_array($_GET['view'] ?? '', ['month', 'edit'], true) ? $_GET['view'] : 'summary'; $can_edit = lookupUserPermission('module_financial') >= 2; if ($view === 'edit' && !$can_edit) { $view = 'summary'; } $current_year = intval(date('Y')); $months_elapsed = reportMonthsElapsed($year); // How far through the year we are, to the day - what the budget should have covered by now $year_elapsed = $year < $current_year ? 1.0 : ($year > $current_year ? 0.0 : (intval(date('z')) + 1) / (intval(date('L')) ? 366 : 365)); // The plan: [category => [1..12 => amount]] $budget = []; $sql = mysqli_query($mysqli, "SELECT budget_category_id, budget_month, SUM(budget_amount) AS amount FROM budget WHERE budget_year = $year GROUP BY budget_category_id, budget_month"); while ($row = mysqli_fetch_assoc($sql)) { $cat = intval($row['budget_category_id']); $budget[$cat] = $budget[$cat] ?? array_fill(1, 12, 0.0); $month = intval($row['budget_month']); if ($month >= 1 && $month <= 12) { $budget[$cat][$month] += floatval($row['amount']); } } $has_prev_budget = intval(dashboardCount("SELECT COUNT(*) FROM budget WHERE budget_year = " . ($year - 1))); // What was spent $actual = reportExpensesByCategory("$year-01-01", "$year-12-31"); $categories = reportCategoryRows('Expense', $actual); foreach ($budget as $cat => $months) { if (!isset($categories[$cat])) { // Budget against a category that is archived or gone - still show it so it can be cleared $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT category_id, category_name, category_color, category_archived_at FROM categories WHERE category_id = $cat")); $categories[$cat] = $row ?: ['category_id' => $cat, 'category_name' => 'Deleted category', 'category_color' => '', 'category_archived_at' => date('Y-m-d')]; } } uasort($categories, function ($a, $b) { return strcasecmp($a['category_name'], $b['category_name']); }); unset($categories[0]); // One line per category $lines = []; foreach ($categories as $cat => $category) { $plan = $budget[$cat] ?? array_fill(1, 12, 0.0); $spent = reportGridRow($actual, $cat); $plan_total = array_sum($plan); $plan_to_date = 0.0; for ($m = 1; $m <= $months_elapsed; $m++) { $plan_to_date += $plan[$m]; } // The month we are in only counts as far as we are through it if ($year === $current_year && $months_elapsed) { $plan_to_date -= $plan[$months_elapsed] * (1 - intval(date('j')) / intval(date('t'))); } $lines[$cat] = [ 'name' => $category['category_name'], 'archived' => !empty($category['category_archived_at']), 'plan' => $plan, 'plan_total' => $plan_total, 'plan_to_date' => $plan_to_date, 'spent' => $spent['months'], 'spent_total' => $spent['total'], ]; } $shown = array_filter($lines, function ($line) { return $line['plan_total'] > 0 || $line['spent_total'] > 0; }); $plan_total = array_sum(array_column($lines, 'plan_total')); $plan_to_date = array_sum(array_column($lines, 'plan_to_date')); $spent_total = array_sum(array_column($lines, 'spent_total')); $over = array_filter($shown, function ($line) { return $line['plan_total'] > 0 && $line['spent_total'] > $line['plan_to_date'] + 0.005; }); $unbudgeted = array_filter($shown, function ($line) { return $line['plan_total'] <= 0 && $line['spent_total'] > 0; }); if (reportExporting()) { $columns = ['name' => ['label' => 'Category']]; for ($m = 1; $m <= 12; $m++) { $columns["b$m"] = ['label' => date('M', mktime(0, 0, 0, $m, 1)) . ' budget', 'type' => 'money']; $columns["a$m"] = ['label' => date('M', mktime(0, 0, 0, $m, 1)) . ' actual', 'type' => 'money']; } $columns['plan_total'] = ['label' => "$year budget", 'type' => 'money']; $columns['spent_total'] = ['label' => "$year actual", 'type' => 'money']; $columns['left'] = ['label' => 'Remaining', 'type' => 'money']; $rows = []; foreach ($shown as $line) { $row = ['name' => $line['name'], 'plan_total' => $line['plan_total'], 'spent_total' => $line['spent_total'], 'left' => $line['plan_total'] - $line['spent_total']]; for ($m = 1; $m <= 12; $m++) { $row["b$m"] = $line['plan'][$m]; $row["a$m"] = $line['spent'][$m]; } $rows[] = $row; } reportCsv("Budget vs Actual $year", $columns, $rows, ['name' => 'Total', 'plan_total' => $plan_total, 'spent_total' => $spent_total, 'left' => $plan_total - $spent_total]); } // Month by month, all categories together $plan_months = array_fill(1, 12, 0.0); $spent_months = array_fill(1, 12, 0.0); foreach ($lines as $line) { for ($m = 1; $m <= 12; $m++) { $plan_months[$m] += $line['plan'][$m]; $spent_months[$m] += $line['spent'][$m]; } } require_once "includes/inc_report_layout.php"; $money_input = function ($value) { return $value > 0 ? number_format($value, 2, '.', '') : ''; }; $used_meter = function ($spent, $plan) { if ($plan <= 0) { return $spent > 0 ? 'No budget' : ''; } $share = $spent / $plan * 100; $tone = $share > 100 ? 'bg-danger' : ($share > 90 ? 'bg-warning' : ''); return '
| Category | = date('M', mktime(0, 0, 0, $m, 1)) ?> | = $year ?> | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| = escapeHtml($line['name']) ?> |
= reportGridAmount($spent, "/agent/expenses.php?category=$cat&canned_date=custom&dtf=$dtf&dtt=$dtt") ?>
= $plan > 0 ? reportMoneyWhole($plan) : ' ' ?>
|
= reportGridAmount($line['spent_total']) ?>
= $line['plan_total'] > 0 ? reportMoneyWhole($line['plan_total']) : ' ' ?>
|
|||||||||||
| No budget and no spending in = $year ?>. | |||||||||||||
| Total |
= reportGridAmount($spent_months[$m]) ?>
= $plan_months[$m] > 0 ? reportMoneyWhole($plan_months[$m]) : ' ' ?>
|
= reportGridAmount($spent_total) ?>
= reportMoneyWhole($plan_total) ?>
|
|||||||||||