employeeService = $employeeService; $this->attendanceService = $attendanceService; } public function today(Request $request) { 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); } $record = $this->attendanceService->getTodayRecord($employee); $branchId = $employee->branch_id ?? $request->user()->branch_id ?? null; $creatorId = $request->user()->type === 'company' ? $request->user()->id : $request->user()->created_by; $activeLocation = \App\Models\AttendanceLocation::where('created_by', $creatorId) ->where('is_active', true) ->where(function ($q) use ($branchId) { $q->whereNull('branch_id')->orWhere('branch_id', $branchId); }) ->first(); $geofence = null; if ($activeLocation) { $geofence = [ 'latitude' => (float)$activeLocation->latitude, 'longitude' => (float)$activeLocation->longitude, 'radius' => (int)$activeLocation->radius_meters, 'name' => $activeLocation->name, ]; } else { $branch = $branchId ? \App\Models\Branch::find($branchId) : null; if ($branch && isset($branch->latitude, $branch->longitude)) { $geofence = [ 'latitude' => (float)$branch->latitude, 'longitude' => (float)$branch->longitude, 'radius' => (int)($branch->radius ?? 500), 'name' => $branch->name ?? 'Branch', ]; } } $formatDateTime = function ($date, $time) { if (!$date || !$time) return null; $cleanDate = explode('T', $date)[0]; $cleanDate = explode(' ', $cleanDate)[0]; return "{$cleanDate}T{$time}"; }; $data = [ 'clocked_in' => $record ? !!$record->clock_in : false, 'clocked_out' => $record ? !!$record->clock_out : false, 'on_break' => $record ? (!empty($record->break_in) && empty($record->break_out)) : false, 'clock_in_time' => $record ? $formatDateTime($record->date, $record->clock_in) : null, 'clock_out_time' => $record ? $formatDateTime($record->date, $record->clock_out) : null, 'break_in_time' => $record ? $formatDateTime($record->date, $record->break_in) : null, 'break_out_time' => $record ? $formatDateTime($record->date, $record->break_out) : null, 'total_hours' => $record ? (float)($record->total_hours ?? 0) : 0, 'late_hours' => $record ? (float)($record->late_hours ?? 0) : 0, 'early_hours' => $record ? (float)($record->early_hours ?? 0) : 0, 'break_hours' => $record ? (float)($record->break_hours ?? 0) : 0, 'overtime_hours' => $record ? (float)($record->overtime_hours ?? 0) : 0, 'status' => $record->status ?? 'present', 'notes' => $record->notes ?? null, 'geofence' => $geofence, ]; if ($record) { $data['id'] = $record->id; $data['date'] = $record->date; $data['clock_in'] = $record->clock_in; $data['clock_out'] = $record->clock_out; $data['break_in'] = $record->break_in; $data['break_out'] = $record->break_out; } return response()->json([ 'success' => true, 'data' => $data, 'message' => null, 'errors' => null, ]); } public function clock(Request $request) { $action = $request->input('action') ?? $request->input('type'); $actionMap = [ 'in' => 'clock_in', 'out' => 'clock_out', 'clock_in' => 'clock_in', 'clock_out' => 'clock_out', 'break_in' => 'break_in', 'break_out' => 'break_out', ]; $resolvedAction = $actionMap[$action] ?? $action; $request->merge(['action' => $resolvedAction]); $request->validate([ 'action' => 'required|string|in:clock_in,clock_out,break_in,break_out', 'latitude' => 'nullable|numeric', 'longitude' => 'nullable|numeric', 'notes' => 'nullable|string', 'activity_note' => 'nullable|string', 'activity' => 'nullable|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); } $payload = array_merge($request->all(), ['action' => $resolvedAction]); $result = $this->attendanceService->processClock($request->user(), $employee, $payload); return response()->json([ 'success' => $result['success'], 'data' => $result['data'], 'message' => $result['message'], 'errors' => null, ], $result['status_code']); } public function history(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); } $records = AttendanceRecord::where(function ($q) use ($employee, $request) { $q->where('employee_id', $employee->id) ->orWhere('employee_id', $request->user()->id); }) ->orderBy('date', 'desc') ->limit(30) ->get(); return response()->json([ 'success' => true, 'data' => $records, 'message' => null, 'errors' => null, ]); } public function summary(Request $request) { $todayStr = \Carbon\Carbon::now('Asia/Manila')->format('Y-m-d'); $currentMonth = \Carbon\Carbon::now('Asia/Manila')->format('m'); // Employees on approved leave today $offEmployees = \App\Models\LeaveApplication::with('user:id,name,avatar') ->where('status', 'approved') ->whereDate('start_date', '<=', $todayStr) ->whereDate('end_date', '>=', $todayStr) ->get() ->map(function ($app) { return [ 'id' => $app->user_id, 'name' => $app->user->name ?? 'Employee', 'avatar' => $app->user->avatar ?? null, ]; }) ->unique('id') ->values(); // Employees with birthdays this month $birthdayEmployees = \App\Models\Employee::with('user:id,name,avatar') ->whereNotNull('date_of_birth') ->whereMonth('date_of_birth', $currentMonth) ->get() ->map(function ($emp) { return [ 'id' => $emp->user_id, 'name' => $emp->user->name ?? $emp->name ?? 'Employee', 'avatar' => $emp->user->avatar ?? null, 'dob' => $emp->date_of_birth, ]; }) ->values(); return response()->json([ 'success' => true, 'data' => [ 'off_today_count' => $offEmployees->count(), 'off_today_list' => $offEmployees, 'birthday_this_month_count' => $birthdayEmployees->count(), 'birthday_this_month_list' => $birthdayEmployees, ], 'message' => null, 'errors' => null, ]); } }