configuration->getConfig('semester'); $schoolYear = $schoolYear ?: (string)$this->configuration->getConfig('school_year'); [$rangeStart, $rangeEnd] = $this->semesterRangeService->getSchoolYearRange($schoolYear); $semesterNorm = $this->semesterRangeService->normalizeSemester($semester); if ($semesterNorm !== '') { $semesterRange = $this->semesterRangeService->getSemesterRange($schoolYear, $semesterNorm); if ($semesterRange) { [$rangeStart, $rangeEnd] = $semesterRange; } } $days = $this->semesterRangeService->buildSundayList($rangeStart, $rangeEnd); $assignedBySection = $semesterNorm !== '' ? $this->teacherClass->assignedBySectionForTerm($semesterNorm, $schoolYear) : $this->teacherClass->assignedBySectionForTerm($semester, $schoolYear); $sectionCodes = array_keys($assignedBySection); $labels = []; if (!empty($sectionCodes)) { $rows = $this->classSection->query() ->select(['class_section_id', 'class_section_name']) ->whereIn('class_section_id', $sectionCodes) ->get() ->toArray(); foreach ($rows as $row) { $labels[(int)$row['class_section_id']] = trim((string)$row['class_section_name']) ?: 'Section #' . (int)$row['class_section_id']; } } $teacherIds = []; foreach ($assignedBySection as $rows) { foreach ($rows as $row) { $teacherIds[(int)$row['teacher_id']] = true; } } $teacherIds = array_keys($teacherIds); $statusByTeacher = []; if (!empty($teacherIds) && !empty($days)) { $query = DB::table('staff_attendance') ->select('user_id', 'date', 'status', 'reason') ->where('school_year', $schoolYear) ->whereIn('user_id', $teacherIds) ->whereIn('date', $days); if ($semesterNorm !== '') { $query->where('semester', $semesterNorm); } $rows = $query->get(); foreach ($rows as $row) { $statusByTeacher[(int)$row->user_id][(string)$row->date] = [ 'status' => strtolower((string)$row->status), 'reason' => $row->reason, ]; } } $noSchoolRows = DB::table('calendar_events') ->select('date') ->where('school_year', $schoolYear) ->where('no_school', 1) ->where('date', '>=', $rangeStart) ->where('date', '<=', $rangeEnd) ->get(); $noSchoolDays = []; foreach ($noSchoolRows as $row) { $noSchoolDays[] = (string)$row->date; } $sections = []; foreach ($sectionCodes as $code) { $teachersPayload = []; foreach (($assignedBySection[$code] ?? []) as $row) { $teacherId = (int)$row['teacher_id']; $name = trim(($row['firstname'] ?? '') . ' ' . ($row['lastname'] ?? '')) ?: ('User#' . $teacherId); $position = strtolower((string)($row['position'] ?? 'main')); $position = in_array($position, ['main', 'ta'], true) ? $position : 'main'; $cells = []; $totals = ['p' => 0, 'a' => 0, 'l' => 0]; foreach ($days as $date) { $cell = $statusByTeacher[$teacherId][$date] ?? null; if (!$cell || empty($cell['status'])) { continue; } $cells[$date] = $cell; if ($cell['status'] === 'present') { $totals['p']++; } elseif ($cell['status'] === 'absent') { $totals['a']++; } elseif ($cell['status'] === 'late') { $totals['l']++; } } $teachersPayload[] = [ 'teacher_id' => $teacherId, 'name' => $name, 'position' => $position, 'cells' => $cells, 'totals' => $totals, ]; } $sections[] = [ 'section_code' => (int)$code, 'label' => $labels[$code] ?? ('Section #' . (int)$code), 'teachers' => $teachersPayload, ]; } return [ 'filters' => [ 'semester' => $semester, 'school_year' => $schoolYear, 'range_start' => $rangeStart, 'range_end' => $rangeEnd, ], 'sundays' => $days, 'noSchoolDays' => $noSchoolDays, 'sections' => $sections, 'admins' => [], ]; } public function adminsAttendanceData(?string $date, ?string $semester, ?string $schoolYear): array { $date = $date ?: now()->toDateString(); $semester = $semester ?: (string)$this->configuration->getConfig('semester'); $schoolYear = $schoolYear ?: (string)$this->configuration->getConfig('school_year'); $allStaff = $this->staffAttendance->staffWithStatusForDay($date, $semester, $schoolYear, []); $userIds = []; foreach ($allStaff as $row) { $uid = (int)($row['user_id'] ?? 0); if ($uid > 0) { $userIds[$uid] = true; } } $userIds = array_keys($userIds); $rolesRows = DB::table('user_roles as ur') ->select('ur.user_id', 'r.id as role_id', 'r.name as role_name', 'r.slug as role_slug', 'r.priority') ->join('roles as r', 'r.id', '=', 'ur.role_id') ->where('r.is_active', 1) ->whereIn('ur.user_id', $userIds) ->get(); $rolesByUser = []; foreach ($rolesRows as $row) { $rolesByUser[(int)$row->user_id][] = (array)$row; } foreach ($rolesByUser as &$list) { usort($list, fn($a, $b) => ((int)($a['priority'] ?? 100)) <=> ((int)($b['priority'] ?? 100))); } $excludeSlugs = ['parent', 'guest', 'teacher', 'teacher_assistant', 'ta', 'assistant_teacher']; $staffByUser = []; foreach ($allStaff as $row) { $uid = (int)($row['user_id'] ?? 0); if ($uid > 0 && !isset($staffByUser[$uid])) { $staffByUser[$uid] = $row; } } $adminsGrid = []; foreach ($userIds as $uid) { $roles = $rolesByUser[$uid] ?? []; $picked = null; foreach ($roles as $role) { $slug = strtolower((string)($role['role_slug'] ?? $role['role_name'] ?? '')); if (!in_array($slug, $excludeSlugs, true)) { $picked = $role; break; } } if (!$picked) { continue; } $base = $staffByUser[$uid] ?? ['user_id' => $uid]; $base['role_name'] = $picked['role_name'] ?? 'Admin'; $base['role_slug'] = $picked['role_slug'] ?? null; $adminsGrid[] = $base; } usort($adminsGrid, function ($a, $b) { $an = trim(($a['firstname'] ?? '') . ' ' . ($a['lastname'] ?? '')); $bn = trim(($b['firstname'] ?? '') . ' ' . ($b['lastname'] ?? '')); return strcasecmp($an, $bn); }); return [ 'semester' => $semester, 'school_year' => $schoolYear, 'date' => $date, 'admins_grid' => $adminsGrid, ]; } public function saveAdmins(Authenticatable $user, array $data): array { $date = (string)$data['date']; $semester = (string)$data['semester']; $schoolYear = (string)$data['school_year']; $admins = (array)($data['admins'] ?? $data['staff'] ?? []); $allowedStatuses = ['present', 'absent', 'late']; $excludedRoleSlugs = ['parent', 'guest', 'teacher', 'teacher_assistant']; DB::transaction(function () use ($admins, $date, $semester, $schoolYear, $allowedStatuses, $excludedRoleSlugs) { foreach ($admins as $uid => $row) { $uid = (int)$uid; $status = strtolower(trim((string)($row['status'] ?? ''))); $reason = trim((string)($row['reason'] ?? '')); $roleName = (string)($row['role_name'] ?? ''); $roleSlug = strtolower(str_replace(['-', ' '], '_', (string)($row['role_slug'] ?? $roleName))); if (in_array($roleSlug, $excludedRoleSlugs, true)) { continue; } if ($status === '') { DB::table('staff_attendance') ->where('user_id', $uid) ->whereDate('date', $date) ->where('semester', $semester) ->where('school_year', $schoolYear) ->delete(); continue; } if (!in_array($status, $allowedStatuses, true)) { continue; } DB::table('staff_attendance')->updateOrInsert( [ 'user_id' => $uid, 'date' => $date, 'semester' => $semester, 'school_year' => $schoolYear, ], [ 'role_name' => $roleName !== '' ? $roleName : 'Admin', 'status' => $status, 'reason' => $reason !== '' ? $reason : null, 'updated_at' => now(), 'created_at' => now(), ] ); } }); return [ 'ok' => true, 'message' => 'Admins attendance saved successfully.', ]; } public function saveCell(Authenticatable $user, array $data): array { $date = (string)$data['date']; $status = (string)($data['status'] ?? ''); $targetId = (int)$data['user_id']; $roleName = trim((string)($data['role_name'] ?? '')); $semester = (string)$data['semester']; $schoolYear = (string)$data['school_year']; $reason = trim((string)($data['reason'] ?? '')); if ($status === '') { DB::table('staff_attendance') ->where('user_id', $targetId) ->whereDate('date', $date) ->where('semester', $semester) ->where('school_year', $schoolYear) ->delete(); return ['ok' => true]; } DB::table('staff_attendance')->updateOrInsert( [ 'user_id' => $targetId, 'date' => $date, 'semester' => $semester, 'school_year' => $schoolYear, ], [ 'role_name' => $roleName !== '' ? $roleName : 'Admin', 'status' => $status, 'reason' => $reason !== '' ? $reason : null, 'updated_at' => now(), 'created_at' => now(), ] ); return ['ok' => true]; } public function monthCsv(?string $semester, ?string $schoolYear, ?string $scope): StreamedResponse { $semester = $semester ?: (string)$this->configuration->getConfig('semester'); $schoolYear = $schoolYear ?: (string)$this->configuration->getConfig('school_year'); $scope = strtolower((string)$scope); [$rangeStart, $rangeEnd] = $this->semesterRangeService->getSchoolYearRange($schoolYear); $semesterNorm = $this->semesterRangeService->normalizeSemester($semester); if ($semesterNorm !== '') { $semesterRange = $this->semesterRangeService->getSemesterRange($schoolYear, $semesterNorm); if ($semesterRange) { [$rangeStart, $rangeEnd] = $semesterRange; } } $daysList = $this->semesterRangeService->buildSundayList($rangeStart, $rangeEnd); $filename = ($scope === 'admins' ? 'admin_attendance_' : 'teacher_attendance_') . $semester . '_' . str_replace('/', '-', $schoolYear) . '.csv'; return response()->streamDownload(function () use ($semester, $schoolYear, $scope, $daysList, $semesterNorm, $rangeStart, $rangeEnd) { $out = fopen('php://output', 'w'); if ($scope === 'admins') { fputcsv($out, ['User ID', 'Date', 'Status', 'Reason']); $query = DB::table('staff_attendance') ->select('user_id', 'date', 'status', 'reason') ->where('school_year', $schoolYear) ->where('date', '>=', $rangeStart) ->where('date', '<=', $rangeEnd); if ($semesterNorm !== '') { $query->where('semester', $semesterNorm); } foreach ($query->orderBy('date')->get() as $row) { fputcsv($out, [ $row->user_id, $row->date, strtoupper(substr((string)$row->status, 0, 1)), $row->reason, ]); } fclose($out); return; } fputcsv($out, ['Teacher ID', 'Date', 'Status', 'Reason']); $query = DB::table('staff_attendance') ->select('user_id', 'date', 'status', 'reason') ->where('school_year', $schoolYear) ->where('date', '>=', $rangeStart) ->where('date', '<=', $rangeEnd); if ($semesterNorm !== '') { $query->where('semester', $semesterNorm); } foreach ($query->orderBy('date')->get() as $row) { fputcsv($out, [ $row->user_id, $row->date, strtoupper(substr((string)$row->status, 0, 1)), $row->reason, ]); } fclose($out); }, $filename); } }