Files
BarangaySystem/app/Http/Controllers/Barangay/ReportsController.php
Jonathan Sykes bee4a1f5ab
Some checks failed
tests / PHP 8.2 (swoole-5.1.6) (push) Has been cancelled
tests / PHP 8.3 (swoole-5.1.6) (push) Has been cancelled
tests / PHP 8.4 (swoole-6.0) (push) Has been cancelled
feat: complete all plan phases — reports, cleanup market fragments
- 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
2026-06-07 03:15:04 +08:00

227 lines
8.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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 = [
'012 (Children)' => [0, 12],
'1317 (Youth)' => [13, 17],
'1830 (Young Adult)' => [18, 30],
'3159 (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,
];
}
}