- Add Reports page with population/household/document/blotter/budget/project views - Add ReportsController with year-filtered queries for all report types - Add /reports module to config/modules.php - Register /barangay/reports in VueRouteMap and web.php - Remove unused market Home fragments (HomeCoopMember, HomeStoreOwner, etc.) - Remove leftover market Components/Market/ directory - Add Reports card to Home.vue admin quick access
227 lines
8.1 KiB
PHP
227 lines
8.1 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace App\Http\Controllers\Barangay;
|
||
|
||
use App\Enums\UserActions;
|
||
use App\Http\Controllers\Helpers\Permissions\UserPermissions;
|
||
use App\Http\Controllers\Helpers\ResponseHelper;
|
||
use App\Models\Barangay\Blotter;
|
||
use App\Models\Barangay\BarangayBudget;
|
||
use App\Models\Barangay\BarangayProject;
|
||
use App\Models\Barangay\DocumentRequest;
|
||
use App\Models\Barangay\Household;
|
||
use App\Models\Barangay\Resident;
|
||
use Carbon\Carbon;
|
||
use Hypervel\Http\Request;
|
||
use Hypervel\Support\Facades\Auth;
|
||
use Hypervel\Support\Facades\DB;
|
||
|
||
class ReportsController
|
||
{
|
||
public function generate(Request $request)
|
||
{
|
||
if (!UserPermissions::isActionPermitted(Auth::user()->acct_type, UserActions::ViewGlobalReports)) {
|
||
return ResponseHelper::returnUnauthorized();
|
||
}
|
||
|
||
$type = $request->input('type', 'population');
|
||
$year = (int) $request->input('year', date('Y'));
|
||
|
||
$data = match ($type) {
|
||
'population' => $this->populationReport(),
|
||
'households' => $this->householdsReport(),
|
||
'documents' => $this->documentsReport($year),
|
||
'blotters' => $this->blottersReport($year),
|
||
'budget' => $this->budgetReport($year),
|
||
'projects' => $this->projectsReport($year),
|
||
default => [],
|
||
};
|
||
|
||
return response()->json(['success' => true, 'data' => $data]);
|
||
}
|
||
|
||
private function populationReport(): array
|
||
{
|
||
$total = Resident::count();
|
||
$active = Resident::where('is_active', true)->count();
|
||
$voters = Resident::where('voter_status', true)->count();
|
||
|
||
$genderBreakdown = Resident::select('gender', DB::raw('count(*) as count'))
|
||
->whereNotNull('gender')
|
||
->groupBy('gender')
|
||
->pluck('count', 'gender')
|
||
->toArray();
|
||
|
||
$civilBreakdown = Resident::select('civil_status', DB::raw('count(*) as count'))
|
||
->whereNotNull('civil_status')
|
||
->groupBy('civil_status')
|
||
->pluck('count', 'civil_status')
|
||
->toArray();
|
||
|
||
$purokBreakdown = Resident::select('purok', DB::raw('count(*) as count'))
|
||
->whereNotNull('purok')
|
||
->groupBy('purok')
|
||
->orderBy('purok')
|
||
->pluck('count', 'purok')
|
||
->toArray();
|
||
|
||
// Age groups
|
||
$now = Carbon::now();
|
||
$ageGroups = [
|
||
'0–12 (Children)' => [0, 12],
|
||
'13–17 (Youth)' => [13, 17],
|
||
'18–30 (Young Adult)' => [18, 30],
|
||
'31–59 (Adult)' => [31, 59],
|
||
'60+ (Senior)' => [60, 150],
|
||
];
|
||
|
||
$ageBreakdown = [];
|
||
foreach ($ageGroups as $label => [$min, $max]) {
|
||
$from = $now->copy()->subYears($max)->format('Y-m-d');
|
||
$to = $now->copy()->subYears($min)->format('Y-m-d');
|
||
$ageBreakdown[$label] = Resident::whereBetween('date_of_birth', [$from, $to])->count();
|
||
}
|
||
|
||
return compact('total', 'active', 'voters', 'genderBreakdown', 'civilBreakdown', 'purokBreakdown', 'ageBreakdown') + [
|
||
'total_residents' => $total,
|
||
'active_residents' => $active,
|
||
'registered_voters' => $voters,
|
||
'gender_breakdown' => $genderBreakdown,
|
||
'civil_status_breakdown'=> $civilBreakdown,
|
||
'purok_breakdown' => $purokBreakdown,
|
||
'age_breakdown' => $ageBreakdown,
|
||
];
|
||
}
|
||
|
||
private function householdsReport(): array
|
||
{
|
||
$total = Household::count();
|
||
$withElectricity = Household::where('has_electricity', true)->count();
|
||
$withWater = Household::where('has_water', true)->count();
|
||
$avgMembers = round(Household::withCount('chapterMembers')->avg('chapter_members_count') ?? 0, 1);
|
||
|
||
$byOwnership = Household::select('ownership_type', DB::raw('count(*) as count'))
|
||
->groupBy('ownership_type')
|
||
->pluck('count', 'ownership_type')
|
||
->toArray();
|
||
|
||
return [
|
||
'total_households' => $total,
|
||
'with_electricity' => $withElectricity,
|
||
'with_water' => $withWater,
|
||
'avg_members' => $avgMembers,
|
||
'by_ownership' => $byOwnership,
|
||
];
|
||
}
|
||
|
||
private function documentsReport(int $year): array
|
||
{
|
||
$base = DocumentRequest::whereYear('created_at', $year);
|
||
|
||
$total = (clone $base)->count();
|
||
$claimed = (clone $base)->where('status', 'CLAIMED')->count();
|
||
$revenue = (clone $base)->where('payment_status', 'PAID')->sum('base_fee');
|
||
|
||
$byStatus = (clone $base)->select('status', DB::raw('count(*) as count'))
|
||
->groupBy('status')
|
||
->pluck('count', 'status')
|
||
->toArray();
|
||
|
||
$byType = (clone $base)->select(
|
||
'barangay_request_types.name',
|
||
DB::raw('count(*) as count'),
|
||
DB::raw('sum(case when barangay_document_requests.payment_status = \'PAID\' then barangay_document_requests.base_fee else 0 end) as revenue')
|
||
)
|
||
->leftJoin('barangay_request_types', 'barangay_request_types.id', '=', 'barangay_document_requests.request_type_id')
|
||
->groupBy('barangay_request_types.name')
|
||
->get()
|
||
->toArray();
|
||
|
||
return compact('total', 'claimed', 'revenue', 'byStatus', 'byType') + [
|
||
'total_revenue' => $revenue,
|
||
'by_status' => $byStatus,
|
||
'by_type' => $byType,
|
||
];
|
||
}
|
||
|
||
private function blottersReport(int $year): array
|
||
{
|
||
$base = Blotter::whereYear('created_at', $year);
|
||
|
||
$total = (clone $base)->count();
|
||
$resolved = (clone $base)->whereIn('status', ['RESOLVED', 'SETTLED'])->count();
|
||
$pending = (clone $base)->whereIn('status', ['FILED', 'FOR_HEARING'])->count();
|
||
|
||
$byStatus = (clone $base)->select('status', DB::raw('count(*) as count'))
|
||
->groupBy('status')
|
||
->pluck('count', 'status')
|
||
->toArray();
|
||
|
||
$byType = (clone $base)->select('incident_type', DB::raw('count(*) as count'))
|
||
->groupBy('incident_type')
|
||
->pluck('count', 'incident_type')
|
||
->toArray();
|
||
|
||
return [
|
||
'total' => $total,
|
||
'resolved' => $resolved,
|
||
'pending' => $pending,
|
||
'by_status' => $byStatus,
|
||
'by_incident_type'=> $byType,
|
||
];
|
||
}
|
||
|
||
private function budgetReport(int $year): array
|
||
{
|
||
$income = BarangayBudget::income()->byYear($year)->sum('amount');
|
||
$expense = BarangayBudget::expense()->byYear($year)->sum('amount');
|
||
$balance = $income - $expense;
|
||
|
||
$bySource = BarangayBudget::select('source', 'category', DB::raw('sum(amount) as amount'))
|
||
->byYear($year)
|
||
->groupBy('source', 'category')
|
||
->orderBy('category')
|
||
->get()
|
||
->toArray();
|
||
|
||
return [
|
||
'summary' => compact('income', 'expense', 'balance') + [
|
||
'total_income' => $income,
|
||
'total_expense' => $expense,
|
||
],
|
||
'by_source' => $bySource,
|
||
];
|
||
}
|
||
|
||
private function projectsReport(int $year): array
|
||
{
|
||
$base = BarangayProject::whereYear('created_at', $year);
|
||
|
||
$total = (clone $base)->count();
|
||
$ongoing = (clone $base)->where('status', 'ONGOING')->count();
|
||
$completed = (clone $base)->where('status', 'COMPLETED')->count();
|
||
$budget = (clone $base)->sum('budget');
|
||
|
||
$byStatus = (clone $base)->select('status', DB::raw('count(*) as count'))
|
||
->groupBy('status')
|
||
->pluck('count', 'status')
|
||
->toArray();
|
||
|
||
$byType = (clone $base)->select('type', DB::raw('count(*) as count'))
|
||
->groupBy('type')
|
||
->get()
|
||
->toArray();
|
||
|
||
return [
|
||
'total' => $total,
|
||
'ongoing' => $ongoing,
|
||
'completed' => $completed,
|
||
'total_budget' => $budget,
|
||
'by_status' => $byStatus,
|
||
'by_type' => $byType,
|
||
];
|
||
}
|
||
}
|