with(['employee.user.profile', 'rejection']) ->when($tableQuery['search'] !== '', function (Builder $query) use ($tableQuery): void { $search = $tableQuery['search']; $query->where(function (Builder $query) use ($search): void { $query->whereHas('employee.user.profile', fn (Builder $query) => $query->where('full_name', 'like', "%{$search}%")) ->orWhereHas('employee.user', fn (Builder $query) => $query->where('username', 'like', "%{$search}%")) ->orWhereHas('rejection', fn (Builder $query) => $query->where('reason', 'like', "%{$search}%")); }); }); $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']); return $query ->paginate(10) ->withQueryString(); } /** * @param array{start_date: string, end_date: string} $validated */ public function create(array $validated): void { $employee = auth()->user()?->employee; if ($employee === null) { throw ValidationException::withMessages([ 'employee' => 'Akun Anda tidak terhubung ke data pegawai.', ]); } $startDate = Carbon::parse($validated['start_date'])->startOfDay(); $endDate = Carbon::parse($validated['end_date'])->startOfDay(); $this->ensureMinimumLeadTime($startDate); $this->ensureValidDateRange($startDate, $endDate); LeaveRequest::create([ 'employee_id' => $employee->id, 'start_date' => $startDate, 'end_date' => $endDate, 'total_days' => $this->calculateTotalDays($startDate, $endDate), 'status' => LeaveRequestStatus::PENDING, ]); } /** * @param array{start_date: string, end_date: string} $validated */ public function update(LeaveRequest $leaveRequest, array $validated): void { $this->ensureOwnedBySubmitter($leaveRequest); $this->ensurePending($leaveRequest, 'Pengajuan cuti hanya dapat diubah saat status menunggu.'); $startDate = Carbon::parse($validated['start_date'])->startOfDay(); $endDate = Carbon::parse($validated['end_date'])->startOfDay(); $this->ensureMinimumLeadTime($startDate); $this->ensureValidDateRange($startDate, $endDate); $leaveRequest->start_date = $startDate; $leaveRequest->end_date = $endDate; $leaveRequest->total_days = $this->calculateTotalDays($startDate, $endDate); $leaveRequest->save(); } public function delete(LeaveRequest $leaveRequest): void { $this->ensureOwnedBySubmitter($leaveRequest); $this->ensurePending($leaveRequest, 'Pengajuan cuti hanya dapat dihapus saat status menunggu.'); $leaveRequest->delete(); } public function approve(LeaveRequest $leaveRequest): void { $this->ensurePending($leaveRequest, 'Pengajuan cuti ini sudah diverifikasi.'); $user = auth()->user(); DB::transaction(function () use ($leaveRequest, $user): void { $leaveRequest->status = LeaveRequestStatus::APPROVED; $leaveRequest->verified_at = now(); $leaveRequest->verified_by_id = $user->id; $leaveRequest->save(); }); } public function reject(LeaveRequest $leaveRequest, string $reason): void { $this->ensurePending($leaveRequest, 'Pengajuan cuti ini sudah diverifikasi.'); $user = auth()->user(); DB::transaction(function () use ($leaveRequest, $user, $reason): void { $leaveRequest->status = LeaveRequestStatus::REJECTED; $leaveRequest->verified_at = now(); $leaveRequest->verified_by_id = $user->id; $leaveRequest->save(); $leaveRequest->rejection()->create([ 'reason' => $reason, 'rejected_by_id' => $user->id, ]); }); } private function calculateTotalDays(Carbon $startDate, Carbon $endDate): int { return (int) $startDate->diffInDays($endDate) + 1; } private function ensureMinimumLeadTime(Carbon $startDate): void { $minimumStartDate = now()->startOfDay()->addDay(); if ($startDate->lt($minimumStartDate)) { throw ValidationException::withMessages([ 'start_date' => 'Pengajuan cuti harus diajukan minimal H-1 (tanggal mulai paling cepat besok).', ]); } } private function ensureValidDateRange(Carbon $startDate, Carbon $endDate): void { if ($endDate->lt($startDate)) { throw ValidationException::withMessages([ 'end_date' => 'Tanggal selesai tidak boleh sebelum tanggal mulai.', ]); } } private function ensureOwnedBySubmitter(LeaveRequest $leaveRequest): void { if (auth()->user()?->employee?->id !== $leaveRequest->employee_id) { throw ValidationException::withMessages([ 'leave_request' => 'Anda tidak memiliki akses untuk mengubah pengajuan cuti ini.', ]); } } private function ensurePending(LeaveRequest $leaveRequest, string $message): void { if ($leaveRequest->status !== LeaveRequestStatus::PENDING) { throw ValidationException::withMessages([ 'leave_request' => $message, ]); } } private function applySorting(Builder $query, string $sort, string $direction): void { if (in_array($sort, ['created_at', 'start_date', 'end_date', 'total_days', 'status'], true)) { $query->orderBy($sort, $direction); return; } $query->latest(); } }