where('status', 'Dibuka') ->first(); $formPengangsuran = Form::where('jenis_form', 'pengangsuran') ->where('status', 'Dibuka') ->first(); $pengajuan = PengajuanUkt::with(['hasilPenilaian', 'dokumen', 'mahasiswa']) ->where('mahasiswa_id', Auth::id()) ->latest() ->paginate(10); $stats = $this->getPengajuanStats(Auth::id()); return view('mahasiswa.dashboard', compact( 'formPenurunan', 'formPengangsuran', 'pengajuan', 'stats' )); } public function create($jenis) { if (!in_array($jenis, ['penurunan', 'pengangsuran'])) { abort(404); } $formSetting = Form::where('jenis_form', $jenis) ->where('status', 'Dibuka') ->first(); if (!$formSetting) { return redirect()->route('pengajuan.index') ->with('error', 'Form pengajuan '.$jenis.' UKT saat ini tidak tersedia'); } // Cek apakah mahasiswa sudah melakukan pengajuan jenis apapun (penurunan/pengangsuran) di semester ini $existingSubmission = PengajuanUkt::where('mahasiswa_id', Auth::id()) ->whereHas('form', function($query) use ($formSetting) { $query->where('semester', $formSetting->semester) ->where('tahun', $formSetting->tahun); }) ->first(); // Hanya izinkan buat ulang jika status sebelumnya 'tidak valid' if ($existingSubmission && $existingSubmission->status_validasi !== 'tidak valid') { $message = match($existingSubmission->status_validasi) { 'menunggu' => 'Anda sudah mengajukan '.$existingSubmission->jenis_pengajuan.' UKT untuk semester '.$formSetting->semester.' '.$formSetting->tahun.' dan masih dalam proses verifikasi', 'valid' => 'Pengajuan '.$existingSubmission->jenis_pengajuan.' UKT Anda untuk semester '.$formSetting->semester.' '.$formSetting->tahun.' sudah divalidasi', default => 'Anda tidak dapat mengajukan form ini' }; return redirect()->route('pengajuan.index')->with('error', $message); } $kriteria = Kriteria::with('subKriteria')->get(); $mahasiswa = Auth::user()->mahasiswa; return view('form.'.$jenis, compact('jenis', 'kriteria', 'mahasiswa', 'formSetting')); } public function store(Request $request, $jenis) { if (!in_array($jenis, ['penurunan', 'pengangsuran'])) { abort(404); } $user = Auth::user(); if (!$user->mahasiswa) { return back()->with('error', 'Anda tidak terdaftar sebagai mahasiswa.'); } $mahasiswa = $user->mahasiswa; $form = Form::where('jenis_form', $jenis) ->where('status', 'Dibuka') ->firstOrFail(); $existingSubmission = PengajuanUkt::where('mahasiswa_id', $mahasiswa->user_id) ->whereHas('form', function($query) use ($form) { $query->where('semester', $form->semester) ->where('tahun', $form->tahun); }) ->whereIn('status_validasi', ['menunggu', 'valid']) ->first(); if ($existingSubmission && $existingSubmission->status_validasi !== 'tidak valid') { return back()->with('error', 'Anda sudah pernah mengajukan '.$existingSubmission->jenis_pengajuan.' UKT untuk semester '.$form->semester.' '.$form->tahun.'. Anda hanya bisa mengajukan satu form per semester.'); } $validated = $request->validate([ 'ukt_saat_ini' => 'required|numeric|min:500000', 'alasan_pengajuan' => 'required|string|min:20|max:500', 'penghasilan' => 'required|in:< 2 juta,2 - 3 juta,3 - 4 juta,> 5 juta', 'file_penghasilan' => 'required|mimes:pdf|max:1536', 'pekerjaan' => 'required|in:Bekerja Tetap,Bekerja Tidak Tetap,Tidak Bekerja', 'file_pekerjaan' => 'required|mimes:pdf|max:1536', 'tanggungan' => 'required|in:1 Orang,2 - 4 Orang,> 4 Orang', 'file_tanggungan' => 'required|mimes:pdf|max:1536', 'status_ortu' => 'required|in:Lengkap,Cerai/Yatim/Piatu,Yatim Piatu', 'file_status_ortu' => 'required|mimes:pdf|max:1536', 'hunian' => 'required|in:Milik Sendiri,Sewa/Kontrak,Menumpang', 'file_hunian' => 'required|mimes:pdf|max:1536', 'kendaraan' => 'required|in:Tidak Punya,Motor,Mobil,Motor dan Mobil', 'file_kendaraan' => ($request->kendaraan !== 'Tidak Punya' ? 'required|' : '') . 'mimes:pdf|max:1536', 'semester' => 'required|in:1,2,3,4,5,>6', 'ipk' => 'required|in:< 2.75,2.75 - 3.00,3.00 - 3.50,> 3.50', 'file_khs' => 'required|file|mimes:pdf|max:1536', 'file_ukt' => 'required|mimes:pdf|max:1536', 'file_pendukung' => 'nullable|mimes:pdf|max:1536' ], [ 'ukt_saat_ini.required' => 'Nominal UKT saat ini wajib diisi', 'file_ukt' => 'required|mimes:pdf|max:1536', 'required' => 'Field :attribute wajib diisi', 'file_khs.required' => 'File bukti khs wajib diupload', 'in' => 'Pilihan :attribute tidak valid', 'mimes' => 'File harus berupa PDF', 'max' => 'Ukuran file maksimal 1.5MB', 'alasan_pengajuan.required' => 'Harap jelaskan alasan pengajuan Anda', ]); DB::beginTransaction(); try { $cleanUktValue = (int) str_replace('.', '', $request->ukt_saat_ini); // Store UKT file $fileUktPath = $request->file('file_ukt')->store('pengajuan/dokumen', 'public'); // Store KHS file (will be used for both IPK and Semester) $fileKhsPath = $request->file('file_khs')->store('pengajuan/dokumen', 'public'); $pengajuan = PengajuanUkt::create([ 'mahasiswa_id' => $mahasiswa->user_id, 'jenis_pengajuan' => $jenis, 'status_validasi' => 'menunggu', 'status_form_id' => $form->id, 'alasan_penolakan' => '', 'ukt_saat_ini' => $cleanUktValue, 'alasan_pengajuan' => $request->input('alasan_pengajuan', ''), 'file_ukt' => $fileUktPath ]); PengajuanDetail::create([ 'pengajuan_ukt_id' => $pengajuan->id, 'kriteria' => 'Slip Pembayaran UKT', 'kriteria_id' => null, 'sub_kriteria_id' => null, 'subkriteria_text' => 'Nominal: Rp '.number_format($cleanUktValue, 0, ',', '.'), 'file_dokumen' => $fileUktPath, 'verified' => false ]); $kriteriaMap = [ 'penghasilan' => 'Penghasilan Orang Tua', 'pekerjaan' => 'Pekerjaan Orang Tua', 'tanggungan' => 'Tanggungan Orang Tua', 'status_ortu' => 'Status Orang Tua', 'hunian' => 'Hunian', 'kendaraan' => 'Kendaraan', 'semester' => 'Semester', 'ipk' => 'IPK' ]; $allKriteria = Kriteria::whereIn('nama_kriteria', array_values($kriteriaMap))->get(); if ($allKriteria->count() !== count($kriteriaMap)) { throw new \Exception("Beberapa kriteria tidak ditemukan dalam database"); } $allSubKriteria = SubKriteria::whereIn('kriteria_id', $allKriteria->pluck('id'))->get()->groupBy('kriteria_id'); $detailsToInsert = []; // Process all criteria except IPK and Semester foreach ($kriteriaMap as $inputName => $kriteriaName) { if (in_array($inputName, ['semester', 'ipk'])) continue; $kriteria = $allKriteria->firstWhere('nama_kriteria', $kriteriaName); $subkriteriaValue = $request->$inputName; $subKriteria = isset($allSubKriteria[$kriteria->id]) ? $allSubKriteria[$kriteria->id]->firstWhere('nama_subkriteria', $subkriteriaValue) : null; // Handle file upload $fileInputName = 'file_' . $inputName; $filePath = null; if ($request->hasFile($fileInputName)) { $filePath = $request->file($fileInputName)->store('pengajuan/dokumen', 'public'); } // Special handling for Kendaraan if ($kriteriaName === 'Kendaraan' && $subkriteriaValue === 'Tidak Punya') { $filePath = null; } $detailsToInsert[] = [ 'pengajuan_ukt_id' => $pengajuan->id, 'kriteria' => $kriteriaName, 'kriteria_id' => $kriteria->id, 'sub_kriteria_id' => $subKriteria ? $subKriteria->id : null, 'subkriteria_text' => $subkriteriaValue, 'file_dokumen' => $filePath, 'verified' => false, 'created_at' => now(), 'updated_at' => now() ]; } // Add Semester criteria with KHS file $semesterKriteria = $allKriteria->firstWhere('nama_kriteria', 'Semester'); $semesterSubKriteria = $allSubKriteria[$semesterKriteria->id] ->firstWhere('nama_subkriteria', $request->semester); $detailsToInsert[] = [ 'pengajuan_ukt_id' => $pengajuan->id, 'kriteria' => 'Semester', 'kriteria_id' => $semesterKriteria->id, 'sub_kriteria_id' => $semesterSubKriteria->id, 'subkriteria_text' => $request->semester, 'file_dokumen' => $fileKhsPath, 'verified' => false, 'created_at' => now(), 'updated_at' => now() ]; // Add IPK criteria with same KHS file $ipkKriteria = $allKriteria->firstWhere('nama_kriteria', 'IPK'); $ipkSubKriteria = $allSubKriteria[$ipkKriteria->id] ->firstWhere('nama_subkriteria', $request->ipk); $detailsToInsert[] = [ 'pengajuan_ukt_id' => $pengajuan->id, 'kriteria' => 'IPK', 'kriteria_id' => $ipkKriteria->id, 'sub_kriteria_id' => $ipkSubKriteria->id, 'subkriteria_text' => $request->ipk, 'file_dokumen' => $fileKhsPath, 'verified' => false, 'created_at' => now(), 'updated_at' => now() ]; // Insert all details at once PengajuanDetail::insert($detailsToInsert); // Handle optional supporting document if ($request->hasFile('file_pendukung')) { $filePendukungPath = $request->file('file_pendukung')->store('pengajuan/dokumen', 'public'); $pengajuan->update(['file_pendukung' => $filePendukungPath]); } DB::commit(); return response()->json([ 'success' => true, 'message' => 'Pengajuan berhasil dikirim', 'redirect' => route('pengajuan.index') ]); } catch (\Exception $e) { DB::rollBack(); Log::error('Pengajuan error: '.$e->getMessage()); return response()->json([ 'success' => false, 'message' => 'Gagal mengirim pengajuan: '.$e->getMessage(), 'errors' => $e->getMessage() ], 500); } } private function getDefaultBobot($kriteriaName) { $totalKriteria = 8; return round(1 / $totalKriteria, 3); } public function show(PengajuanUkt $pengajuan) { if ($pengajuan->mahasiswa_id != auth()->id()) { abort(403); } return redirect()->route('dashboard.mahasiswa')->with('success', 'Pengajuan berhasil diproses'); } public function viewDocument($pengajuanId, $docType) { if (!in_array(auth()->user()->role, ['admin', 'karyawan'])) { abort(403, 'Akses ditolak!'); } $pengajuan = PengajuanUkt::findOrFail($pengajuanId); $docType = str_replace('-', ' ', $docType); $detail = $pengajuan->details() ->where('kriteria', $docType) ->firstOrFail(); if (!$detail->file_dokumen) { abort(404, 'File tidak ditemukan'); } $fullPath = storage_path('app/public/'.$detail->file_dokumen); if (!file_exists($fullPath)) { abort(404, 'File tidak ditemukan di storage'); } return response()->file($fullPath); } private function cleanFileName($name) { return preg_replace('/[^A-Za-z0-9\-]/', '', str_replace(' ', '_', $name)); } private function getPengajuanStats($mahasiswaId) { return [ 'total' => PengajuanUkt::where('mahasiswa_id', $mahasiswaId)->count(), 'diterima' => PengajuanUkt::where('mahasiswa_id', $mahasiswaId) ->where('status_validasi', 'valid')->count(), 'ditolak' => PengajuanUkt::where('mahasiswa_id', $mahasiswaId) ->where('status_validasi', 'tidak valid')->count(), 'menunggu' => PengajuanUkt::where('mahasiswa_id', $mahasiswaId) ->where('status_validasi', 'menunggu')->count() ]; } }