From 9c68d4b7347db066817b930e8c8b446da99ea1a6 Mon Sep 17 00:00:00 2001 From: SitiAfiah Date: Fri, 5 Jun 2026 19:03:03 +0700 Subject: [PATCH] genarate spp serta update anggota --- .../app/Http/Controllers/SppController.php | 147 ++-- manajemen_mp/app/Models/Anggota.php | 13 + manajemen_mp/app/Models/Kolat.php | 7 + ...6_05_161754_create_kolat_pelatih_table.php | 32 + .../database/seeders/DatabaseSeeder.php | 2 +- manajemen_mp/database/seeders/KolatSeeder.php | 2 +- .../resources/views/layout/app.blade.php | 16 + .../views/login-register/register.blade.php | 2 +- .../views/spp/pengurus/create.blade.php | 34 +- .../views/spp/pengurus/index.blade.php | 675 +++++++++++------- 10 files changed, 591 insertions(+), 339 deletions(-) create mode 100644 manajemen_mp/database/migrations/2026_06_05_161754_create_kolat_pelatih_table.php diff --git a/manajemen_mp/app/Http/Controllers/SppController.php b/manajemen_mp/app/Http/Controllers/SppController.php index b5226f3..a802f8c 100644 --- a/manajemen_mp/app/Http/Controllers/SppController.php +++ b/manajemen_mp/app/Http/Controllers/SppController.php @@ -56,35 +56,52 @@ public function create() return view('spp.pengurus.create', compact('data_anggota')); } - public function store(Request $request) - { - $request->validate([ - 'anggota_id' => 'required|exists:anggotas,id', - 'bulan' => 'required', - 'tahun' => 'required', - 'nominal' => 'required|numeric', - 'jatuh_tempo' => 'required|date', - ]); + public function store(Request $request) +{ + // 1. Validasi Input + $request->validate([ + 'anggota_id' => 'required|exists:anggotas,id', + 'bulan' => 'required', + 'tahun' => 'required', + 'nominal' => 'required|numeric', + 'jatuh_tempo' => 'required|date', + ]); - try { - $anggota = Anggota::findOrFail($request->anggota_id); + // 2. PENJAGA: Cek apakah tagihan untuk anggota, bulan, dan tahun tersebut sudah ada + $cekDuplikat = Spp::where('anggota_id', $request->anggota_id) + ->where('bulan', $request->bulan) + ->where('tahun', $request->tahun) + ->exists(); - Spp::create([ - 'anggota_id' => $request->anggota_id, - 'kolat_id' => $anggota->kolat_id, - 'bulan' => $request->bulan, - 'tahun' => $request->tahun, - 'nominal' => $request->nominal, - 'jatuh_tempo' => $request->jatuh_tempo, - 'status' => 'belum_bayar', - ]); - - return redirect()->route('spp.index')->with('success', 'Tagihan berhasil dibuat!'); - } catch (\Exception $e) { - return redirect()->back()->withInput()->with('error', 'Gagal menyimpan: '.$e->getMessage()); - } + // Jika sudah ada, langsung tendang balik ke form dengan pesan error + if ($cekDuplikat) { + return redirect()->back() + ->withInput() // withInput menjaga agar form tidak kosong lagi + ->with('error', "Gagal! Tagihan untuk bulan {$request->bulan} {$request->tahun} sudah terdaftar untuk anggota ini."); } + // 3. Proses Penyimpanan (Jika lolos penjagaan) + try { + $anggota = Anggota::findOrFail($request->anggota_id); + + Spp::create([ + 'anggota_id' => $request->anggota_id, + 'kolat_id' => $anggota->kolat_id, + 'bulan' => $request->bulan, + 'tahun' => $request->tahun, + 'nominal' => $request->nominal, + 'jatuh_tempo' => $request->jatuh_tempo, + // Catatan Kecil: Pastikan penulisan 'belum_bayar' ini sama persis + // dengan filter yang dipakai di Dashboard Anggota ya! + 'status' => 'pending', + ]); + + return redirect()->route('spp.index')->with('success', 'Tagihan manual berhasil dibuat!'); + + } catch (\Exception $e) { + return redirect()->back()->withInput()->with('error', 'Gagal menyimpan: '.$e->getMessage()); + } +} public function bayar(Request $request, $id) { $spp = Spp::with('anggota')->findOrFail($id); @@ -127,43 +144,57 @@ public function bayar(Request $request, $id) } } - public function generateTagihan(Request $request) - { - $bulanIni = Carbon::now()->translatedFormat('F'); - $tahunIni = Carbon::now()->year; - $jatuhTempo = Carbon::now()->day(10); + public function generateTagihan(Request $request) +{ + // PERBAIKAN BHS INDONESIA: Tambahkan ->locale('id') agar outputnya pasti "Juni" + $bulanIni = \Carbon\Carbon::now()->locale('id')->translatedFormat('F'); + $tahunIni = \Carbon\Carbon::now()->year; - $anggota = Anggota::all(); + // Sesuaikan format jatuh tempo ke Y-m-d agar aman saat masuk ke database + $jatuhTempo = \Carbon\Carbon::now()->day(10)->format('Y-m-d'); - DB::beginTransaction(); - try { - foreach ($anggota as $agt) { - $exists = Spp::where('anggota_id', $agt->id) - ->where('bulan', $bulanIni) - ->where('tahun', $tahunIni) - ->exists(); + // --- 1. PENJAGA: Cek apakah bulan dan tahun ini sudah pernah di-generate --- + $cekSudahGenerate = Spp::where('bulan', $bulanIni) + ->where('tahun', $tahunIni) + ->exists(); - if (! $exists) { - Spp::create([ - 'anggota_id' => $agt->id, - 'kolat_id' => $agt->kolat_id, - 'bulan' => $bulanIni, - 'tahun' => $tahunIni, - 'nominal' => 50000, - 'jatuh_tempo' => $jatuhTempo, - 'status' => 'belum_bayar', - ]); - } - } - DB::commit(); - - return redirect()->route('spp.index')->with('success', 'Tagihan berhasil dibuat!'); - } catch (\Exception $e) { - DB::rollback(); - - return redirect()->back()->with('error', 'Gagal: '.$e->getMessage()); - } + if ($cekSudahGenerate) { + // Jika sudah ada, langsung tolak dan kembalikan ke halaman index dengan pesan warning + return redirect()->route('spp.index')->with('warning', "Tagihan SPP bulan {$bulanIni} {$tahunIni} sudah pernah dibuat!"); } + // -------------------------------------------------------------------------- + + $anggota = Anggota::where('status', 'Aktif')->get(); // Pastikan hanya menagih yang aktif + + DB::beginTransaction(); + try { + foreach ($anggota as $agt) { + // Pengecekan per-user untuk keamanan ekstra + $exists = Spp::where('anggota_id', $agt->id) + ->where('bulan', $bulanIni) + ->where('tahun', $tahunIni) + ->exists(); + + if (! $exists) { + Spp::create([ + 'anggota_id' => $agt->id, + 'kolat_id' => $agt->kolat_id, + 'bulan' => $bulanIni, + 'tahun' => $tahunIni, + 'nominal' => 50000, + 'jatuh_tempo' => $jatuhTempo, + 'status' => 'belum_bayar', // PERBAIKAN STATUS: Disamakan menjadi 'belum_bayar' + ]); + } + } + DB::commit(); + + return redirect()->route('spp.index')->with('success', "Tagihan bulan {$bulanIni} berhasil dibuat!"); + } catch (\Exception $e) { + DB::rollback(); + return redirect()->back()->with('error', 'Gagal: '.$e->getMessage()); + } +} public function exportExcel(Request $request) { diff --git a/manajemen_mp/app/Models/Anggota.php b/manajemen_mp/app/Models/Anggota.php index 2d41e4b..c7aece6 100644 --- a/manajemen_mp/app/Models/Anggota.php +++ b/manajemen_mp/app/Models/Anggota.php @@ -30,6 +30,19 @@ public function role() return $this->belongsTo(Role::class, 'role_id'); } + public function kolatLatihan() + { + return $this->belongsToMany(Kolat::class, 'kolat_pelatih', 'anggota_id', 'kolat_id'); + } + + // Scope agar gampang memanggil data yang hanya ber-role Pelatih + public function scopeIsPelatih($query) + { + return $query->whereHas('role', function ($q) { + $q->where('nama_role', 'Pelatih'); + }); + } + // Relasi: Mendapatkan riwayat kehadiran anggota public function riwayatPresensi() { diff --git a/manajemen_mp/app/Models/Kolat.php b/manajemen_mp/app/Models/Kolat.php index 6995bcb..ef83ef9 100644 --- a/manajemen_mp/app/Models/Kolat.php +++ b/manajemen_mp/app/Models/Kolat.php @@ -23,4 +23,11 @@ public function anggota() { return $this->hasMany(Anggota::class, 'kolat_id'); } + + public function pelatihs() + { + // Panggil relasi pivot, lalu sambungkan dengan scope isPelatih() + return $this->belongsToMany(Anggota::class, 'kolat_pelatih', 'kolat_id', 'anggota_id') + ->isPelatih(); + } } diff --git a/manajemen_mp/database/migrations/2026_06_05_161754_create_kolat_pelatih_table.php b/manajemen_mp/database/migrations/2026_06_05_161754_create_kolat_pelatih_table.php new file mode 100644 index 0000000..6f0f36d --- /dev/null +++ b/manajemen_mp/database/migrations/2026_06_05_161754_create_kolat_pelatih_table.php @@ -0,0 +1,32 @@ +id(); + + // Relasi ke anggota (yang bertindak sebagai pelatih) dan kolat + $table->foreignId('anggota_id')->constrained('anggotas')->onDelete('cascade'); + $table->foreignId('kolat_id')->constrained('kolat')->onDelete('cascade'); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('kolat_pelatih'); + } +}; diff --git a/manajemen_mp/database/seeders/DatabaseSeeder.php b/manajemen_mp/database/seeders/DatabaseSeeder.php index 54b105e..e467d4c 100644 --- a/manajemen_mp/database/seeders/DatabaseSeeder.php +++ b/manajemen_mp/database/seeders/DatabaseSeeder.php @@ -13,7 +13,7 @@ class DatabaseSeeder extends Seeder public function run(): void { $this->call([ - // RoleSeeder::class, + RoleSeeder::class, KolatSeeder::class, UserSeeder::class, ]); diff --git a/manajemen_mp/database/seeders/KolatSeeder.php b/manajemen_mp/database/seeders/KolatSeeder.php index 201f043..f970bf8 100644 --- a/manajemen_mp/database/seeders/KolatSeeder.php +++ b/manajemen_mp/database/seeders/KolatSeeder.php @@ -14,7 +14,7 @@ class KolatSeeder extends Seeder public function run(): void { DB::table('kolat')->insert([ - 'id' => 2, // Kita paksa ID 1 biar nyambung sama UserSeeder tadi + 'id' => 1, // Kita paksa ID 1 biar nyambung sama UserSeeder tadi 'nama_kolat' => 'Kolat Unej', 'created_at' => now(), 'updated_at' => now(), diff --git a/manajemen_mp/resources/views/layout/app.blade.php b/manajemen_mp/resources/views/layout/app.blade.php index d1344ac..d4c6b3d 100644 --- a/manajemen_mp/resources/views/layout/app.blade.php +++ b/manajemen_mp/resources/views/layout/app.blade.php @@ -100,6 +100,22 @@ @endif + + diff --git a/manajemen_mp/resources/views/login-register/register.blade.php b/manajemen_mp/resources/views/login-register/register.blade.php index 7b3ccbf..9683eb9 100644 --- a/manajemen_mp/resources/views/login-register/register.blade.php +++ b/manajemen_mp/resources/views/login-register/register.blade.php @@ -6,7 +6,7 @@ Daftar Anggota - TapakMP - + diff --git a/manajemen_mp/resources/views/spp/pengurus/create.blade.php b/manajemen_mp/resources/views/spp/pengurus/create.blade.php index e5839bd..3281f40 100644 --- a/manajemen_mp/resources/views/spp/pengurus/create.blade.php +++ b/manajemen_mp/resources/views/spp/pengurus/create.blade.php @@ -13,11 +13,11 @@ - @if(session('error')) + {{-- @if(session('error'))
{{ session('error') }}
- @endif + @endif --}}
@@ -106,4 +106,34 @@ border: 1px solid #0d6efd !important; } + + + + @endsection diff --git a/manajemen_mp/resources/views/spp/pengurus/index.blade.php b/manajemen_mp/resources/views/spp/pengurus/index.blade.php index 37c3b09..3358fe9 100644 --- a/manajemen_mp/resources/views/spp/pengurus/index.blade.php +++ b/manajemen_mp/resources/views/spp/pengurus/index.blade.php @@ -1,299 +1,422 @@ @extends('layout.app') @section('content') -@php - $userRole = auth()->user()->role->nama_role ?? 'Anggota'; -@endphp + @php + $userRole = auth()->user()->role->nama_role ?? 'Anggota'; + @endphp -
- -
-
-

Manajemen SPP

-

Kelola tagihan dan pembayaran SPP anggota Merpati Putih.

+
+ +
+
+

Manajemen SPP

+

Kelola tagihan dan pembayaran SPP anggota Merpati Putih.

+
+
+ {{-- HANYA PENGURUS YANG BISA GENERATE MASAL --}} + @if ($userRole == 'Pengurus') +
+ @csrf + +
+ @endif + + + Excel + + + + PDF + + + + Tambah Manual + +
-
- {{-- HANYA PENGURUS YANG BISA GENERATE MASAL --}} - @if($userRole == 'Pengurus') -
- @csrf - -
- @endif - - Excel - + + @if (session('success')) +
+ {{ session('success') }} +
+ @endif - - PDF - - - - Tambah Manual - -
-
- - - @if(session('success')) -
- {{ session('success') }} -
- @endif - - -
-
-
-
-
-

SPP LUNAS

-

{{ $stats['lunas'] }}

+ +
+
+
+
+
+

SPP LUNAS

+

{{ $stats['lunas'] }}

+
+ +
+
+
+
+
+
+
+

PENDING

+

{{ $stats['pending'] }}

+
+ +
+
+
+
+
+
+
+

BELUM BAYAR

+

{{ $stats['belum_bayar'] }}

+
+ +
+
+
+
+
+
+
+

TOTAL MASUK

+

Rp {{ number_format($stats['total_pemasukan'], 0, ',', '.') }}

+
+
-
-
-
-
-
-

PENDING

-

{{ $stats['pending'] }}

+ + +
+
+
Filter Data SPP
+
+
+
+
+ +
- -
+
+ + +
+
+ + +
+
+ + +
+
+ + + Reset + +
+
-
-
-
-
-

BELUM BAYAR

-

{{ $stats['belum_bayar'] }}

-
- -
+ + +
+
+
Daftar Tagihan SPP
-
-
-
-
-
-

TOTAL MASUK

-

Rp {{ number_format($stats['total_pemasukan'], 0, ',', '.') }}

+
+ @if ($data_spp->isEmpty()) +
+ +
Belum ada data tagihan SPP
+

Silakan tambah manual atau generate otomatis.

+ Tambah Manual
- -
-
-
-
- - -
-
-
Filter Data SPP
-
-
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - - Reset - -
-
-
-
- - -
-
-
Daftar Tagihan SPP
-
-
- @if($data_spp->isEmpty()) -
- -
Belum ada data tagihan SPP
-

Silakan tambah manual atau generate otomatis.

- Tambah Manual -
- @else -
- - - - - - - - - - - - @foreach($data_spp as $spp) - - - - - - + + @endforeach + +
AnggotaPeriodeNominalStatusAksi
-
-
- {{ strtoupper(substr($spp->anggota->nama_lengkap, 0, 1)) }} -
-
-
{{ $spp->anggota->nama_lengkap }}
- - ID: {{ $spp->anggota_id }} - -
-
-
-
{{ $spp->bulan }}
-
{{ $spp->tahun }}
-
- Rp {{ number_format($spp->nominal, 0, ',', '.') }} - - @if($spp->status == 'lunas') - - Lunas - - @elseif($spp->status == 'pending') - - Menunggu Validasi - - @elseif($spp->status == 'belum_bayar' && \Carbon\Carbon::parse($spp->jatuh_tempo)->isPast()) - - Terlambat - - @else - - Belum Bayar - - @endif - - @if($spp->status == 'belum_bayar') -
- @csrf - -
- - @elseif($spp->status == 'pending') -
- - - {{-- HANYA PENGURUS YANG BISA VALIDASI STATUS PENDING --}} - @if($userRole == 'Pengurus') -
- @csrf - -
- @endif -
- - -
+
+ @endif +
-
- + + + + + @endsection