Features: - Implement two fee types: entrance fee and annual fee (both NT$1,000) - Add 50% discount for disability certificate holders - Add disability certificate upload in member profile - Integrate disability verification into cashier approval workflow - Add membership fee settings in system admin Document permissions: - Fix hard-coded role logic in Document model - Use permission-based authorization instead of role checks Additional features: - Add announcements, general ledger, and trial balance modules - Add income management and accounting entries - Add comprehensive test suite with factories - Update UI translations to Traditional Chinese 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
89 lines
3.3 KiB
PHP
89 lines
3.3 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Member;
|
|
use App\Models\MembershipPayment;
|
|
use App\Models\FinanceDocument;
|
|
use App\Models\Announcement;
|
|
use Illuminate\Http\Request;
|
|
|
|
class AdminDashboardController extends Controller
|
|
{
|
|
public function index()
|
|
{
|
|
// Member statistics
|
|
$totalMembers = Member::count();
|
|
$activeMembers = Member::whereDate('membership_expires_at', '>=', now()->toDateString())->count();
|
|
$expiredMembers = Member::where(function ($q) {
|
|
$q->whereNull('membership_expires_at')
|
|
->orWhereDate('membership_expires_at', '<', now()->toDateString());
|
|
})->count();
|
|
$expiringSoon = Member::whereBetween('membership_expires_at', [
|
|
now()->toDateString(),
|
|
now()->addDays(30)->toDateString()
|
|
])->count();
|
|
|
|
// Payment statistics
|
|
$totalPayments = MembershipPayment::count();
|
|
$totalRevenue = MembershipPayment::sum('amount') ?? 0;
|
|
$recentPayments = MembershipPayment::with('member')
|
|
->orderByDesc('paid_at')
|
|
->limit(5)
|
|
->get();
|
|
$paymentsThisMonth = MembershipPayment::whereYear('paid_at', now()->year)
|
|
->whereMonth('paid_at', now()->month)
|
|
->count();
|
|
$revenueThisMonth = MembershipPayment::whereYear('paid_at', now()->year)
|
|
->whereMonth('paid_at', now()->month)
|
|
->sum('amount') ?? 0;
|
|
|
|
// Finance document statistics
|
|
$pendingApprovals = FinanceDocument::where('status', '!=', FinanceDocument::STATUS_APPROVED_CHAIR)
|
|
->where('status', '!=', FinanceDocument::STATUS_REJECTED)
|
|
->count();
|
|
$fullyApprovedDocs = FinanceDocument::where('status', FinanceDocument::STATUS_APPROVED_CHAIR)->count();
|
|
$rejectedDocs = FinanceDocument::where('status', FinanceDocument::STATUS_REJECTED)->count();
|
|
|
|
// Documents pending user's approval
|
|
$user = auth()->user();
|
|
$myPendingApprovals = 0;
|
|
if ($user->hasRole('finance_cashier')) {
|
|
$myPendingApprovals += FinanceDocument::where('status', FinanceDocument::STATUS_PENDING)->count();
|
|
}
|
|
if ($user->hasRole('finance_accountant')) {
|
|
$myPendingApprovals += FinanceDocument::where('status', FinanceDocument::STATUS_APPROVED_CASHIER)->count();
|
|
}
|
|
if ($user->hasRole('finance_chair')) {
|
|
$myPendingApprovals += FinanceDocument::where('status', FinanceDocument::STATUS_APPROVED_ACCOUNTANT)->count();
|
|
}
|
|
|
|
// Recent announcements
|
|
$recentAnnouncements = Announcement::query()
|
|
->published()
|
|
->active()
|
|
->forAccessLevel($user)
|
|
->orderByDesc('is_pinned')
|
|
->orderByDesc('published_at')
|
|
->limit(5)
|
|
->get();
|
|
|
|
return view('admin.dashboard.index', compact(
|
|
'totalMembers',
|
|
'activeMembers',
|
|
'expiredMembers',
|
|
'expiringSoon',
|
|
'totalPayments',
|
|
'totalRevenue',
|
|
'recentPayments',
|
|
'paymentsThisMonth',
|
|
'revenueThisMonth',
|
|
'pendingApprovals',
|
|
'fullyApprovedDocs',
|
|
'rejectedDocs',
|
|
'myPendingApprovals',
|
|
'recentAnnouncements'
|
|
));
|
|
}
|
|
}
|