employeeService = $employeeService; $this->leaveService = $leaveService; } public function balances(Request $request) { try { $employee = $this->employeeService->resolveEmployee($request->user()); } catch (Exception $e) { return response()->json([ 'success' => false, 'data' => [], 'message' => 'Employee profile not found', 'errors' => null, ], 403); } $balances = $this->leaveService->getBalances($employee); return response()->json([ 'success' => true, 'data' => $balances, 'message' => null, 'errors' => null, ]); } public function applications(Request $request) { try { $employee = $this->employeeService->resolveEmployee($request->user()); } catch (Exception $e) { return response()->json([ 'success' => false, 'data' => [], 'message' => 'Employee profile not found', 'errors' => null, ], 403); } $applications = LeaveApplication::where('employee_id', $request->user()->id) ->orderBy('created_at', 'desc') ->get(); return response()->json([ 'success' => true, 'data' => $applications, 'message' => null, 'errors' => null, ]); } public function apply(Request $request) { $request->validate([ 'leave_type' => 'nullable|string', 'leave_type_id' => 'nullable|integer', 'start_date' => 'required|date', 'end_date' => 'required|date|after_or_equal:start_date', 'reason' => 'required|string', ]); try { $employee = $this->employeeService->resolveEmployee($request->user()); } catch (Exception $e) { return response()->json([ 'success' => false, 'data' => null, 'message' => 'Employee profile not found', 'errors' => null, ], 403); } $application = $this->leaveService->submitApplication($request->user(), $employee, $request->all()); return response()->json([ 'success' => true, 'message' => 'Leave application submitted successfully', 'data' => $application, 'errors' => null, ], 201); } public function updateStatus(Request $request, $id) { $request->validate([ 'status' => 'required|string|in:approved,rejected,cancelled,pending', ]); $user = $request->user(); $application = LeaveApplication::find($id); if (!$application) { return response()->json([ 'success' => false, 'data' => null, 'message' => 'Leave application not found', 'errors' => null, ], 404); } $employee = $this->employeeService->resolveEmployee($user); // Scoping & authorization checks if ($request->status === 'cancelled') { $isOwner = in_array($application->employee_id, [$employee->id, $user->id]) || $application->created_by === $user->id; if (!$isOwner && !in_array($user->type, ['company', 'hr', 'superadmin', 'manager'])) { return response()->json([ 'success' => false, 'data' => null, 'message' => 'Unauthorized to cancel this leave application', 'errors' => null, ], 403); } } else { // Approval or Rejection requires manager / HR role within same company tenant $userType = strtolower($user->type ?? ''); $isElevatedRole = in_array($userType, ['company', 'hr', 'superadmin', 'manager']); if (!$isElevatedRole) { try { $isElevatedRole = $user->hasAnyRole(['company', 'hr', 'superadmin', 'manager']); } catch (\Throwable $e) { $isElevatedRole = false; } } $empUser = User::find($application->employee_id) ?? Employee::find($application->employee_id)?->user ?? User::find($application->created_by); $appCompanyId = $empUser ? ($empUser->type === 'company' ? $empUser->id : ($empUser->created_by ?: $empUser->id)) : null; $approverCompanyId = $user->type === 'company' ? $user->id : ($user->created_by ?: $user->id); $isSuperAdmin = in_array($userType, ['superadmin']); $isSameCompany = ($appCompanyId !== null && $approverCompanyId !== null && (int)$appCompanyId === (int)$approverCompanyId); if (!$isElevatedRole || (!$isSameCompany && !$isSuperAdmin)) { return response()->json([ 'success' => false, 'data' => null, 'message' => 'Unauthorized to review this leave application. Approval requires authorized manager or HR within the same organization.', 'errors' => null, ], 403); } } $application->update(['status' => $request->status]); return response()->json([ 'success' => true, 'message' => 'Leave application status updated to ' . $request->status, 'data' => $application, 'errors' => null, ]); } public function cancel(Request $request, $id) { return $this->updateStatus($request->merge(['status' => 'cancelled']), $id); } }