get('status'); $user = Auth::user(); $isGlobalAdmin = $user->isGlobalAdmin(); $query = PengajuanIzin::with(['user', 'jenisIzin', 'statusPengajuan', 'suratIzin']); if (!$isGlobalAdmin) { $query->whereHas('user', function ($q) use ($user) { $q->where('id_kantor', $user->id_kantor); }); } if ($statusId) { $query->where('id_status', $statusId); } if ($request->has('search') && $request->search != '') { $query->whereHas('user', function ($q) use ($request) { $q->where('nama_lengkap', 'like', '%' . $request->search . '%'); }); } $izin = $query->orderBy('created_at', 'desc')->paginate(15); $statuses = \App\Models\StatusPengajuan::all(); $jenisIzin = \App\Models\JenisIzin::all(); $users = \App\Models\User::with(['kantor', 'divisi', 'jabatan'])->select('id', 'nama_lengkap', 'nik', 'id_kantor', 'id_divisi', 'id_jabatan')->orderBy('nama_lengkap', 'asc')->get(); $kantor = \App\Models\Kantor::all(); $divisi = \App\Models\Divisi::all(); return view('izin.index', compact('izin', 'statusId', 'statuses', 'jenisIzin', 'users', 'kantor', 'divisi')); } public function store(Request $request) { $request->validate([ 'id_user' => 'required|exists:users,id', 'id_jenis_izin' => 'required|exists:jenis_izin,id_jenis_izin', 'tanggal_mulai' => 'required|date|after_or_equal:today', 'tanggal_selesai' => 'required|date|after_or_equal:tanggal_mulai', 'alasan' => 'required|string', 'bukti_file' => 'nullable|file|mimes:jpeg,png,jpg,pdf|max:2048' ]); $jenisIzin = \App\Models\JenisIzin::find($request->id_jenis_izin); if ($jenisIzin && $jenisIzin->nama_izin == 'Cuti') { $pegawai = User::find($request->id_user); $tanggalMulai = Carbon::parse($request->tanggal_mulai); $tanggalSelesai = Carbon::parse($request->tanggal_selesai); $jumlahHari = $tanggalMulai->diffInDays($tanggalSelesai) + 1; if ($jumlahHari > 3) { return back()->withInput()->with('error', 'Pengajuan cuti maksimal 3 hari berturut-turut dalam satu pengajuan.'); } if ($pegawai && $pegawai->sisa_cuti < $jumlahHari) { return back()->withInput()->with('error', "Sisa cuti pegawai tidak mencukupi. Sisa: {$pegawai->sisa_cuti} hari, diajukan: {$jumlahHari} hari."); } $minDate = Carbon::now('Asia/Jakarta')->addDays(7)->startOfDay(); if (Carbon::parse($request->tanggal_mulai, 'Asia/Jakarta')->lt($minDate)) { return back()->withInput()->with('error', 'Pengajuan Cuti minimal H-7!'); } } if ($jenisIzin && $jenisIzin->id_jenis_izin == \App\Enums\JenisIzin::SAKIT) { $tanggalMulai = Carbon::parse($request->tanggal_mulai); $jumlahHari = $tanggalMulai->diffInDays(Carbon::parse($request->tanggal_selesai)) + 1; $sudahSakitBulanIni = PengajuanIzin::where('id_user', $request->id_user) ->where('id_jenis_izin', \App\Enums\JenisIzin::SAKIT) ->whereIn('id_status', [StatusPengajuan::PENDING, StatusPengajuan::DISETUJUI]) ->whereMonth('tanggal_mulai', $tanggalMulai->month) ->whereYear('tanggal_mulai', $tanggalMulai->year) ->exists(); if (($jumlahHari > 1 || $sudahSakitBulanIni) && !$request->hasFile('bukti_file')) { return back()->withInput()->with('error', $jumlahHari > 1 ? 'Izin sakit lebih dari 1 hari wajib melampirkan Surat Keterangan Dokter (SKD).' : 'Sudah pernah izin sakit di bulan ini. Wajib melampirkan Surat Keterangan Dokter (SKD).' ); } } $tglMulai = $request->tanggal_mulai; $tglSelesai = $request->tanggal_selesai; $userId = $request->id_user; $overlappingIzin = PengajuanIzin::where('id_user', $userId) ->whereIn('id_status', [StatusPengajuan::PENDING, StatusPengajuan::DISETUJUI]) ->where(function ($q) use ($tglMulai, $tglSelesai) { $q->whereBetween('tanggal_mulai', [$tglMulai, $tglSelesai]) ->orWhereBetween('tanggal_selesai', [$tglMulai, $tglSelesai]) ->orWhere(function ($q2) use ($tglMulai, $tglSelesai) { $q2->where('tanggal_mulai', '<=', $tglMulai) ->where('tanggal_selesai', '>=', $tglSelesai); }); })->exists(); if ($overlappingIzin) { return back()->withInput()->with('error', 'Pegawai sudah memiliki pengajuan Izin/Cuti (Pending/Disetujui) pada rentang tanggal tersebut!'); } $existingPresensi = \App\Models\Presensi::where('id_user', $userId) ->whereBetween('tanggal', [$tglMulai, $tglSelesai]) ->whereNotNull('jam_masuk') ->exists(); if ($existingPresensi) { return back()->withInput()->with('error', 'Pegawai sudah tercatat melakukan absensi kehadiran pada rentang tanggal tersebut!'); } try { DB::beginTransaction(); $path = null; if ($request->hasFile('bukti_file')) { $path = $request->file('bukti_file')->store('uploads/izin', 'public'); } $izin = PengajuanIzin::create([ 'id_user' => $request->id_user, 'id_jenis_izin' => $request->id_jenis_izin, 'tanggal_mulai' => $request->tanggal_mulai, 'tanggal_selesai' => $request->tanggal_selesai, 'alasan' => $request->alasan, 'bukti_file' => $path, 'id_status' => StatusPengajuan::PENDING ]); if ($izin->id_jenis_izin == JenisIzinEnum::CUTI) { $user = User::with(['jabatan', 'divisi'])->find($request->id_user); $ttdAktif = TandaTangan::where('id_user', $user->id)->active()->first(); $tglMulai = Carbon::parse($request->tanggal_mulai)->translatedFormat('d F Y'); $tglSelesai = Carbon::parse($request->tanggal_selesai)->translatedFormat('d F Y'); $isiSurat = "Dengan hormat,\n\n" . "Saya yang bertanda tangan di bawah ini:\n" . "Nama: {$user->nama_lengkap}\n" . "NIK: {$user->nik}\n" . "Jabatan: " . ($user->jabatan->nama_jabatan ?? '-') . "\n" . "Divisi: " . ($user->divisi->nama_divisi ?? '-') . "\n\n" . "Dengan ini mengajukan Cuti mulai tanggal {$tglMulai} sampai dengan {$tglSelesai}.\n\n" . "Alasan: {$request->alasan}\n\n" . "Demikian surat ini saya buat dengan sebenar-benarnya. " . "Atas perhatian dan persetujuannya, saya ucapkan terima kasih."; SuratIzin::create([ 'id_izin' => $izin->id_izin, 'id_user' => $user->id, 'id_ttd_pengaju' => $ttdAktif?->id_tanda_tangan, 'isi_surat' => $isiSurat, 'status_surat' => StatusSurat::MENUNGGU_MANAJER, ]); } DB::commit(); return redirect()->back()->with('success', 'Pengajuan izin berhasil dibuat.'); } catch (\Exception $e) { DB::rollBack(); return redirect()->back()->with('error', 'Gagal membuat izin: ' . $e->getMessage()); } } public function approve($id) { try { DB::beginTransaction(); $izin = PengajuanIzin::with(['jenisIzin', 'user'])->findOrFail($id); $user = Auth::user(); // Security Check if (!$user->isGlobalAdmin() && $izin->user->id_kantor != $user->id_kantor) { return redirect()->back()->with('error', 'Anda tidak diizinkan menyetujui pengajuan dari kantor lain.'); } if ($izin->id_status !== StatusPengajuan::PENDING) { return redirect()->back()->with('error', 'Pengajuan ini sudah diproses sebelumnya.'); } if ($izin->id_jenis_izin == JenisIzinEnum::CUTI) { return redirect()->back()->with('error', 'Pengajuan Cuti harus disetujui melalui menu Surat Izin (Manajer → HRD).'); } $izin->update(['id_status' => StatusPengajuan::DISETUJUI]); $statusPresensiId = StatusPresensi::IZIN; if ($izin->id_jenis_izin == JenisIzinEnum::SAKIT) { $statusPresensiId = StatusPresensi::SAKIT; } $startDate = Carbon::parse($izin->tanggal_mulai, 'Asia/Jakarta'); $endDate = Carbon::parse($izin->tanggal_selesai, 'Asia/Jakarta'); while ($startDate->lte($endDate)) { $tanggal = $startDate->format('Y-m-d'); Presensi::updateOrCreate( [ 'id_user' => $izin->id_user, 'tanggal' => $tanggal ], [ 'id_status' => $statusPresensiId, 'jam_masuk' => null, 'jam_pulang' => null, 'id_validasi' => StatusValidasi::VALID, 'alasan_telat' => $izin->jenisIzin->nama_izin . ': ' . $izin->alasan ] ); $startDate->addDay(); } DB::commit(); app(NotifikasiService::class)->kirim( $izin->id_user, 'izin_disetujui', 'Pengajuan Izin Disetujui', 'Pengajuan ' . $izin->jenisIzin->nama_izin . ' Anda telah disetujui.', ['id_izin' => $izin->id_izin] ); return redirect()->back()->with('success', 'Izin disetujui & data presensi diperbarui.'); } catch (\Exception $e) { DB::rollBack(); return redirect()->back()->with('error', 'Gagal: ' . $e->getMessage()); } } public function reject(Request $request, $id) { $izin = PengajuanIzin::with('user')->findOrFail($id); $user = Auth::user(); // Security Check if (!$user->isGlobalAdmin() && $izin->user->id_kantor != $user->id_kantor) { return redirect()->back()->with('error', 'Anda tidak diizinkan menolak pengajuan dari kantor lain.'); } if ($izin->id_status !== StatusPengajuan::PENDING) { return redirect()->back()->with('error', 'Pengajuan ini sudah diproses sebelumnya.'); } if ($izin->id_jenis_izin == JenisIzinEnum::CUTI) { return redirect()->back()->with('error', 'Pengajuan Cuti harus ditolak melalui menu Surat Izin.'); } $izin->update([ 'id_status' => StatusPengajuan::DITOLAK ]); app(NotifikasiService::class)->kirim( $izin->id_user, 'izin_ditolak', 'Pengajuan Izin Ditolak', 'Pengajuan ' . ($izin->jenisIzin->nama_izin ?? 'Izin') . ' Anda ditolak.', ['id_izin' => $izin->id_izin] ); return redirect()->back()->with('success', 'Pengajuan izin berhasil ditolak.'); } }