with('createdBy', 'approvedBy'); // Filter by fiscal year if ($fiscalYear = $request->integer('fiscal_year')) { $query->where('fiscal_year', $fiscalYear); } // Filter by status if ($status = $request->string('status')->toString()) { $query->where('status', $status); } $budgets = $query->orderByDesc('fiscal_year') ->orderByDesc('created_at') ->paginate(15); // Get unique fiscal years for filter dropdown $fiscalYears = Budget::select('fiscal_year') ->distinct() ->orderByDesc('fiscal_year') ->pluck('fiscal_year'); return view('admin.budgets.index', [ 'budgets' => $budgets, 'fiscalYears' => $fiscalYears, ]); } public function create() { return view('admin.budgets.create'); } public function store(Request $request) { $validated = $request->validate([ 'fiscal_year' => ['required', 'integer', 'min:2000', 'max:2100'], 'name' => ['required', 'string', 'max:255'], 'period_type' => ['required', 'in:annual,quarterly,monthly'], 'period_start' => ['required', 'date'], 'period_end' => ['required', 'date', 'after:period_start'], 'notes' => ['nullable', 'string'], ]); $budget = Budget::create([ ...$validated, 'status' => Budget::STATUS_DRAFT, 'created_by_user_id' => $request->user()->id, ]); AuditLogger::log('budget.created', $budget, $validated); return redirect() ->route('admin.budgets.edit', $budget) ->with('status', __('Budget created successfully. Add budget items below.')); } public function show(Budget $budget) { $budget->load([ 'createdBy', 'approvedBy', 'budgetItems.chartOfAccount', 'budgetItems' => fn($q) => $q->orderBy('chart_of_account_id'), ]); // Group budget items by account type $incomeItems = $budget->budgetItems->filter(fn($item) => $item->chartOfAccount->isIncome()); $expenseItems = $budget->budgetItems->filter(fn($item) => $item->chartOfAccount->isExpense()); return view('admin.budgets.show', [ 'budget' => $budget, 'incomeItems' => $incomeItems, 'expenseItems' => $expenseItems, ]); } public function edit(Budget $budget) { if (!$budget->canBeEdited()) { return redirect() ->route('admin.budgets.show', $budget) ->with('error', __('This budget cannot be edited.')); } $budget->load(['budgetItems.chartOfAccount']); // Get all active income and expense accounts $incomeAccounts = ChartOfAccount::where('account_type', 'income') ->where('is_active', true) ->orderBy('account_code') ->get(); $expenseAccounts = ChartOfAccount::where('account_type', 'expense') ->where('is_active', true) ->orderBy('account_code') ->get(); return view('admin.budgets.edit', [ 'budget' => $budget, 'incomeAccounts' => $incomeAccounts, 'expenseAccounts' => $expenseAccounts, ]); } public function update(Request $request, Budget $budget) { if (!$budget->canBeEdited()) { abort(403, 'This budget cannot be edited.'); } $validated = $request->validate([ 'name' => ['required', 'string', 'max:255'], 'period_start' => ['required', 'date'], 'period_end' => ['required', 'date', 'after:period_start'], 'notes' => ['nullable', 'string'], 'budget_items' => ['nullable', 'array'], 'budget_items.*.chart_of_account_id' => ['required', 'exists:chart_of_accounts,id'], 'budget_items.*.budgeted_amount' => ['required', 'numeric', 'min:0'], 'budget_items.*.notes' => ['nullable', 'string'], ]); DB::transaction(function () use ($budget, $validated, $request) { // Update budget $budget->update([ 'name' => $validated['name'], 'period_start' => $validated['period_start'], 'period_end' => $validated['period_end'], 'notes' => $validated['notes'] ?? null, ]); // Delete existing budget items and recreate $budget->budgetItems()->delete(); // Create new budget items if (!empty($validated['budget_items'])) { foreach ($validated['budget_items'] as $itemData) { if ($itemData['budgeted_amount'] > 0) { BudgetItem::create([ 'budget_id' => $budget->id, 'chart_of_account_id' => $itemData['chart_of_account_id'], 'budgeted_amount' => $itemData['budgeted_amount'], 'notes' => $itemData['notes'] ?? null, ]); } } } AuditLogger::log('budget.updated', $budget, [ 'user' => $request->user()->name, 'items_count' => count($validated['budget_items'] ?? []), ]); }); return redirect() ->route('admin.budgets.show', $budget) ->with('status', __('Budget updated successfully.')); } public function submit(Request $request, Budget $budget) { if (!$budget->isDraft()) { abort(403, 'Only draft budgets can be submitted.'); } if ($budget->budgetItems()->count() === 0) { return redirect() ->route('admin.budgets.edit', $budget) ->with('error', __('Cannot submit budget without budget items.')); } $budget->update(['status' => Budget::STATUS_SUBMITTED]); AuditLogger::log('budget.submitted', $budget, ['submitted_by' => $request->user()->name]); return redirect() ->route('admin.budgets.show', $budget) ->with('status', __('Budget submitted for approval.')); } public function approve(Request $request, Budget $budget) { if (!$budget->canBeApproved()) { abort(403, 'This budget cannot be approved.'); } // Check if user has permission (admin or chair) $user = $request->user(); if (!$user->hasRole('chair') && !$user->is_admin && !$user->hasRole('admin')) { abort(403, 'Only chair can approve budgets.'); } $budget->update([ 'status' => Budget::STATUS_APPROVED, 'approved_by_user_id' => $user->id, 'approved_at' => now(), ]); AuditLogger::log('budget.approved', $budget, ['approved_by' => $user->name]); return redirect() ->route('admin.budgets.show', $budget) ->with('status', __('Budget approved successfully.')); } public function activate(Request $request, Budget $budget) { if (!$budget->isApproved()) { abort(403, 'Only approved budgets can be activated.'); } $budget->update(['status' => Budget::STATUS_ACTIVE]); AuditLogger::log('budget.activated', $budget, ['activated_by' => $request->user()->name]); return redirect() ->route('admin.budgets.show', $budget) ->with('status', __('Budget activated successfully.')); } public function close(Request $request, Budget $budget) { if (!$budget->isActive()) { abort(403, 'Only active budgets can be closed.'); } $budget->update(['status' => Budget::STATUS_CLOSED]); AuditLogger::log('budget.closed', $budget, ['closed_by' => $request->user()->name]); return redirect() ->route('admin.budgets.show', $budget) ->with('status', __('Budget closed successfully.')); } public function destroy(Request $request, Budget $budget) { if (!$budget->isDraft()) { abort(403, 'Only draft budgets can be deleted.'); } $fiscalYear = $budget->fiscal_year; $budget->delete(); AuditLogger::log('budget.deleted', null, [ 'fiscal_year' => $fiscalYear, 'deleted_by' => $request->user()->name, ]); return redirect() ->route('admin.budgets.index') ->with('status', __('Budget deleted successfully.')); } }