MIF_E31230887/app/Http/Controllers/AdminPeminjamanController.php

366 lines
16 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\Peminjaman;
use App\Models\Buku;
use App\Models\Anggota;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class AdminPeminjamanController extends Controller
{
public function index(Request $request)
{
$search = $request->input('search');
$query = Peminjaman::with(['buku', 'anggota', 'user'])->latest();
if ($search) {
$searchTerm = strtolower($search);
$query->where(function ($q) use ($searchTerm) {
$q->whereHas('anggota', function ($qMember) use ($searchTerm) {
$qMember->whereRaw('LOWER(nama) LIKE ?', ["%{$searchTerm}%"]);
})->orWhereHas('buku', function ($qBuku) use ($searchTerm) {
$qBuku->whereRaw('LOWER(judul) LIKE ?', ["%{$searchTerm}%"]);
})->orWhereHas('user', function ($qUser) use ($searchTerm) {
$qUser->whereRaw('LOWER(name) LIKE ?', ["%{$searchTerm}%"]);
});
});
}
$peminjaman = $query->paginate(15)->appends(['search' => $search]);
$buku = Buku::orderBy('judul', 'asc')->get();
$anggota = Anggota::orderBy('nama', 'asc')->get();
return view('admin.peminjaman.index', compact('peminjaman', 'buku', 'anggota', 'search'));
}
public function create()
{
$buku = Buku::where('eksemplar', '>', 0)->orderBy('judul', 'asc')->get();
$anggota = Anggota::orderBy('nama', 'asc')->get();
return view('admin.peminjaman.create', compact('buku', 'anggota'));
}
public function edit($id)
{
$peminjaman = Peminjaman::findOrFail($id);
$buku = Buku::all();
$anggota = Anggota::orderBy('nama', 'asc')->get();
return view('admin.peminjaman.edit', compact('peminjaman', 'buku', 'anggota'));
}
public function store(Request $request)
{
// 1. Validasi Input
$validated = $request->validate([
'id_anggota' => 'required|exists:anggotas,id',
'id_buku' => 'required|exists:buku,id_buku',
'tanggal_pinjam' => 'required|date_format:Y-m-d|after_or_equal:2000-01-01|before_or_equal:2100-12-31',
'tanggal_kembali' => 'required|date_format:Y-m-d|after_or_equal:tanggal_pinjam|before_or_equal:2100-12-31',
]);
$validated['status_peminjaman'] = 'Dipinjam';
// 2. Kurangi Stok Buku
$buku = Buku::findOrFail($validated['id_buku']);
$buku->decrement('eksemplar');
// 3. Simpan ke Database
$peminjaman = Peminjaman::create($validated);
$peminjaman->load(['buku', 'anggota']);
$waSuccess = false;
// 4. Proses Kirim WA (Format Struk Teks Resmi)
try {
$targetNum = $peminjaman->anggota->no_hp ?? '';
$fonnteToken = 'vpzqxF2ZGgTGz9F5UbUS'; // Token Fonnte
// Standarisasi Format Nomor ke awalan 62
if (!empty($targetNum)) {
$targetNum = preg_replace('/^0/', '62', trim($targetNum));
}
if (!empty($targetNum) && !empty($fonnteToken)) {
// Merangkai Teks Menyerupai Struk Kertas
$pesanStruk = "🏢 *PERPUSTAKAAN DAERAH JEMBER*\n";
$pesanStruk .= "Jl. Mastrip No. 1, Kabupaten Jember\n";
$pesanStruk .= "===============================\n\n";
$pesanStruk .= "📄 *BUKTI PEMINJAMAN BUKU*\n";
$pesanStruk .= "No. Transaksi : PMJ-{$peminjaman->id_peminjaman}\n";
$pesanStruk .= "Tanggal Cetak : " . \Carbon\Carbon::now()->format('d-m-Y H:i') . "\n\n";
$pesanStruk .= "*DATA PEMINJAM*\n";
$pesanStruk .= "Nama : {$peminjaman->anggota->nama}\n";
$pesanStruk .= "No. HP : {$peminjaman->anggota->no_hp}\n\n";
$pesanStruk .= "*DETAIL BUKU*\n";
$pesanStruk .= "Judul : {$peminjaman->buku->judul}\n";
$pesanStruk .= "Kode Pengembalian : {$peminjaman->buku->bibid}\n";
$pesanStruk .= "Pinjam : " . \Carbon\Carbon::parse($peminjaman->tanggal_pinjam)->format('d F Y') . "\n";
$pesanStruk .= "Kembali: *" . \Carbon\Carbon::parse($peminjaman->tanggal_kembali)->format('d F Y') . "*\n\n";
$pesanStruk .= "===============================\n";
$pesanStruk .= "⚠️ *Catatan:*\n";
$pesanStruk .= "Tunjukkan Kode Pengembalian ke Admin saat pengembalian buku.\n";
$pesanStruk .= "Harap kembalikan buku tepat waktu.\n";
$pesanStruk .= "Denda keterlambatan: Rp 1.000/hari.\n\n";
$pesanStruk .= "Terima kasih atas kunjungan Anda!\n";
$pesanStruk .= "_Sistem Sarakata - TA 2026_";
// Eksekusi Pengiriman via Http Laravel
$response = \Illuminate\Support\Facades\Http::withoutVerifying()->timeout(15)->withHeaders([
'Authorization' => $fonnteToken,
])->post('https://api.fonnte.com/send', [
'target' => $targetNum,
'message' => $pesanStruk,
]);
if ($response->successful() && ($response->json('status') == true)) {
$waSuccess = true;
} else {
\Illuminate\Support\Facades\Log::warning("Fonnte Log: Gagal Terkirim", [
'target' => $targetNum,
'http_status' => $response->status(),
'body' => $response->body()
]);
}
} else {
\Illuminate\Support\Facades\Log::warning('Fonnte Log: Token / No HP kosong', [
'target' => $targetNum ?? 'kosong',
'token_set' => !empty($fonnteToken),
]);
}
} catch (\Exception $e) {
\Illuminate\Support\Facades\Log::error("Error WA Pengiriman: " . $e->getMessage());
}
// 5. Notifikasi Kembali ke Layar Admin
$msg = 'Transaksi peminjaman berhasil dicatat.';
if ($waSuccess) {
$msg .= ' Struk WA berhasil terkirim kepada Anggota.';
}
return redirect()->route('admin.peminjaman.index')->with('success', $msg);
}
public function cetakStruk($id)
{
$peminjaman = Peminjaman::with(['buku', 'anggota', 'user'])->findOrFail($id);
return view('admin.peminjaman.struk', compact('peminjaman'));
}
public function scan()
{
return view('admin.peminjaman.scan');
}
public function prosesScan(Request $request)
{
$request->validate(['bibid' => 'required|string']);
$buku = Buku::where('bibid', $request->bibid)->first();
if (!$buku) {
return back()->with('error', 'Aset buku tidak ditemukan di dalam database.');
}
$peminjaman = Peminjaman::with(['user', 'anggota'])
->where('id_buku', $buku->id_buku)
->where('status_peminjaman', 'Dipinjam')
->first();
if (!$peminjaman) {
return back()->with('error', 'Buku ini tidak dalam status dipinjam oleh siapapun.');
}
$tanggal_kembali_seharusnya = \Carbon\Carbon::parse($peminjaman->tanggal_kembali);
$tanggal_dikembalikan_aktual = \Carbon\Carbon::now();
$denda = 0;
if ($tanggal_dikembalikan_aktual->gt($tanggal_kembali_seharusnya)) {
$selisih_hari = $tanggal_dikembalikan_aktual->diffInDays($tanggal_kembali_seharusnya);
$denda = $selisih_hari * 1000;
}
$lokasi = $this->prediksiLokasiRakUntukAdmin($buku->nomor_panggil);
return view('admin.peminjaman.scan', compact('buku', 'peminjaman', 'denda', 'lokasi'));
}
private function prediksiLokasiRakUntukAdmin($nomor_panggil)
{
if (empty($nomor_panggil))
return ['rak' => 'Tidak Diketahui', 'area' => '-'];
$kode_utama = (int) substr(trim($nomor_panggil), 0, 3);
return match (true) {
$kode_utama >= 0 && $kode_utama <= 99 => match (true) {
$kode_utama <= 19 => ['rak' => 'Rak 01', 'area' => 'Karya Umum'],
$kode_utama <= 50 => ['rak' => 'Rak 02', 'area' => 'Karya Umum'],
default => ['rak' => 'Rak 03-05', 'area' => 'Karya Umum Lainnya'],
},
$kode_utama >= 100 && $kode_utama <= 199 => match (true) {
$kode_utama <= 150 => ['rak' => 'Rak 06-10', 'area' => 'Filsafat'],
default => ['rak' => 'Rak 11-14', 'area' => 'Psikologi'],
},
$kode_utama >= 200 && $kode_utama <= 299 => match (true) {
$kode_utama == 297 => ['rak' => 'Rak 25-32', 'area' => 'Agama Islam'],
default => ['rak' => 'Rak 15-24', 'area' => 'Agama Umum'],
},
$kode_utama >= 300 && $kode_utama <= 399 => match (true) {
$kode_utama <= 330 => ['rak' => 'Rak 33-36', 'area' => 'Sosiologi & Politik'],
$kode_utama <= 360 => ['rak' => 'Rak 37-40', 'area' => 'Ekonomi & Hukum'],
default => ['rak' => 'Rak 41-44', 'area' => 'Pendidikan & Adat'],
},
$kode_utama >= 400 && $kode_utama <= 499 => ['rak' => 'Rak 45', 'area' => 'Bahasa'],
$kode_utama >= 500 && $kode_utama <= 599 => ['rak' => 'Rak 46-48', 'area' => 'Ilmu Murni'],
$kode_utama >= 600 && $kode_utama <= 699 => match (true) {
$kode_utama <= 610 => ['rak' => 'Rak 49-53', 'area' => 'Kedokteran'],
$kode_utama <= 630 => ['rak' => 'Rak 54-58', 'area' => 'Teknik'],
$kode_utama <= 650 => ['rak' => 'Rak 59-63', 'area' => 'Pertanian'],
default => ['rak' => 'Rak 64-68', 'area' => 'Manajemen Bisnis'],
},
$kode_utama >= 700 && $kode_utama <= 799 => match (true) {
$kode_utama <= 739 => ['rak' => 'Rak 71', 'area' => 'Kesenian'],
$kode_utama <= 769 => ['rak' => 'Rak 72', 'area' => 'Seni Rupa'],
$kode_utama <= 789 => ['rak' => 'Rak 73', 'area' => 'Fotografi/Musik'],
default => ['rak' => 'Rak 74', 'area' => 'Olahraga'],
},
$kode_utama >= 800 && $kode_utama <= 899 => ['rak' => 'Rak 77-79', 'area' => 'Sastra'],
$kode_utama >= 900 && $kode_utama <= 999 => match (true) {
$kode_utama <= 919 => ['rak' => 'Rak 69, 70', 'area' => 'Geografi'],
default => ['rak' => 'Rak 80-84', 'area' => 'Sejarah Umum'],
},
default => ['rak' => 'Rak 75-76', 'area' => 'Koleksi Terbaru'],
};
}
public function update(Request $request, $id)
{
$validated = $request->validate([
'id_anggota' => 'required|exists:anggotas,id',
'id_buku' => 'required|exists:buku,id_buku',
'tanggal_pinjam' => 'required|date_format:Y-m-d|after_or_equal:2000-01-01|before_or_equal:2100-12-31',
'tanggal_kembali' => 'required|date_format:Y-m-d|after_or_equal:tanggal_pinjam|before_or_equal:2100-12-31',
]);
$peminjaman = Peminjaman::findOrFail($id);
// If the book changed, adjust stock
if ($peminjaman->id_buku != $validated['id_buku']) {
if ($peminjaman->status_peminjaman == 'Dipinjam') {
$oldBuku = Buku::find($peminjaman->id_buku);
if ($oldBuku) $oldBuku->increment('eksemplar');
$newBuku = Buku::findOrFail($validated['id_buku']);
$newBuku->decrement('eksemplar');
}
}
$peminjaman->update($validated);
return redirect()->route('admin.peminjaman.index')->with('success', 'Data peminjaman berhasil diperbarui.');
}
public function destroy($id)
{
$peminjaman = Peminjaman::findOrFail($id);
if ($peminjaman->status_peminjaman == 'Dipinjam' && $peminjaman->buku) {
$peminjaman->buku->increment('eksemplar');
}
$peminjaman->delete();
return redirect()->route('admin.peminjaman.index')->with('success', 'Data peminjaman berhasil dihapus.');
}
public function kembalikan($id)
{
$peminjaman = Peminjaman::findOrFail($id);
$tglTenggat = \Carbon\Carbon::parse($peminjaman->tanggal_kembali)->startOfDay();
$tglSekarang = \Carbon\Carbon::now()->startOfDay();
$denda = 0;
if ($tglSekarang->gt($tglTenggat)) {
$selisihHari = $tglSekarang->diffInDays($tglTenggat);
$denda = $selisihHari * 1000;
}
$peminjaman->update([
'status_peminjaman' => 'Dikembalikan',
'tanggal_dikembalikan' => now(),
'denda' => $denda
]);
if ($peminjaman->buku) {
$peminjaman->buku->increment('eksemplar');
}
$pesan = 'Buku berhasil dikembalikan.';
if ($denda > 0) {
$pesan .= ' Denda keterlambatan Rp ' . number_format($denda, 0, ',', '.');
}
return redirect()->back()->with('success', $pesan);
}
public function resendWa($id)
{
$peminjaman = Peminjaman::with(['buku', 'anggota'])->findOrFail($id);
try {
$targetNum = $peminjaman->anggota->no_hp ?? '';
$fonnteToken = env('FONNTE_TOKEN', 'vpzqxF2ZGgTGz9F5UbUS');
if (!empty($targetNum)) {
$targetNum = preg_replace('/^0/', '62', trim($targetNum));
}
if (!empty($targetNum) && !empty($fonnteToken)) {
$pesanStruk = "🏢 *PERPUSTAKAAN DAERAH JEMBER*\n";
$pesanStruk .= "Jl. Mastrip No. 1, Kabupaten Jember\n";
$pesanStruk .= "===============================\n\n";
$pesanStruk .= "📄 *SALINAN BUKTI PEMINJAMAN*\n";
$pesanStruk .= "No. Transaksi : PMJ-{$peminjaman->id_peminjaman}\n";
$pesanStruk .= "Tanggal Cetak : " . \Carbon\Carbon::now()->format('d-m-Y H:i') . "\n\n";
$pesanStruk .= "*DATA PEMINJAM*\n";
$pesanStruk .= "Nama : {$peminjaman->anggota->nama}\n";
$pesanStruk .= "No. HP : {$peminjaman->anggota->no_hp}\n\n";
$pesanStruk .= "*DETAIL BUKU*\n";
$pesanStruk .= "Judul : {$peminjaman->buku->judul}\n";
$pesanStruk .= "Kode Panggil : {$peminjaman->buku->bibid}\n";
$pesanStruk .= "Pinjam : " . \Carbon\Carbon::parse($peminjaman->tanggal_pinjam)->format('d F Y') . "\n";
$pesanStruk .= "Kembali: *" . \Carbon\Carbon::parse($peminjaman->tanggal_kembali)->format('d F Y') . "*\n\n";
$pesanStruk .= "===============================\n";
$pesanStruk .= "⚠️ *Catatan:*\n";
$pesanStruk .= "Tunjukkan Kode Panggil ke Admin saat pengembalian buku.\n";
$pesanStruk .= "Harap kembalikan buku tepat waktu.\n";
$pesanStruk .= "Denda keterlambatan: Rp 1.000/hari.\n\n";
$pesanStruk .= "Terima kasih atas kunjungan Anda!\n";
$pesanStruk .= "_Sistem Sarakata - TA 2026_";
$response = \Illuminate\Support\Facades\Http::withoutVerifying()->timeout(15)->withHeaders([
'Authorization' => $fonnteToken,
])->post('https://api.fonnte.com/send', [
'target' => $targetNum,
'message' => $pesanStruk,
]);
if ($response->successful() && ($response->json('status') == true)) {
return back()->with('success', 'Struk WhatsApp berhasil dikirim ulang ke nomor ' . $targetNum);
} else {
\Illuminate\Support\Facades\Log::warning("Fonnte Log: Gagal Terkirim Ulang", ['body' => $response->body()]);
return back()->with('error', 'Gagal mengirim ulang WA. API Fonnte menolak koneksi (Status: '.$response->status().').');
}
} else {
return back()->with('error', 'Nomor HP anggota kosong atau Token Fonnte belum dikonfigurasi.');
}
} catch (\Exception $e) {
\Illuminate\Support\Facades\Log::error("Error WA Pengiriman Ulang: " . $e->getMessage());
return back()->with('error', 'Terjadi kesalahan sistem saat menghubungi server WhatsApp.');
}
}
}