MIF_E31222658/app/Http/Controllers/AdminPengajuanController.php

527 lines
16 KiB
PHP

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use App\Models\PengajuanUkt;
use App\Models\PengajuanDetail;
use App\Models\Kriteria;
use App\Models\Form;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\DB;
use App\Notifications\PengajuanValidNotification;
use App\Notifications\PengajuanDitolakNotification;
use Illuminate\Support\Facades\Log;
use App\Models\RankingFile;
class AdminPengajuanController extends Controller
{
// Status mapping for consistent status values
const STATUS_MAPPING = [
'diterima' => 'valid',
'menunggu' => 'menunggu',
'ditolak' => 'tidak valid'
];
const REJECTION_REASONS_BY_CRITERIA = [
'Pekerjaan Orang Tua' => [
'Surat keterangan kerja tidak valid',
'Tidak ada bukti usaha (untuk wiraswasta)',
'Dokumen kadaluarsa',
'Dokumen tidak sesuai',
'Lainnya'
],
'Penghasilan Orang Tua' => [
'Jumlah gaji tidak sesuai',
'Slip gaji tidak terbaca',
'Dokumen tidak sesuai',
'Lainnya'
],
'Status Orang Tua' => [
'Dokumen tidak sesuai',
'Dokumen tidak terbaca',
'Tidak ada bukti surat perceraian',
'Tidak ada bukti surat keterangan kematian',
'Lainnya'
],
'Tanggungan Orang Tua' => [
'Kartu Keluarga tidak terbaca',
'Jumlah tanggungan tidak sesuai',
'Dokumen tidak sesuai',
'Lainnya'
],
'Hunian' => [
'Bukti kepemilikan tidak valid',
'Dokumen tidak terbaca',
'Dokumen tidak sesuai',
'Lainnya'
],
'Kendaraan' => [
'Dokumen tidak sesuai',
'STNK tidak valid',
'STNK tidak terbaca',
'Lainnya'
],
'IPK' => [
'KHS tidak valid',
'KHS tidak terbaca',
'IPK tidak sesuai dengan sistem',
'Lainnya'
],
'Semester' => [
'KHS tidak valid',
'KHS tidak terbaca',
'Semester tidak sesuai dengan sistem',
'Lainnya'
],
'Slip Pembayaran UKT' => [
'Dokumen tidak terbaca',
'Nominal tidak sesuai',
'Slip pembayaran tidak sesuai',
'Lainnya'
]
];
/**
* Display list of UKT submissions with filtering
*/
public function index()
{
$statusFilter = $this->mapStatusFilter(request('status'));
$query = PengajuanUkt::with(['mahasiswa.user:id,name','form'])
->withCount('details');
if (request()->has('status_form_id') && request('status_form_id') != '') {
$query->where('status_form_id', request('status_form_id'));
}
// Filter by jenis form
if (request()->has('jenis_form') && request('jenis_form') != '') {
$query->whereHas('form', function($q) {
$q->where('jenis_form', request('jenis_form'));
});
}
if ($statusFilter) {
$query->where('status_validasi', $statusFilter);
}
$pengajuan = $query->latest()->paginate(10);
$forms = Form::select('id', 'nama_form')
->orderBy('created_at', 'desc')
->get();
$stats = [
'totalPengajuan' => PengajuanUkt::count(),
'valid' => PengajuanUkt::valid()->count(),
'menunggu' => PengajuanUkt::menunggu()->count(),
'tidak valid' => PengajuanUkt::tidakValid()->count()
];
return view('admin.pengajuan.index', compact('pengajuan', 'stats', 'forms'));
}
/**
* Map status filter to consistent values
*/
protected function mapStatusFilter($status)
{
return self::STATUS_MAPPING[$status] ?? $status;
}
public function detail($id)
{
$pengajuan = PengajuanUkt::with([
'details' => function($query) {
$query->select([
'id',
'pengajuan_ukt_id',
'kriteria',
'subkriteria_text',
'file_dokumen',
'verified',
'rejection_reason',
'verified_by',
'verified_at'
])->with(['verifier:id,name']);
},
'mahasiswa.user:id,name'
])->select([
'id',
'mahasiswa_id',
'jenis_pengajuan',
'alasan_pengajuan',
'ukt_saat_ini',
'status_validasi',
'created_at',
'updated_at'
])->findOrFail($id);
return view('admin.pengajuan.detail', [
'pengajuan' => $pengajuan,
'rejectionReasonsByCriteria' => self::REJECTION_REASONS_BY_CRITERIA
]);
}
public function bulkVerify(Request $request, $id)
{
if (auth()->user()->role !== 'karyawan') {
abort(403, 'Hanya karyawan yang dapat melakukan verifikasi');
}
$data = $request->all();
$rules = [
'valid' => 'required|array',
'valid.*' => 'in:0,1',
'alasan' => 'nullable|array',
'alasan_lainnya' => 'nullable|array',
];
foreach ($data['valid'] as $detailId => $value) {
if ((string) $value === '0') {
$rules["alasan.$detailId"] = 'required|string';
if (isset($data['alasan'][$detailId]) && $data['alasan'][$detailId] === 'Lainnya') {
$rules["alasan_lainnya.$detailId"] = 'required|string';
}
}
}
$validator = Validator::make($data, $rules);
if ($validator->fails()) {
return response()->json([
'success' => false,
'message' => 'Validasi gagal',
'errors' => $validator->errors()
], 422);
}
DB::beginTransaction();
try {
$pengajuan = PengajuanUkt::with(['details', 'mahasiswa.user'])->findOrFail($id);
foreach ($pengajuan->details as $detail) {
$isValid = $request->input("valid.{$detail->id}") == '1';
$alasan = $request->input("alasan.{$detail->id}");
$alasanLainnya = $request->input("alasan_lainnya.{$detail->id}");
$rejectionReason = $this->processRejectionReason($alasan, $alasanLainnya);
$detail->update([
'verified' => $isValid,
'verified_by' => auth()->id(),
'verified_at' => now(),
'rejection_reason' => $isValid ? null : $rejectionReason,
]);
}
$status = $this->updatePengajuanStatus($pengajuan);
DB::commit();
return response()->json([
'success' => true,
'message' => 'Validasi dokumen berhasil disimpan! Status: ' . ($status === 'valid' ? 'Valid' : 'Tidak Valid'),
'redirect' => route('admin.pengajuan.detail', $id)
]);
} catch (\Exception $e) {
DB::rollBack();
return response()->json([
'success' => false,
'message' => 'Gagal memvalidasi pengajuan: ' . $e->getMessage()
], 500);
}
}
public function revalidate(Request $request, $id)
{
$validator = Validator::make($request->all(), [
'valid' => 'required|array',
'valid.*' => 'in:0,1',
'alasan' => 'required_without:valid.*|array',
'alasan_lainnya' => 'required_if:alasan.*,Lainnya|array'
]);
if ($validator->fails()) {
return response()->json([
'success' => false,
'message' => 'Validasi gagal',
'errors' => $validator->errors()
], 422);
}
DB::beginTransaction();
try {
$pengajuan = PengajuanUkt::with('details')->findOrFail($id);
$changesMade = false;
foreach ($request->input('valid') as $detailId => $isValid) {
$detail = $pengajuan->details->firstWhere('id', $detailId);
if (!$detail) continue;
$alasan = $request->input("alasan.{$detailId}");
$alasanLainnya = $request->input("alasan_lainnya.{$detailId}");
$rejectionReason = $isValid ? null : $this->processRejectionReason($alasan, $alasanLainnya);
// Check if changes are needed
if ($detail->verified != $isValid || $detail->rejection_reason != $rejectionReason) {
$detail->verified = $isValid;
$detail->rejection_reason = $rejectionReason;
$detail->verified_by = auth()->id();
$detail->verified_at = now();
$detail->save();
$changesMade = true;
}
}
if ($changesMade) {
$status = $this->updatePengajuanStatus($pengajuan);
DB::commit();
return response()->json([
'success' => true,
'message' => 'Revalidasi berhasil disimpan',
'redirect' => route('admin.pengajuan.detail', $id)
]);
}
DB::commit();
return response()->json([
'success' => true,
'message' => 'Tidak ada perubahan yang dilakukan',
'redirect' => route('admin.pengajuan.detail', $id)
]);
} catch (\Exception $e) {
DB::rollBack();
return response()->json([
'success' => false,
'message' => 'Gagal menyimpan revalidasi: ' . $e->getMessage()
], 500);
}
}
public function verifyUkt(Request $request, $id)
{
$validator = Validator::make($request->all(), [
'valid_ukt' => 'required|boolean',
'alasan_ukt' => 'required_if:valid_ukt,0|nullable|string',
'alasan_lainnya_ukt' => 'required_if:alasan_ukt,Lainnya|nullable|string'
]);
if ($validator->fails()) {
return back()
->withErrors($validator)
->withInput();
}
DB::beginTransaction();
try {
$pengajuan = PengajuanUkt::with(['details', 'mahasiswa.user'])
->findOrFail($id);
$alasanUkt = $this->processRejectionReason(
$request->alasan_ukt,
$request->alasan_lainnya_ukt
);
$pengajuan->update([
'file_ukt_valid' => $request->valid_ukt,
'file_ukt_alasan' => $request->valid_ukt ? null : $alasanUkt,
'updated_at' => now()
]);
$this->updatePengajuanStatus($pengajuan);
DB::commit();
return redirect()
->route('admin.pengajuan.detail', $id)
->with('success', 'Verifikasi Slip UKT berhasil disimpan');
} catch (\Exception $e) {
DB::rollBack();
return back()
->withInput()
->with('error', 'Gagal memverifikasi Slip UKT: ' . $e->getMessage());
}
}
protected function processRejectionReason($alasan, $alasanLainnya)
{
if ($alasan === 'Lainnya') {
return 'Lainnya: ' . $alasanLainnya;
}
return $alasan;
}
protected function updatePengajuanStatus($pengajuan)
{
$invalidCount = $pengajuan->details()
->where('verified', false)
->orWhereNotNull('rejection_reason')
->count();
$status = $invalidCount === 0 ? 'valid' : 'tidak valid';
$pengajuan->update([
'status_validasi' => $status,
'updated_at' => now()
]);
$this->sendNotification($pengajuan, $status);
return $status;
}
private function sendNotification($pengajuan, $status)
{
try {
if ($status === 'valid') {
$pengajuan->mahasiswa->user->notify(new PengajuanValidNotification($pengajuan));
} else {
$reasons = $pengajuan->details()
->whereNotNull('rejection_reason')
->pluck('rejection_reason', 'kriteria')
->map(function($reason, $kriteria) {
return "$kriteria: " . (strpos($reason, 'Lainnya:') === 0 ? substr($reason, 8) : $reason);
})
->implode("\n");
$pengajuan->mahasiswa->user->notify(
new PengajuanDitolakNotification($pengajuan, $reasons)
);
}
} catch (\Exception $e) {
// Silent fail for notifications
}
}
public function destroy($id)
{
DB::beginTransaction();
try {
$pengajuan = PengajuanUkt::with(['details', 'hasilPenilaian'])->findOrFail($id);
foreach ($pengajuan->details as $detail) {
if ($detail->file_dokumen) {
$filePath = 'public/' . ltrim($detail->file_dokumen, '/');
if (Storage::exists($filePath)) {
Storage::delete($filePath);
}
}
}
$pengajuan->hasilPenilaian()->delete();
$pengajuan->details()->delete();
$pengajuan->delete();
DB::commit();
return redirect()->route('admin.pengajuan')
->with('success', 'Pengajuan berhasil dihapus');
} catch (\Exception $e) {
DB::rollBack();
return back()->with('error', 'Gagal menghapus pengajuan');
}
}
public function storeRankingReport(Request $request)
{
$request->validate([
'ranking_file' => 'required|file|mimes:pdf|max:10240'
]);
DB::beginTransaction();
try {
$existingFile = RankingFile::first();
if ($existingFile) {
Storage::delete('public/' . $existingFile->path);
$existingFile->delete();
}
$file = $request->file('ranking_file');
$path = $file->store('public/ranking_files');
RankingFile::create([
'path' => str_replace('public/', '', $path),
'uploaded_by' => auth()->id()
]);
DB::commit();
return redirect()->route('admin.pengajuan')
->with('report_status', [
'type' => 'success',
'message' => 'Laporan ranking berhasil dikirim ke mahasiswa!'
]);
} catch (\Exception $e) {
DB::rollBack();
return back()->with('report_status', [
'type' => 'danger',
'message' => 'Gagal mengunggah laporan ranking: ' . $e->getMessage()
]);
}
}
public function deleteRankingReport()
{
DB::beginTransaction();
try {
$rankingFile = RankingFile::first();
if ($rankingFile) {
Storage::delete('public/' . $rankingFile->path);
$rankingFile->delete();
$message = 'Laporan ranking berhasil dibatalkan!';
$type = 'warning';
} else {
$message = 'Tidak ada laporan ranking aktif';
$type = 'info';
}
DB::commit();
return redirect()->route('admin.pengajuan')
->with('report_status', [
'type' => $type,
'message' => $message
]);
} catch (\Exception $e) {
DB::rollBack();
return back()->with('report_status', [
'type' => 'danger',
'message' => 'Gagal membatalkan laporan ranking: ' . $e->getMessage()
]);
}
}
public function viewRankingReport($id)
{
$rankingFile = RankingFile::findOrFail($id);
$filePath = storage_path('app/public/' . $rankingFile->path);
if (!file_exists($filePath)) {
abort(404, 'File laporan tidak ditemukan');
}
return response()->download(
$filePath,
'Laporan-UKT-'.$rankingFile->created_at->format('Y-m-d').'.pdf'
);
}
}