whereIn('status', ['Dipinjam', 'Terlambat']) ->get(); $groupedLoans = $loans->groupBy('user_id'); $peminjamanAktif = $groupedLoans->map(function ($userLoans, $userId) { $user = $userLoans->first()->user; $firstLoan = $userLoans->first(); return [ 'id_peminjaman' => 'PIN-ADM-'.sprintf('%03d', $userId), 'user_id' => $userId, 'peminjam' => $user->nama_lengkap ?? 'Unknown', 'email' => $user->email, 'nomor_hp' => $user->phone ?? '-', 'tanggal_pinjam' => $firstLoan->borrowed_at, 'tenggat_kembali' => $firstLoan->due_at, 'status' => $firstLoan->status, 'role' => $user->role, 'books' => $userLoans->map(fn ($l) => [ 'id' => $l->book->id, 'judul' => $l->book->judul, 'cover' => $l->book->cover, ])->toArray(), ]; })->values(); $daftarPeminjam = $peminjamanAktif->pluck('peminjam')->unique(); return view('admin.peminjaman.index', [ 'pageTitle' => 'Manajemen Peminjaman', 'peminjamanAktif' => $peminjamanAktif, 'daftarPeminjam' => $daftarPeminjam, ]); } public function create() { $users = User::whereIn('role', ['siswa', 'guru'])->get(); $groupedUsers = $users->map(function ($user) { $jumlahPinjam = Loan::where('user_id', $user->id) ->whereIn('status', ['Dipinjam', 'Terlambat']) ->count(); $user->jumlah_pinjam = $jumlahPinjam; $user->kena_limit = $jumlahPinjam >= 2; $user->disabled = $user->kena_limit || $user->is_banned; if ($user->is_banned) { $user->status_text = '(Akun Dibekukan)'; } elseif ($user->kena_limit) { $user->status_text = '(Limit Penuh: 2/2)'; } else { $user->status_text = ''; } return $user; })->groupBy('role'); $daftarBuku = Book::where('status', 'Tersedia') ->whereJsonContains('tipe_akses', 'offline') ->where('stok', '>', 0) ->get(); return view('admin.peminjaman.create', [ 'pageTitle' => 'Buat Peminjaman Manual', 'groupedUsers' => $groupedUsers, 'daftarBuku' => $daftarBuku, ]); } public function store(Request $request) { $validated = $request->validate([ 'peminjam_id' => 'required|exists:users,id', 'tanggal_pinjam' => 'required|date', 'tanggal_kembali' => 'required|date|after:tanggal_pinjam', 'buku_ids' => 'required|array|min:1|max:2', 'buku_ids.*' => 'exists:books,id', ]); \DB::beginTransaction(); try { foreach ($validated['buku_ids'] as $bookId) { $book = Book::findOrFail($bookId); // Check if book is available if ($book->status !== 'Tersedia') { throw new \Exception("Buku '{$book->judul}' tidak tersedia."); } // Check stock if ($book->stok <= 0) { throw new \Exception("Buku '{$book->judul}' stok habis."); } // Create loan record Loan::create([ 'user_id' => $validated['peminjam_id'], 'book_id' => $bookId, 'loan_code' => 'LOAN-'.date('Ymd').'-'.strtoupper(substr(md5(uniqid()), 0, 6)), 'borrowed_at' => $validated['tanggal_pinjam'], 'due_at' => $validated['tanggal_kembali'], 'status' => 'Dipinjam', ]); // Update book status and decrement stock $book->update([ 'status' => 'Dipinjam', 'stok' => $book->stok - 1, ]); } \DB::commit(); return redirect()->route('admin.peminjaman.index')->with('success', 'Peminjaman berhasil dibuat.'); } catch (\Exception $e) { \DB::rollBack(); return back()->withErrors(['error' => $e->getMessage()])->withInput(); } } public function export(Request $request) { $request->validate([ 'bulan_laporan' => 'nullable|date', ]); $query = Loan::with(['user', 'book'])->orderBy('borrowed_at', 'asc'); if ($request->filled('bulan_laporan')) { $date = Carbon::parse($request->bulan_laporan); $query->whereMonth('borrowed_at', $date->month) ->whereYear('borrowed_at', $date->year); $fileName = 'Laporan_Peminjaman_'.$date->format('Y-m').'.xls'; // Changed to .xls } else { $fileName = 'Laporan_Peminjaman_Semua.xls'; // Changed to .xls } $loans = $query->get(); $headers = [ "Content-Type" => "application/vnd.ms-excel", "Content-Disposition" => "attachment; filename=$fileName", "Pragma" => "no-cache", "Cache-Control" => "must-revalidate, post-check=0, pre-check=0", "Expires" => "0" ]; $columns = ['NO', 'ID PEMINJAMAN', 'PEMINJAM', 'ROLE', 'JUDUL BUKU', 'TGL PINJAM', 'TENGGAT KEMBALI', 'STATUS', 'DENDA KETERLAMBATAN']; $callback = function() use($loans, $columns) { $file = fopen('php://output', 'w'); // Generate HTML table for Excel echo ''; echo ''; foreach($columns as $col) echo ""; echo ''; echo ''; $i = 1; foreach ($loans as $loan) { echo ''; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ''; } echo '
$col
" . $i++ . "" . ($loan->loan_code ?? '-') . "" . ($loan->user->nama_lengkap ?? 'Unknown') . "" . ($loan->user->role ?? '-') . "" . ($loan->book->judul ?? 'Unknown') . "" . $loan->borrowed_at->format('d/m/Y') . "" . ($loan->due_at ? $loan->due_at->format('d/m/Y') : '-') . "" . ($loan->status ?? '-') . "" . ($loan->fine_overdue ?? 0) . "
'; fclose($file); }; return response()->stream($callback, 200, $headers); } public function dendaIndex() { $now = Carbon::now(); // Fetch all loans that are overdue or users that are banned $loans = Loan::with(['user', 'book']) ->where(function ($query) use ($now) { $query->where('due_at', '<', $now) ->whereIn('status', ['Dipinjam', 'Terlambat']); }) ->orWhereHas('user', function ($query) { $query->where('is_banned', true); }) ->get(); $groupedLoans = $loans->groupBy('user_id'); $siswaTelat = $groupedLoans->map(function ($userLoans, $userId) use ($now) { $user = $userLoans->first()->user; $firstLoan = $userLoans->first(); $tenggat = Carbon::parse($firstLoan->due_at); $hariTelat = $now->greaterThan($tenggat) ? (int) $tenggat->diffInDays($now) : 0; $isGuru = $user->role === 'guru'; $totalDenda = $isGuru ? 0 : ($hariTelat * 1000); // Link WA $hp = $user->phone ?? ''; $waLink = '#'; if ($hp) { if (substr($hp, 0, 1) == '0') { $hp = '62'.substr($hp, 1); } if ($isGuru && $hariTelat > 0) { $pesan = "Halo Bapak/Ibu {$user->nama_lengkap}, anda terlambat pengembalian buku selama {$hariTelat} hari. Mohon segera dikembalikan ke perpustakaan. Terima kasih."; } else { $pesan = $hariTelat > 0 ? "Halo {$user->nama_lengkap}, anda terlambat pengembalian buku. Total Denda: Rp ".number_format($totalDenda, 0, ',', '.') : "Halo {$user->nama_lengkap}, akun anda sedang dinonaktifkan sementara. Mohon hubungi petugas."; } $waLink = "https://wa.me/{$hp}?text=".urlencode($pesan); } return [ 'id' => $firstLoan->id, 'user_id' => $userId, 'peminjam' => $user->nama_lengkap, 'nomor_hp' => $user->phone ?? '-', 'kelas' => $user->kelas ?? 'Guru', 'hari_terlambat' => $hariTelat, 'total_denda' => $totalDenda, 'is_guru' => $isGuru, 'wa_link' => $waLink, 'is_banned' => $user->is_banned, 'tenggat_kembali' => $firstLoan->due_at, 'books' => $userLoans->map(fn ($l) => [ 'id' => $l->book->id, 'judul' => $l->book->judul, ])->toArray(), ]; })->values(); $listKelas = $siswaTelat->pluck('kelas')->unique()->values(); return view('admin.denda.index', [ 'pageTitle' => 'Manajemen Denda & Sanksi', 'siswaTelat' => $siswaTelat, 'listKelas' => $listKelas, ]); } public function berikanSanksi(Request $request) { $validated = $request->validate([ 'user_id' => 'required|exists:users,id', 'action' => 'required|in:ban,unban', ]); $user = User::findOrFail($validated['user_id']); $user->is_banned = ($validated['action'] === 'ban'); $user->save(); return response()->json([ 'status' => 'success', 'message' => $user->is_banned ? "Akun {$user->nama_lengkap} berhasil dibekukan." : "Akun {$user->nama_lengkap} telah diaktifkan kembali.", ]); } public function kembalikan(Request $request) { $validated = $request->validate([ 'user_id' => 'required|exists:users,id', 'returns' => 'required|array', 'returns.*.book_id' => 'required|exists:books,id', 'returns.*.condition' => 'required|string', 'returns.*.fine_damage' => 'required|integer', 'returns.*.fine_overdue' => 'required|integer', 'returns.*.notes' => 'nullable|string', 'send_email' => 'nullable|boolean', ]); \DB::beginTransaction(); try { foreach ($validated['returns'] as $item) { $loan = Loan::where('user_id', $validated['user_id']) ->where('book_id', $item['book_id']) ->whereIn('status', ['Dipinjam', 'Terlambat']) ->first(); if ($loan) { $loan->update([ 'status' => 'Dikembalikan', 'returned_at' => now(), 'condition' => $item['condition'], 'fine_damage' => $item['fine_damage'], 'fine_overdue' => $item['fine_overdue'], 'return_notes' => $item['notes'], ]); // Update book status and increment stock $loan->book->update([ 'status' => 'Tersedia', 'stok' => $loan->book->stok + 1, ]); } } \DB::commit(); // Send email if requested if ($request->send_email && $request->user_id) { $user = User::findOrFail($request->user_id); $totalDenda = 0; $emailReturns = []; foreach ($validated['returns'] as $item) { $book = Book::find($item['book_id']); $emailReturns[] = [ 'judul' => $book->judul ?? 'Buku Unknown', 'condition' => $item['condition'], 'fine_damage' => $item['fine_damage'], 'fine_overdue' => $item['fine_overdue'], ]; $totalDenda += ($item['fine_damage'] + $item['fine_overdue']); } try { Mail::to($user->email)->send(new ReturnReceipt($user, $emailReturns, $totalDenda)); } catch (\Exception $e) { // Log the error but don't fail the return process \Log::error('Gagal mengirim email pengembalian: ' . $e->getMessage()); return response()->json([ 'status' => 'success', 'message' => 'Buku berhasil dikembalikan, namun gagal mengirim email.', 'email_error' => $e->getMessage() ]); } } return response()->json(['status' => 'success', 'message' => 'Buku berhasil dikembalikan.']); } catch (\Exception $e) { \DB::rollBack(); return response()->json(['status' => 'error', 'message' => $e->getMessage()], 500); } } }