TIF_NGANJUK_E41220778/app/Http/Controllers/Admin/AdminPeminjamanController.php

376 lines
14 KiB
PHP

<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Book;
use App\Models\Loan;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Log;
use App\Mail\ReturnReceipt;
class AdminPeminjamanController extends Controller
{
public function index(Request $request)
{
$loans = Loan::with(['user', 'book'])
->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' => 'required|date|before_or_equal:today',
]);
$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 '<table border="1">';
echo '<thead><tr style="background-color: #f2f2f2;">';
foreach($columns as $col) echo "<th>$col</th>";
echo '</tr></thead>';
echo '<tbody>';
$i = 1;
foreach ($loans as $loan) {
echo '<tr>';
echo "<td>" . $i++ . "</td>";
echo "<td>" . ($loan->loan_code ?? '-') . "</td>";
echo "<td>" . ($loan->user->nama_lengkap ?? 'Unknown') . "</td>";
echo "<td>" . ($loan->user->role ?? '-') . "</td>";
echo "<td>" . ($loan->book->judul ?? 'Unknown') . "</td>";
echo "<td>" . $loan->borrowed_at->format('d/m/Y') . "</td>";
echo "<td>" . ($loan->due_at ? $loan->due_at->format('d/m/Y') : '-') . "</td>";
echo "<td>" . ($loan->status ?? '-') . "</td>";
echo "<td>" . ($loan->fine_overdue ?? 0) . "</td>";
echo '</tr>';
}
echo '</tbody></table>';
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);
}
}
}