feat: tambah widget statistik kas dan perbaikan alur verifikasi transaksi
- Tambah CashStatsWidget: total saldo, pemasukan/pengeluaran bulan ini, saldo bulan lalu - Widget hanya tampil di halaman transaksi kas (bukan dashboard) - Hanya transaksi yang sudah diverifikasi masuk ke perhitungan total kas - Perbaiki namespace Action notifikasi (Filament v5) - Perbaiki observer: hapus throw Exception yang menyebabkan widget hilang - Tambah redirect setelah aksi setujui/tolak/verifikasi agar halaman refresh otomatis - Tambah file DOKUMENTASI.md
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
namespace App\Filament\Resources\CashRecords\Pages;
|
||||
|
||||
use App\Filament\Resources\CashRecords\CashRecordResource;
|
||||
use App\Filament\Widgets\CashStatsWidget;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
@@ -12,8 +13,11 @@ class ListCashRecords extends ListRecords
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
CreateAction::make(),
|
||||
];
|
||||
return [CreateAction::make()];
|
||||
}
|
||||
|
||||
protected function getHeaderWidgets(): array
|
||||
{
|
||||
return [CashStatsWidget::class];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,8 @@ class CashRecordsTable
|
||||
'success',
|
||||
route('filament.admin.resources.cash-records.index')
|
||||
);
|
||||
}),
|
||||
})
|
||||
->after(fn ($livewire) => redirect(request()->header('Referer'))),
|
||||
|
||||
// Ketua: tolak transaksi 500rb–2jt
|
||||
Action::make('reject_ketua')
|
||||
@@ -125,7 +126,8 @@ class CashRecordsTable
|
||||
'danger',
|
||||
route('filament.admin.resources.cash-records.index')
|
||||
);
|
||||
}),
|
||||
})
|
||||
->after(fn ($livewire) => redirect(request()->header('Referer'))),
|
||||
|
||||
// Bendahara/ketua: verifikasi (hanya jika approval sudah selesai atau tidak diperlukan)
|
||||
Action::make('verify')
|
||||
@@ -155,7 +157,8 @@ class CashRecordsTable
|
||||
->action(fn (CashRecord $record) => $record->update([
|
||||
'verified_by' => auth()->id(),
|
||||
'verified_at' => now(),
|
||||
])),
|
||||
]))
|
||||
->after(fn ($livewire) => redirect(request()->header('Referer'))),
|
||||
|
||||
EditAction::make()->hidden(fn ($record) => $record->verified_at !== null),
|
||||
])
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use App\Models\CashRecord;
|
||||
use Filament\Widgets\StatsOverviewWidget;
|
||||
use Filament\Widgets\StatsOverviewWidget\Stat;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class CashStatsWidget extends StatsOverviewWidget
|
||||
{
|
||||
protected int | string | array $columnSpan = 'full';
|
||||
|
||||
public static function canView(): bool
|
||||
{
|
||||
return request()->routeIs('filament.admin.resources.cash-records.index');
|
||||
}
|
||||
|
||||
protected function getStats(): array
|
||||
{
|
||||
$saldo = fn ($query) => $query
|
||||
->whereNotNull('verified_at')
|
||||
->join('cash_categories', 'cash_records.category_id', '=', 'cash_categories.id')
|
||||
->selectRaw("SUM(CASE WHEN cash_categories.name = 'pemasukan' THEN amount ELSE -amount END) as saldo")
|
||||
->value('saldo') ?? 0;
|
||||
|
||||
$bulanIni = now()->startOfMonth();
|
||||
$bulanLalu = now()->subMonth()->startOfMonth();
|
||||
|
||||
$totalSaldo = $saldo(CashRecord::query());
|
||||
$pemasukanBulanIni = CashRecord::whereNotNull('verified_at')
|
||||
->join('cash_categories', 'cash_records.category_id', '=', 'cash_categories.id')
|
||||
->where('cash_categories.name', 'pemasukan')
|
||||
->whereMonth('date', $bulanIni->month)->whereYear('date', $bulanIni->year)
|
||||
->sum('amount');
|
||||
$pengeluaranBulanIni = CashRecord::whereNotNull('verified_at')
|
||||
->join('cash_categories', 'cash_records.category_id', '=', 'cash_categories.id')
|
||||
->where('cash_categories.name', 'pengeluaran')
|
||||
->whereMonth('date', $bulanIni->month)->whereYear('date', $bulanIni->year)
|
||||
->sum('amount');
|
||||
$saldoBulanLalu = $saldo(CashRecord::query()->where('date', '<', $bulanIni));
|
||||
|
||||
return [
|
||||
Stat::make('Total Saldo', 'Rp ' . number_format($totalSaldo, 0, ',', '.'))
|
||||
->description('Akumulasi semua transaksi')
|
||||
->color($totalSaldo >= 0 ? 'success' : 'danger')
|
||||
->icon('heroicon-o-banknotes'),
|
||||
|
||||
Stat::make('Pemasukan ' . $bulanIni->translatedFormat('F Y'), 'Rp ' . number_format($pemasukanBulanIni, 0, ',', '.'))
|
||||
->description('Total pemasukan bulan ini')
|
||||
->color('success')
|
||||
->icon('heroicon-o-arrow-trending-up'),
|
||||
|
||||
Stat::make('Pengeluaran ' . $bulanIni->translatedFormat('F Y'), 'Rp ' . number_format($pengeluaranBulanIni, 0, ',', '.'))
|
||||
->description('Total pengeluaran bulan ini')
|
||||
->color('danger')
|
||||
->icon('heroicon-o-arrow-trending-down'),
|
||||
|
||||
Stat::make('Saldo Bulan Lalu', 'Rp ' . number_format($saldoBulanLalu, 0, ',', '.'))
|
||||
->description($bulanLalu->translatedFormat('F Y'))
|
||||
->color($saldoBulanLalu >= 0 ? 'info' : 'warning')
|
||||
->icon('heroicon-o-clock'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,8 @@ class StatsOverview extends StatsOverviewWidget
|
||||
{
|
||||
protected function getStats(): array
|
||||
{
|
||||
$totalKas = CashRecord::join('cash_categories', 'cash_records.category_id', '=', 'cash_categories.id')
|
||||
$totalKas = CashRecord::whereNotNull('verified_at')
|
||||
->join('cash_categories', 'cash_records.category_id', '=', 'cash_categories.id')
|
||||
->selectRaw("SUM(CASE WHEN cash_categories.name = 'pemasukan' THEN amount ELSE -amount END) as saldo")
|
||||
->value('saldo') ?? 0;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user